/* * Copyright (c) 1997 by Massimino Pascal * Copyright 2006-2014, Haiku, Inc. All rights reserved. * * Distributed under the terms of the MIT License. * * Authors: * Stephan Aßmus, superstippi@gmx.de * Massimino Pascal, Pascal.Massimon@ens.fr * John Scipione, jscipione@gmail.com */ /*! When shown ifs, Diana Rose (4 years old) said, "It looks like dancing." */ #include "IFS.h" #include #include #include #include #include #include #include #include #include // for getpid() #include // for gettimeofday() #define HALF 0 #define random() ya_random() #define FLOAT_TO_INT(x) (int32)((float)(UNIT)*(x)) #define LRAND() ((long) (random() & 0x7fffffff)) #define NRAND(n) ((int) (LRAND() % (n))) #define MAXRAND (2147483648.0) // unsigned 1<<31 as a float #define SRAND(n) // already seeded by screenhack.c TODO: ?!? is it? // The following 'random' numbers are taken from CRC, 18th Edition, page 622. // Each array element was taken from the corresponding line in the table, // except that a[0] was from line 100. 8s and 9s in the table were simply // skipped. The high order digit was taken mod 4. #define VECTOR_SIZE 55 static unsigned int a[VECTOR_SIZE] = { 035340171546, 010401501101, 022364657325, 024130436022, 002167303062, // 5 037570375137, 037210607110, 016272055420, 023011770546, 017143426366, // 10 014753657433, 021657231332, 023553406142, 004236526362, 010365611275, // 14 007117336710, 011051276551, 002362132524, 001011540233, 012162531646, // 20 007056762337, 006631245521, 014164542224, 032633236305, 023342700176, // 25 002433062234, 015257225043, 026762051606, 000742573230, 005366042132, // 30 012126416411, 000520471171, 000725646277, 020116577576, 025765742604, // 35 007633473735, 015674255275, 017555634041, 006503154145, 021576344247, // 40 014577627653, 002707523333, 034146376720, 030060227734, 013765414060, // 45 036072251540, 007255221037, 024364674123, 006200353166, 010126373326, // 50 015664104320, 016401041535, 016215305520, 033115351014, 017411670323 // 55 }; static int i1; static int i2; unsigned int ya_random(void) { int ret = a[i1] + a[i2]; a[i1] = ret; if (++i1 >= VECTOR_SIZE) i1 = 0; if (++i2 >= VECTOR_SIZE) i2 = 0; return ret; } void ya_rand_init(unsigned int seed) { int i; if (seed == 0) { struct timeval tp; struct timezone tzp; gettimeofday(&tp, &tzp); // ignore overflow seed = (999*tp.tv_sec) + (1001*tp.tv_usec) + (1003 * getpid()); } a[0] += seed; for (i = 1; i < VECTOR_SIZE; i++) { seed = a[i-1]*1001 + seed*999; a[i] += seed; } i1 = a[0] % VECTOR_SIZE; i2 = (i1 + 24) % VECTOR_SIZE; } static float gauss_rand(float c, float A, float S) { float y = (float) LRAND() / MAXRAND; y = A * (1.0 - exp(-y * y * S)) / (1.0 - exp(-S)); if (NRAND(2)) return (c + y); return (c - y); } static float half_gauss_rand(float c, float A, float S) { float y = (float) LRAND() / MAXRAND; y = A * (1.0 - exp(-y * y * S)) / (1.0 - exp(-S)); return (c + y); } inline void transform(SIMILITUDE* Similitude, int32 xo, int32 yo, int32* x, int32* y) { int32 xx; int32 yy; xo = xo - Similitude->Cx; xo = (xo * Similitude->R) / UNIT; yo = yo - Similitude->Cy; yo = (yo * Similitude->R) / UNIT; xx = xo - Similitude->Cx; xx = (xx * Similitude->R2) / UNIT; yy = -yo - Similitude->Cy; yy = (yy * Similitude->R2) / UNIT; *x = ((xo * Similitude->Ct - yo * Similitude->St + xx * Similitude->Ct2 - yy * Similitude->St2) / UNIT) + Similitude->Cx; *y = ((xo * Similitude->St + yo * Similitude->Ct + xx * Similitude->St2 + yy * Similitude->Ct2) / UNIT) + Similitude->Cy; } IFS::IFS(BRect bounds) : fRoot(NULL), fCurrentFractal(NULL), fPointBuffer(NULL), fCurrentPoint(0), fAdditive(false), fCurrentMarkValue(1) { if (!bounds.IsValid()) return; ya_rand_init(system_time()); int i; FRACTAL* Fractal; if (fRoot == NULL) { fRoot = (FRACTAL*)calloc(1, sizeof(FRACTAL)); if (fRoot == NULL) return; } Fractal = fRoot; _FreeBuffers(Fractal); i = (NRAND(4)) + 2; // Number of centers switch (i) { case 2: default: Fractal->Depth = fAdditive ? MAX_DEPTH_2 + 1 : MAX_DEPTH_2; Fractal->r_mean = 0.7; Fractal->dr_mean = 0.3; Fractal->dr2_mean = 0.4; break; case 3: Fractal->Depth = fAdditive ? MAX_DEPTH_3 + 1 : MAX_DEPTH_3; Fractal->r_mean = 0.6; Fractal->dr_mean = 0.4; Fractal->dr2_mean = 0.3; break; case 4: Fractal->Depth = MAX_DEPTH_4; Fractal->r_mean = 0.5; Fractal->dr_mean = 0.4; Fractal->dr2_mean = 0.3; break; case 5: Fractal->Depth = MAX_DEPTH_5; Fractal->r_mean = 0.5; Fractal->dr_mean = 0.4; Fractal->dr2_mean = 0.3; break; } Fractal->SimilitudeCount = i; Fractal->MaxPoint = Fractal->SimilitudeCount - 1; for (i = 0; i <= Fractal->Depth + 2; ++i) Fractal->MaxPoint *= Fractal->SimilitudeCount; if ((Fractal->buffer1 = (Point *)calloc(Fractal->MaxPoint, sizeof(Point))) == NULL) { _FreeIFS(Fractal); return; } if ((Fractal->buffer2 = (Point *)calloc(Fractal->MaxPoint, sizeof(Point))) == NULL) { _FreeIFS(Fractal); return; } Fractal->Speed = 6; #if HALF Fractal->Width = bounds.IntegerWidth() / 2 + 1; Fractal->Height = bounds.IntegerHeight() / 2 + 1; #else Fractal->Width = bounds.IntegerWidth() + 1; Fractal->Height = bounds.IntegerHeight() + 1; #endif Fractal->CurrentPoint = 0; Fractal->Count = 0; Fractal->Lx = (Fractal->Width - 1) / 2; Fractal->Ly = (Fractal->Height - 1) / 2; Fractal->Col = NRAND(Fractal->Width * Fractal->Height - 1) + 1; _RandomSimilitudes(Fractal, Fractal->Components, 5 * MAX_SIMILITUDE); delete Fractal->bitmap; Fractal->bitmap = new BBitmap(BRect(0.0, 0.0, Fractal->Width - 1, Fractal->Height - 1), 0, B_RGB32); delete Fractal->markBitmap; Fractal->markBitmap = new BBitmap(BRect(0.0, 0.0, Fractal->Width - 1, Fractal->Height - 1), 0, B_GRAY8); // allocation checked if (Fractal->bitmap != NULL && Fractal->bitmap->IsValid()) memset(Fractal->bitmap->Bits(), 0, Fractal->bitmap->BitsLength()); else { delete Fractal->bitmap; Fractal->bitmap = NULL; } if (Fractal->markBitmap != NULL && Fractal->markBitmap->IsValid()) { memset(Fractal->markBitmap->Bits(), 0, Fractal->markBitmap->BitsLength()); } else { delete Fractal->markBitmap; Fractal->markBitmap = NULL; } } IFS::~IFS() { if (fRoot != NULL) { _FreeIFS(fRoot); free((void*)fRoot); } } void IFS::Draw(BView* view, const buffer_info* info, int32 frames) { int i; float u; float uu; float v; float vv; float u0; float u1; float u2; float u3; SIMILITUDE* S; SIMILITUDE* S1; SIMILITUDE* S2; SIMILITUDE* S3; SIMILITUDE* S4; FRACTAL* F; if (fRoot == NULL) return; F = fRoot; if (F->buffer1 == NULL) return; // do this as many times as necessary to calculate the missing frames // so the animation doesn't jerk when we miss a few frames for (int32 frame = 0; frame < frames; frame++) { u = (float) (F->Count) * (float) (F->Speed) / 1000.0; uu = u * u; v = 1.0 - u; vv = v * v; u0 = vv * v; u1 = 3.0 * vv * u; u2 = 3.0 * v * uu; u3 = u * uu; S = F->Components; S1 = S + F->SimilitudeCount; S2 = S1 + F->SimilitudeCount; S3 = S2 + F->SimilitudeCount; S4 = S3 + F->SimilitudeCount; for (i = F->SimilitudeCount; i; --i, S++, S1++, S2++, S3++, S4++) { S->c_x = u0 * S1->c_x + u1 * S2->c_x + u2 * S3->c_x + u3 * S4->c_x; S->c_y = u0 * S1->c_y + u1 * S2->c_y + u2 * S3->c_y + u3 * S4->c_y; S->r = u0 * S1->r + u1 * S2->r + u2 * S3->r + u3 * S4->r; S->r2 = u0 * S1->r2 + u1 * S2->r2 + u2 * S3->r2 + u3 * S4->r2; S->A = u0 * S1->A + u1 * S2->A + u2 * S3->A + u3 * S4->A; S->A2 = u0 * S1->A2 + u1 * S2->A2 + u2 * S3->A2 + u3 * S4->A2; } if (frame == frames - 1) _DrawFractal(view, info); if (F->Count >= 1000 / F->Speed) { S = F->Components; S1 = S + F->SimilitudeCount; S2 = S1 + F->SimilitudeCount; S3 = S2 + F->SimilitudeCount; S4 = S3 + F->SimilitudeCount; for (i = F->SimilitudeCount; i; --i, S++, S1++, S2++, S3++, S4++) { S2->c_x = 2.0 * S4->c_x - S3->c_x; S2->c_y = 2.0 * S4->c_y - S3->c_y; S2->r = 2.0 * S4->r - S3->r; S2->r2 = 2.0 * S4->r2 - S3->r2; S2->A = 2.0 * S4->A - S3->A; S2->A2 = 2.0 * S4->A2 - S3->A2; *S1 = *S4; } _RandomSimilitudes(F, F->Components + 3 * F->SimilitudeCount, F->SimilitudeCount); _RandomSimilitudes(F, F->Components + 4 * F->SimilitudeCount, F->SimilitudeCount); F->Count = 0; } else F->Count++; } } void IFS::SetAdditive(bool additive) { fAdditive = additive; } void IFS::SetSpeed(int32 speed) { if (fRoot && speed > 0 && speed <= 12) fRoot->Speed = speed; } void IFS::_DrawFractal(BView* view, const buffer_info* info) { FRACTAL* F = fRoot; int i; int j; int32 x; int32 y; int32 xo; int32 yo; SIMILITUDE* Current; SIMILITUDE* Similitude; for (Current = F->Components, i = F->SimilitudeCount; i; --i, Current++) { Current->Cx = FLOAT_TO_INT(Current->c_x); Current->Cy = FLOAT_TO_INT(Current->c_y); Current->Ct = FLOAT_TO_INT(cos(Current->A)); Current->St = FLOAT_TO_INT(sin(Current->A)); Current->Ct2 = FLOAT_TO_INT(cos(Current->A2)); Current->St2 = FLOAT_TO_INT(sin(Current->A2)); Current->R = FLOAT_TO_INT(Current->r); Current->R2 = FLOAT_TO_INT(Current->r2); } fCurrentPoint = 0; fCurrentFractal = F; fPointBuffer = F->buffer2; for (Current = F->Components, i = F->SimilitudeCount; i; --i, Current++) { xo = Current->Cx; yo = Current->Cy; for (Similitude = F->Components, j = F->SimilitudeCount; j; --j, Similitude++) { if (Similitude == Current) continue; transform(Similitude, xo, yo, &x, &y); _Trace(F, x, y); } } if (F->bitmap != NULL && F->markBitmap != NULL) { uint8* bits = (uint8*)F->bitmap->Bits(); uint32 bpr = F->bitmap->BytesPerRow(); uint8* markBits = (uint8*)F->markBitmap->Bits(); uint32 markBPR = F->markBitmap->BytesPerRow(); int32 minX = F->Width; int32 minY = F->Height; int32 maxX = 0; int32 maxY = 0; // Erase previous dots from bitmap, // but only if we're not in BDirectWindow mode, // since the dots will have been erased already if (info == NULL) { if (F->CurrentPoint) { for (int32 i = 0; i < F->CurrentPoint; i++) { Point p = F->buffer1[i]; if (p.x >= 0 && p.x < F->Width && p.y >= 0 && p.y < F->Height) { int32 offset = bpr * p.y + p.x * 4; *(uint32*)&bits[offset] = 0; if (minX > p.x) minX = p.x; if (minY > p.y) minY = p.y; if (maxX < p.x) maxX = p.x; if (maxY < p.y) maxY = p.y; } } } } // draw the new dots into the bitmap if (fCurrentPoint != 0) { if (info != NULL) { for (int32 i = 0; i < fCurrentPoint; i++) { Point p = F->buffer2[i]; if (p.x >= 0 && p.x < F->Width && p.y >= 0 && p.y < F->Height) { int32 offset = bpr * p.y + p.x * 4; if (fAdditive) { if (bits[offset + 0] < 255) { bits[offset + 0] += 51; bits[offset + 1] += 51; bits[offset + 2] += 51; } } else *(uint32*)&bits[offset] = 0xffffffff; } } } else { // in this version, remember the bounds rectangle for (int32 i = 0; i < fCurrentPoint; i++) { Point p = F->buffer2[i]; if (p.x >= 0 && p.x < F->Width && p.y >= 0 && p.y < F->Height) { int32 offset = bpr * p.y + p.x * 4; if (fAdditive) { if (bits[offset + 0] < 255) { bits[offset + 0] += 15; bits[offset + 1] += 15; bits[offset + 2] += 15; } } else *(uint32*)&bits[offset] = 0xffffffff; if (minX > p.x) minX = p.x; if (minY > p.y) minY = p.y; if (maxX < p.x) maxX = p.x; if (maxY < p.y) maxY = p.y; } } } } if (info != NULL && info->bits != NULL) { uint8* screenBits = (uint8*)info->bits; uint32 screenBPR = info->bytesPerRow; int32 left = info->bounds.left; int32 top = info->bounds.top; int32 bpp = info->bits_per_pixel; screenBits += left * bpp + top * bpr; int32 screenWidth = info->bounds.right - left; int32 screenHeight = info->bounds.bottom - top; // redraw the previous points on screen // with the contents of the current bitmap // // draw the new points, erasing the bitmap as we go int32 maxPoints = max_c(F->CurrentPoint, fCurrentPoint); if (maxPoints > 0) { for (int32 i = 0; i < maxPoints; i++) { // copy previous points (black) if (i < F->CurrentPoint) { Point p = F->buffer1[i]; if (p.x >= 0 && p.x < F->Width && p.x < screenWidth && p.y >= 0 && p.y < F->Height && p.y < screenHeight) { int32 markOffset = markBPR * p.y + p.x; if (markBits[markOffset] != fCurrentMarkValue) { int32 offset = bpr * p.y + p.x * 4; // copy the pixel to the screen uint32* src = (uint32*)&bits[offset]; if (bpp == 32) { int32 screenOffset = screenBPR * p.y + p.x * 4; *(uint32*)&screenBits[screenOffset] = *src; } else if (bpp == 16) { int32 screenOffset = screenBPR * p.y + p.x * 2; *(uint16*)&screenBits[screenOffset] = (uint16)(((bits[offset + 2] & 0xf8) << 8) | ((bits[offset + 1] & 0xfc) << 3) | (bits[offset] >> 3)); } else if (bpp == 15) { int32 screenOffset = screenBPR * p.y + p.x * 2; *(uint16*)&screenBits[screenOffset] = (uint16)(((bits[offset + 2] & 0xf8) << 7) | ((bits[offset + 1] & 0xf8) << 2) | (bits[offset] >> 3)); } else if (bpp == 8) { int32 screenOffset = screenBPR * p.y + p.x; screenBits[screenOffset] = bits[offset]; } *src = 0; markBits[markOffset] = fCurrentMarkValue; } // else it means the pixel has been copied already } } // copy current points (white) and erase them from the // bitmap if (i < fCurrentPoint) { Point p = F->buffer2[i]; if (p.x >= 0 && p.x < F->Width && p.x < screenWidth && p.y >= 0 && p.y < F->Height && p.y < screenHeight) { int32 markOffset = markBPR * p.y + p.x; int32 offset = bpr * p.y + p.x * 4; // copy the pixel to the screen uint32* src = (uint32*)&bits[offset]; if (markBits[markOffset] != fCurrentMarkValue) { if (bpp == 32) { int32 screenOffset = screenBPR * p.y + p.x * 4; *(uint32*)&screenBits[screenOffset] = *src; } else if (bpp == 16) { int32 screenOffset = screenBPR * p.y + p.x * 2; *(uint16*)&screenBits[screenOffset] = (uint16)(((bits[offset + 2] & 0xf8) << 8) | ((bits[offset + 1] & 0xfc) << 3) | (bits[offset] >> 3)); } else if (bpp == 15) { int32 screenOffset = screenBPR * p.y + p.x * 2; *(uint16*)&screenBits[screenOffset] = (uint16)(((bits[offset + 2] & 0xf8) << 7) | ((bits[offset + 1] & 0xf8) << 2) | (bits[offset] >> 3)); } else if (bpp == 1) { int32 screenOffset = screenBPR * p.y + p.x; screenBits[screenOffset] = bits[offset]; } markBits[markOffset] = fCurrentMarkValue; } // else it means the pixel has been copied already *src = 0; } } } } } else { // if not in BDirectWindow mode, draw the bitmap BRect b(minX, minY, maxX, maxY); view->DrawBitmapAsync(F->bitmap, b, b); } } // flip buffers F->CurrentPoint = fCurrentPoint; fPointBuffer = F->buffer1; F->buffer1 = F->buffer2; F->buffer2 = fPointBuffer; if (fCurrentMarkValue == 255) fCurrentMarkValue = 0; else fCurrentMarkValue++; } void IFS::_Trace(FRACTAL* F, int32 xo, int32 yo) { int32 x; int32 y; SIMILITUDE* Current; Current = fCurrentFractal->Components; for (int32 i = fCurrentFractal->SimilitudeCount; i; --i, Current++) { transform(Current, xo, yo, &x, &y); fPointBuffer->x = (UNIT * 2 + x) * F->Lx / (UNIT * 2); fPointBuffer->y = (UNIT * 2 - y) * F->Ly / (UNIT * 2); fPointBuffer++; fCurrentPoint++; if (F->Depth && ((x - xo) >> 4) && ((y - yo) >> 4)) { F->Depth--; _Trace(F, x, y); F->Depth++; } } } void IFS::_RandomSimilitudes(FRACTAL* fractal, SIMILITUDE* current, int i) const { while (i-- > 0) { current->c_x = gauss_rand(0.0, .8, 4.0); current->c_y = gauss_rand(0.0, .8, 4.0); current->r = gauss_rand(fractal->r_mean, fractal->dr_mean, 3.0); current->r2 = half_gauss_rand(0.0,fractal->dr2_mean, 2.0); current->A = gauss_rand(0.0, 360.0, 4.0) * (M_PI / 180.0); current->A2 = gauss_rand(0.0, 360.0, 4.0) * (M_PI / 180.0); current++; } } void IFS::_FreeBuffers(FRACTAL* f) { if (f->buffer1) { free((void*)f->buffer1); f->buffer1 = (Point*)NULL; } if (f->buffer2) { free((void*)f->buffer2); f->buffer2 = (Point*)NULL; } } void IFS::_FreeIFS(FRACTAL* f) { _FreeBuffers(f); delete f->bitmap; f->bitmap = NULL; delete f->markBitmap; f->markBitmap = NULL; }