/* -*- Mode: C; tab-width: 4 -*- */ /* strange --- strange attractors */ #if !defined( lint ) && !defined( SABER ) static const char sccsid[] = "@(#)strange.c 5.00 2000/11/01 xlockmore"; #endif /*- * Copyright (c) 1997 by Massimino Pascal * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appear in all copies and that * both that copyright notice and this permission notice appear in * supporting documentation. * * This file is provided AS IS with no warranties of any kind. The author * shall have no liability with respect to the infringement of copyrights, * trade secrets or any patents by this file or any part thereof. In no * event will the author be liable for any lost revenue or profits or * other special, indirect and consequential damages. * * Revision History: * 22-Dec-2004: TDA: Replace Gauss_Rand with a real Gaussian. * 01-Nov-2000: Allocation checks * 10-May-1997: jwz@jwz.org: turned into a standalone program. * Made it render into an offscreen bitmap and then copy * that onto the screen, to reduce flicker. * * strange attractors are not so hard to find... */ #ifdef STANDALONE #define MODE_strange #define PROGCLASS "Strange" #define HACK_INIT init_strange #define HACK_DRAW draw_strange #define strange_opts xlockmore_opts #define DEFAULTS "*delay: 1000 \n" \ "*ncolors: 100 \n" #define SMOOTH_COLORS #include "xlockmore.h" /* from the xscreensaver distribution */ #else /* !STANDALONE */ #include "xlock.h" /* from the xlockmore distribution */ #endif /* !STANDALONE */ #ifdef MODE_strange ModeSpecOpt strange_opts = {0, (XrmOptionDescRec *) NULL, 0, (argtype *) NULL, (OptionStruct *) NULL}; #ifdef USE_MODULES ModStruct strange_description = {"strange", "init_strange", "draw_strange", "release_strange", "init_strange", "init_strange", (char *) NULL, &strange_opts, 1000, 1, 1, 1, 64, 1.0, "", "Shows strange attractors", 0, NULL}; #endif typedef float DBL; typedef int PRM; #define UNIT (1<<12) #define UNIT2 (1<<14) /* #define UNIT2 (3140*UNIT/1000) */ #define SKIP_FIRST 100 #define MAX_POINTS 5500 #define DBL_To_PRM(x) (PRM)( (DBL)(UNIT)*(x) ) #define DO_FOLD(a) (a)<0 ? -A->Fold[ (-(a))&(UNIT2-1) ] : A->Fold[ (a)&(UNIT2-1) ] #if 0 #define DO_FOLD(a) (a)<-UNIT2 ? -A->Fold[(-(a))%UNIT2] : (a)<0 ? -A->Fold[ -(a) ] :\ (a)>UNIT2 ? A->Fold[ (a)%UNIT2 ] : A->Fold[ (a) ] #define DO_FOLD(a) DBL_To_PRM( sin( (DBL)(a)/UNIT ) ) #define DO_FOLD(a) (a)<0 ? DBL_To_PRM( exp( 16.0*(a)/UNIT2 ) )-1.0 : \ DBL_To_PRM( 1.0-exp( -16.0*(a)/UNIT2 ) ) #endif /******************************************************************/ #define MAX_PRM 3*5 typedef struct _ATTRACTOR { DBL Prm1[MAX_PRM], Prm2[MAX_PRM]; PRM Prm[MAX_PRM], *Fold; void (*Iterate) (struct _ATTRACTOR *, PRM, PRM, PRM *, PRM *); XPoint *Buffer1, *Buffer2; int Cur_Pt, Max_Pt; int Col, Count, Speed; int Width, Height; Pixmap dbuf; /* jwz */ GC dbuf_gc; } ATTRACTOR; static ATTRACTOR *Root = (ATTRACTOR *) NULL; static DBL Amp_Prm[MAX_PRM] = { 1.0, 3.5, 3.5, 2.5, 4.7, 1.0, 3.5, 3.6, 2.5, 4.7, 1.0, 1.5, 2.2, 2.1, 3.5 }; static DBL Mid_Prm[MAX_PRM] = { 0.0, 1.5, 0.0, .5, 1.5, 0.0, 1.5, 0.0, .5, 1.5, 0.0, 1.5, -1.0, -.5, 2.5, }; #if 0 static DBL Gauss_Rand(DBL c, DBL A, DBL S) { DBL y; y = (DBL) LRAND() / MAXRAND; y = A * (1.0 - exp(-y * y * S)) / (1.0 - exp(-S)); if (NRAND(2)) return (c + y); else return (c - y); } #else /* I don't know that it makes a difference, but this one actually *is* a Gaussian. [TDA] */ /* Generate Gaussian random number: mean c, "amplitude" A (actually A is 3*standard deviation). 'S' is unused. */ /* Note this generates a pair of gaussian variables, so it saves one to give out next time it's called */ static double Gauss_Rand(DBL c, DBL A, DBL S) { static double d; static Bool ready = 0; if(ready) { ready = 0; return c + A/3 * d; } else { double x, y, w; do { x = 2.0 * (double)LRAND() / MAXRAND - 1.0; y = 2.0 * (double)LRAND() / MAXRAND - 1.0; w = x*x + y*y; } while(w >= 1.0); w = sqrt((-2 * log(w))/w); ready = 1; d = x * w; return c + A/3 * y * w; } } #endif static void Random_Prm(DBL * Prm) { int i; for (i = 0; i < MAX_PRM; ++i) Prm[i] = Gauss_Rand(Mid_Prm[i], Amp_Prm[i], 4.0); } /***************************************************************/ /* 2 examples of non-linear map */ static void Iterate_X2(ATTRACTOR * A, PRM x, PRM y, PRM * xo, PRM * yo) { PRM xx, yy, xy, x2y, y2x, Tmp; xx = (x * x) / UNIT; x2y = (xx * y) / UNIT; yy = (y * y) / UNIT; y2x = (yy * x) / UNIT; xy = (x * y) / UNIT; Tmp = A->Prm[1] * xx + A->Prm[2] * xy + A->Prm[3] * yy + A->Prm[4] * x2y; Tmp = A->Prm[0] - y + (Tmp / UNIT); *xo = DO_FOLD(Tmp); Tmp = A->Prm[6] * xx + A->Prm[7] * xy + A->Prm[8] * yy + A->Prm[9] * y2x; Tmp = A->Prm[5] + x + (Tmp / UNIT); *yo = DO_FOLD(Tmp); } static void Iterate_X3(ATTRACTOR * A, PRM x, PRM y, PRM * xo, PRM * yo) { PRM xx, yy, xy, x2y, y2x, Tmp_x, Tmp_y, Tmp_z; xx = (x * x) / UNIT; x2y = (xx * y) / UNIT; yy = (y * y) / UNIT; y2x = (yy * x) / UNIT; xy = (x * y) / UNIT; Tmp_x = A->Prm[1] * xx + A->Prm[2] * xy + A->Prm[3] * yy + A->Prm[4] * x2y; Tmp_x = A->Prm[0] - y + (Tmp_x / UNIT); Tmp_x = DO_FOLD(Tmp_x); Tmp_y = A->Prm[6] * xx + A->Prm[7] * xy + A->Prm[8] * yy + A->Prm[9] * y2x; Tmp_y = A->Prm[5] + x + (Tmp_y / UNIT); Tmp_y = DO_FOLD(Tmp_y); Tmp_z = A->Prm[11] * xx + A->Prm[12] * xy + A->Prm[13] * yy + A->Prm[14] * y2x; Tmp_z = A->Prm[10] + x + (Tmp_z / UNIT); Tmp_z = UNIT + Tmp_z * Tmp_z / UNIT; *xo = (Tmp_x * UNIT) / Tmp_z; *yo = (Tmp_y * UNIT) / Tmp_z; } static void (*Funcs[2]) (ATTRACTOR *, PRM, PRM, PRM *, PRM *) = { Iterate_X2, Iterate_X3 }; /***************************************************************/ static void free_strange(Display *display, ATTRACTOR *A) { if (A->Buffer1 != NULL) { free(A->Buffer1); A->Buffer1 = (XPoint *) NULL; } if (A->Buffer2 != NULL) { free(A->Buffer2); A->Buffer2 = (XPoint *) NULL; } if (A->dbuf) { XFreePixmap(display, A->dbuf); A->dbuf = None; } if (A->dbuf_gc) { XFreeGC(display, A->dbuf_gc); A->dbuf_gc = None; } if (A->Fold != NULL) { free(A->Fold); A->Fold = (PRM *) NULL; } } void draw_strange(ModeInfo * mi) { int i, j, n, Cur_Pt; PRM x, y, xo, yo; DBL u; XPoint *Buf; Display *display = MI_DISPLAY(mi); Window window = MI_WINDOW(mi); GC gc = MI_GC(mi); DBL Lx, Ly; void (*Iterate) (ATTRACTOR *, PRM, PRM, PRM *, PRM *); PRM xmin, xmax, ymin, ymax; ATTRACTOR *A; if (Root == NULL) return; A = &Root[MI_SCREEN(mi)]; if (A->Fold == NULL) return; Cur_Pt = A->Cur_Pt; Iterate = A->Iterate; u = (DBL) (A->Count) / 1000.0; for (j = MAX_PRM - 1; j >= 0; --j) A->Prm[j] = DBL_To_PRM((1.0 - u) * A->Prm1[j] + u * A->Prm2[j]); x = y = DBL_To_PRM(.0); for (n = SKIP_FIRST; n; --n) { (*Iterate) (A, x, y, &xo, &yo); x = xo + NRAND(8) - 4; y = yo + NRAND(8) - 4; } xmax = 0; xmin = UNIT * 4; ymax = 0; ymin = UNIT * 4; A->Cur_Pt = 0; Buf = A->Buffer2; Lx = (DBL) A->Width / UNIT / 2.2; Ly = (DBL) A->Height / UNIT / 2.2; for (n = A->Max_Pt; n; --n) { (*Iterate) (A, x, y, &xo, &yo); Buf->x = (int) (Lx * (x + DBL_To_PRM(1.1))); Buf->y = (int) (Ly * (DBL_To_PRM(1.1) - y)); /* (void) fprintf( stderr, "X,Y: %d %d ", Buf->x, Buf->y ); */ Buf++; A->Cur_Pt++; if (xo > xmax) xmax = xo; else if (xo < xmin) xmin = xo; if (yo > ymax) ymax = yo; else if (yo < ymin) ymin = yo; x = xo + NRAND(8) - 4; y = yo + NRAND(8) - 4; } MI_IS_DRAWN(mi) = True; XSetForeground(display, gc, MI_BLACK_PIXEL(mi)); if (A->dbuf != None) { /* jwz */ XSetForeground(display, A->dbuf_gc, 0); /* XDrawPoints(display, A->dbuf, A->dbuf_gc, A->Buffer1, Cur_Pt,CoordModeOrigin); */ XFillRectangle(display, A->dbuf, A->dbuf_gc, 0, 0, A->Width, A->Height); } else XDrawPoints(display, window, gc, A->Buffer1, Cur_Pt, CoordModeOrigin); if (MI_NPIXELS(mi) < 2) XSetForeground(display, gc, MI_WHITE_PIXEL(mi)); else XSetForeground(display, gc, MI_PIXEL(mi, A->Col % MI_NPIXELS(mi))); if (A->dbuf != None) { XSetForeground(display, A->dbuf_gc, 1); XDrawPoints(display, A->dbuf, A->dbuf_gc, A->Buffer2, A->Cur_Pt, CoordModeOrigin); XCopyPlane(display, A->dbuf, window, gc, 0, 0, A->Width, A->Height, 0, 0, 1); } else XDrawPoints(display, window, gc, A->Buffer2, A->Cur_Pt, CoordModeOrigin); Buf = A->Buffer1; A->Buffer1 = A->Buffer2; A->Buffer2 = Buf; if ((xmax - xmin < DBL_To_PRM(.2)) && (ymax - ymin < DBL_To_PRM(.2))) A->Count += 4 * A->Speed; else A->Count += A->Speed; if (A->Count >= 1000) { for (i = MAX_PRM - 1; i >= 0; --i) A->Prm1[i] = A->Prm2[i]; Random_Prm(A->Prm2); A->Count = 0; } A->Col++; } /***************************************************************/ void init_strange(ModeInfo * mi) { Display *display = MI_DISPLAY(mi); Window window = MI_WINDOW(mi); GC gc = MI_GC(mi); ATTRACTOR *Attractor; if (Root == NULL) { if ((Root = (ATTRACTOR *) calloc(MI_NUM_SCREENS(mi), sizeof (ATTRACTOR))) == NULL) return; } Attractor = &Root[MI_SCREEN(mi)]; if (Attractor->Fold == NULL) { int i; if ((Attractor->Fold = (PRM *) calloc(UNIT2 + 1, sizeof (PRM))) == NULL) { free_strange(display, Attractor); return; } for (i = 0; i <= UNIT2; ++i) { DBL x; /* x = ( DBL )(i)/UNIT2; */ /* x = sin( M_PI/2.0*x ); */ /* x = sqrt( x ); */ /* x = x*x; */ /* x = x*(1.0-x)*4.0; */ x = (DBL) (i) / UNIT; x = sin(x); Attractor->Fold[i] = DBL_To_PRM(x); } } if (Attractor->Buffer1 == NULL) if ((Attractor->Buffer1 = (XPoint *) calloc(MAX_POINTS, sizeof (XPoint))) == NULL) { free_strange(display, Attractor); return; } if (Attractor->Buffer2 == NULL) if ((Attractor->Buffer2 = (XPoint *) calloc(MAX_POINTS, sizeof (XPoint))) == NULL) { free_strange(display, Attractor); return; } Attractor->Max_Pt = MAX_POINTS; Attractor->Width = MI_WIDTH(mi); Attractor->Height = MI_HEIGHT(mi); Attractor->Cur_Pt = 0; Attractor->Count = 0; Attractor->Col = NRAND(MI_NPIXELS(mi)); Attractor->Speed = 4; Attractor->Iterate = Funcs[NRAND(2)]; Random_Prm(Attractor->Prm1); Random_Prm(Attractor->Prm2); #ifndef NO_DBUF if (Attractor->dbuf != None) XFreePixmap(display, Attractor->dbuf); Attractor->dbuf = XCreatePixmap(display, window, Attractor->Width, Attractor->Height, 1); /* Allocation checked */ if (Attractor->dbuf != None) { XGCValues gcv; gcv.foreground = 0; gcv.background = 0; gcv.graphics_exposures = False; gcv.function = GXcopy; if (Attractor->dbuf_gc != None) XFreeGC(display, Attractor->dbuf_gc); if ((Attractor->dbuf_gc = XCreateGC(display, Attractor->dbuf, GCForeground | GCBackground | GCGraphicsExposures | GCFunction, &gcv)) == None) { XFreePixmap(display, Attractor->dbuf); Attractor->dbuf = None; } else { XFillRectangle(display, Attractor->dbuf, Attractor->dbuf_gc, 0, 0, Attractor->Width, Attractor->Height); XSetBackground(display, gc, MI_BLACK_PIXEL(mi)); XSetFunction(display, gc, GXcopy); } } #endif MI_CLEARWINDOW(mi); /* Do not want any exposure events from XCopyPlane */ XSetGraphicsExposures(display, MI_GC(mi), False); } /***************************************************************/ void release_strange(ModeInfo * mi) { if (Root != NULL) { int screen; for (screen = 0; screen < MI_NUM_SCREENS(mi); ++screen) free_strange(MI_DISPLAY(mi), &Root[screen]); free(Root); Root = (ATTRACTOR *) NULL; } } #endif /* MODE_strange */