1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* strange --- strange attractors */
5 static const char sccsid[] = "@(#)strange.c 5.00 2000/11/01 xlockmore";
9 * Copyright (c) 1997 by Massimino Pascal <Pascal.Massimon@ens.fr>
11 * Permission to use, copy, modify, and distribute this software and its
12 * documentation for any purpose and without fee is hereby granted,
13 * provided that the above copyright notice appear in all copies and that
14 * both that copyright notice and this permission notice appear in
15 * supporting documentation.
17 * This file is provided AS IS with no warranties of any kind. The author
18 * shall have no liability with respect to the infringement of copyrights,
19 * trade secrets or any patents by this file or any part thereof. In no
20 * event will the author be liable for any lost revenue or profits or
21 * other special, indirect and consequential damages.
24 * 01-Nov-2000: Allocation checks
25 * 10-May-1997: jwz@jwz.org: turned into a standalone program.
26 * Made it render into an offscreen bitmap and then copy
27 * that onto the screen, to reduce flicker.
29 * strange attractors are not so hard to find...
32 /* TODO: Bring over tweaks from 3.x version.
33 * For a good idea of what's missing, diff strange.c.20081107-good2 against strange.c-3.29
34 * We forked from the 3.29 series at 20081107, so anything added since then may be missing.
39 # define DEFAULTS "*delay: 10000 \n" \
41 "*fpsSolid: True \n" \
42 "*ignoreRotation: True \n" \
44 # define SMOOTH_COLORS
45 # define refresh_strange 0
46 # include "xlockmore.h" /* from the xscreensaver distribution */
47 #else /* !STANDALONE */
48 # include "xlock.h" /* from the xlockmore distribution */
49 #endif /* !STANDALONE */
52 #define DEF_CURVE "10"
53 #define DEF_POINTS "5500"
58 static XrmOptionDescRec opts[] =
60 /* {"-curve", ".strange.curve", XrmoptionSepArg, 0}, */
61 {"-points", ".strange.points", XrmoptionSepArg, 0},
63 static argtype vars[] =
65 /* {&curve, "curve", "Curve", DEF_CURVE, t_Int},*/
66 {&points, "points", "Points", DEF_POINTS, t_Int},
68 static OptionStruct desc[] =
70 /* {"-curve", "set the curve factor of the attractors"},*/
71 {"-points", "change the number of points/iterations each frame"},
73 ENTRYPOINT ModeSpecOpt strange_opts =
74 {sizeof opts / sizeof opts[0], opts,
75 sizeof vars / sizeof vars[0], vars, desc};
78 ModStruct strange_description =
79 {"strange", "init_strange", "draw_strange", "release_strange",
80 "init_strange", "init_strange", (char *) NULL, &strange_opts,
81 1000, 1, 1, 1, 64, 1.0, "",
82 "Shows strange attractors", 0, NULL};
94 /* #define UNIT2 (3140*UNIT/1000) */
96 #define SKIP_FIRST 100
97 #define DBL_To_PRM(x) (PRM)( (DBL)(UNIT)*(x) )
100 #define DO_FOLD(a) (a)<0 ? -A->Fold[ (-(a))&(UNIT2-1) ] : A->Fold[ (a)&(UNIT2-1) ]
103 #define DO_FOLD(a) (a)<-UNIT2 ? -A->Fold[(-(a))%UNIT2] : (a)<0 ? -A->Fold[ -(a) ] :\
104 (a)>UNIT2 ? A->Fold[ (a)%UNIT2 ] : A->Fold[ (a) ]
105 #define DO_FOLD(a) DBL_To_PRM( sin( (DBL)(a)/UNIT ) )
106 #define DO_FOLD(a) (a)<0 ? DBL_To_PRM( exp( 16.0*(a)/UNIT2 ) )-1.0 : \
107 DBL_To_PRM( 1.0-exp( -16.0*(a)/UNIT2 ) )
110 /* useAccumulator performs two functions:
111 * If it is defined, then support for the accumulator will be compiled.
112 * It is also the condition for which the accumulator renderer will engage.
114 #define useAccumulator (Root->Max_Pt > 6000)
115 #define ACC_GAMMA 10.0
118 #define VARY_SPEED_TO_AVOID_BOREDOM
119 #define POINTS_HISTORY
120 #define MERGE_FRAMES 3
122 /******************************************************************/
126 typedef struct _ATTRACTOR {
127 DBL Prm1[MAX_PRM], Prm2[MAX_PRM];
128 PRM Prm[MAX_PRM], *Fold;
129 void (*Iterate) (struct _ATTRACTOR *, PRM, PRM, PRM *, PRM *);
130 XPoint *Buffer1, *Buffer2;
132 int Col, Count, Speed;
134 Pixmap dbuf; /* jwz */
136 #ifdef useAccumulator
141 static ATTRACTOR *Root = (ATTRACTOR *) NULL;
143 #ifdef useAccumulator
147 #ifdef POINTS_HISTORY
148 static int numOldPoints;
149 static int* oldPointsX;
150 static int* oldPointsY;
151 static int oldPointsIndex;
152 static int startedClearing;
155 static DBL Amp_Prm[MAX_PRM] =
157 1.0, 3.5, 3.5, 2.5, 4.7,
158 1.0, 3.5, 3.6, 2.5, 4.7,
159 1.0, 1.5, 2.2, 2.1, 3.5
161 static DBL Mid_Prm[MAX_PRM] =
163 0.0, 1.5, 0.0, .5, 1.5,
164 0.0, 1.5, 0.0, .5, 1.5,
165 0.0, 1.5, -1.0, -.5, 2.5,
169 Gauss_Rand(DBL c, DBL A, DBL S)
173 y = (DBL) LRAND() / MAXRAND;
174 y = A * (1.0 - exp(-y * y * S)) / (1.0 - exp(-S));
182 Random_Prm(DBL * Prm)
186 for (i = 0; i < MAX_PRM; ++i)
187 Prm[i] = Gauss_Rand(Mid_Prm[i], Amp_Prm[i], 4.0);
190 /***************************************************************/
192 /* 2 examples of non-linear map */
195 Iterate_X2(ATTRACTOR * A, PRM x, PRM y, PRM * xo, PRM * yo)
197 PRM xx, yy, xy, x2y, y2x, Tmp;
200 x2y = (xx * y) / UNIT;
202 y2x = (yy * x) / UNIT;
205 Tmp = A->Prm[1] * xx + A->Prm[2] * xy + A->Prm[3] * yy + A->Prm[4] * x2y;
206 Tmp = A->Prm[0] - y + (Tmp / UNIT);
208 Tmp = A->Prm[6] * xx + A->Prm[7] * xy + A->Prm[8] * yy + A->Prm[9] * y2x;
209 Tmp = A->Prm[5] + x + (Tmp / UNIT);
214 Iterate_X3(ATTRACTOR * A, PRM x, PRM y, PRM * xo, PRM * yo)
216 PRM xx, yy, xy, x2y, y2x, Tmp_x, Tmp_y, Tmp_z;
219 x2y = (xx * y) / UNIT;
221 y2x = (yy * x) / UNIT;
224 Tmp_x = A->Prm[1] * xx + A->Prm[2] * xy + A->Prm[3] * yy + A->Prm[4] * x2y;
225 Tmp_x = A->Prm[0] - y + (Tmp_x / UNIT);
226 Tmp_x = DO_FOLD(Tmp_x);
228 Tmp_y = A->Prm[6] * xx + A->Prm[7] * xy + A->Prm[8] * yy + A->Prm[9] * y2x;
229 Tmp_y = A->Prm[5] + x + (Tmp_y / UNIT);
231 Tmp_y = DO_FOLD(Tmp_y);
233 Tmp_z = A->Prm[11] * xx + A->Prm[12] * xy + A->Prm[13] * yy + A->Prm[14] * y2x;
234 Tmp_z = A->Prm[10] + x + (Tmp_z / UNIT);
235 Tmp_z = UNIT + Tmp_z * Tmp_z / UNIT;
237 *xo = (Tmp_x * UNIT) / Tmp_z;
238 *yo = (Tmp_y * UNIT) / Tmp_z;
241 static void (*Funcs[2]) (ATTRACTOR *, PRM, PRM, PRM *, PRM *) = {
242 Iterate_X2, Iterate_X3
245 /***************************************************************/
248 free_strange(Display *display, ATTRACTOR *A)
250 if (A->Buffer1 != NULL) {
251 (void) free((void *) A->Buffer1);
252 A->Buffer1 = (XPoint *) NULL;
254 if (A->Buffer2 != NULL) {
255 (void) free((void *) A->Buffer2);
256 A->Buffer2 = (XPoint *) NULL;
259 XFreePixmap(display, A->dbuf);
263 XFreeGC(display, A->dbuf_gc);
266 if (A->Fold != NULL) {
267 (void) free((void *) A->Fold);
268 A->Fold = (PRM *) NULL;
273 draw_strange(ModeInfo * mi)
279 Display *display = MI_DISPLAY(mi);
280 Window window = MI_WINDOW(mi);
283 void (*Iterate) (ATTRACTOR *, PRM, PRM, PRM *, PRM *);
284 PRM xmin, xmax, ymin, ymax;
289 A = &Root[MI_SCREEN(mi)];
294 Iterate = A->Iterate;
296 u = (DBL) (A->Count) / 40000.0;
297 for (j = MAX_PRM - 1; j >= 0; --j)
298 A->Prm[j] = DBL_To_PRM((1.0 - u) * A->Prm1[j] + u * A->Prm2[j]);
300 /* We collect the accumulation of the orbits in the 2d int array field. */
301 #ifndef POINTS_HISTORY
302 #ifdef useAccumulator
303 if (useAccumulator) {
304 for (i=0;i<A->Width;i++) {
305 for (j=0;j<A->Height;j++) {
314 x = y = DBL_To_PRM(.0);
315 for (n = SKIP_FIRST; n; --n) {
316 (*Iterate) (A, x, y, &xo, &yo);
317 x = xo + NRAND(8) - 4;
318 y = yo + NRAND(8) - 4;
327 Lx = (DBL) A->Width / UNIT / 2.2;
328 Ly = (DBL) A->Height / UNIT / 2.2;
329 for (n = A->Max_Pt; n; --n) {
330 (*Iterate) (A, x, y, &xo, &yo);
331 #ifdef useAccumulator
332 if (useAccumulator) {
334 mx = (short) ( A->Width*0.1 + A->Width*0.8 * (xo - xmin) / (xmax - xmin) );
335 my = (short) ( A->Width*0.1 + (A->Height - A->Width*0.2) * (yo - ymin) / (ymax - ymin) );
336 if (mx>=0 && my>=0 && mx<A->Width && my<A->Height) {
339 #ifdef POINTS_HISTORY
340 /* #define clearOldPoint(i) { if (startedClearing) { field[oldPoints[i].x][oldPoints[i].y]--; } }
341 #define saveUnplot(X,Y) { clearOldPoint(oldPointsIndex) oldPoints[oldPointsIndex].x = X; oldPoints[oldPointsIndex].y = Y; oldPointsIndex = (oldPointsIndex + 1) % numOldPoints; if (oldPointsIndex==0) { startedClearing=1; } }
343 if (startedClearing) {
344 int oldX = oldPointsX[oldPointsIndex];
345 int oldY = oldPointsY[oldPointsIndex];
346 if (oldX>=0 && oldY>=0 && oldX<A->Width && oldY<A->Height) {
347 A->accMap[oldX][oldY]--;
350 oldPointsX[oldPointsIndex] = mx;
351 oldPointsY[oldPointsIndex] = my;
352 oldPointsIndex = (oldPointsIndex + 1) % numOldPoints;
353 if (oldPointsIndex==0) { startedClearing=1; }
357 Buf->x = (int) (Lx * (x + DBL_To_PRM(1.1)));
358 Buf->y = (int) (Ly * (DBL_To_PRM(1.1) - y));
361 #ifdef useAccumulator
364 /* (void) fprintf( stderr, "X,Y: %d %d ", Buf->x, Buf->y ); */
373 x = xo + NRAND(8) - 4;
374 y = yo + NRAND(8) - 4;
377 MI_IS_DRAWN(mi) = True;
379 XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
381 #ifdef useAccumulator
382 if (useAccumulator) {
385 #ifdef VARY_SPEED_TO_AVOID_BOREDOM
388 colorScale = (A->Width*A->Height/640.0/480.0*800000.0/(float)A->Max_Pt*(float)NUM_COLS/256);
389 if (A->dbuf != None) {
390 XSetForeground(display, A->dbuf_gc, 0);
391 XFillRectangle(display, A->dbuf, A->dbuf_gc, 0, 0, A->Width, A->Height);
393 XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
394 XFillRectangle(display, window, gc, 0, 0, A->Width, A->Height);
396 for (i=0;i<A->Width;i++) {
397 for (j=0;j<A->Height;j++) {
398 if (A->accMap[i][j]>0) {
399 col = (float)A->accMap[i][j] * colorScale;
400 if (col>NUM_COLS-1) {
403 #ifdef VARY_SPEED_TO_AVOID_BOREDOM
405 if (col<NUM_COLS-1) /* we don't count maxxed out pixels */
409 if (MI_NPIXELS(mi) < 2)
410 XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
412 /*XSetForeground(display, gc, MI_PIXEL(mi, A->Col % MI_NPIXELS(mi)));*/
413 XSetForeground(display, gc, cols[col].pixel);
414 if (A->dbuf != None) {
415 XSetForeground(display, A->dbuf_gc, cols[col].pixel);
416 XDrawPoint(display, A->dbuf, A->dbuf_gc, i, j);
418 XSetForeground(display, gc, cols[col].pixel);
419 XDrawPoint(display, window, gc, i, j);
424 if (A->dbuf != None) {
425 XCopyArea(display, A->dbuf, window, gc, 0, 0, A->Width, A->Height, 0, 0);
427 #ifdef VARY_SPEED_TO_AVOID_BOREDOM
428 /* Increaase the rate of change of the parameters if the attractor has become visually boring. */
429 if ((xmax - xmin < DBL_To_PRM(.2)) && (ymax - ymin < DBL_To_PRM(.2))) {
431 } else if (pixelCount>0 && pixelCount<A->Width*A->Height/1000) {
432 A->Speed *= 1.25; /* A->Count = 1000; */
434 A->Speed = 4; /* reset to normal/default */
438 A->Count += A->Speed;
439 if (A->Count >= 1000) {
440 for (i = MAX_PRM - 1; i >= 0; --i)
441 A->Prm1[i] = A->Prm2[i];
449 if (A->dbuf != None) { /* jwz */
450 XSetForeground(display, A->dbuf_gc, 0);
451 /* XDrawPoints(display, A->dbuf, A->dbuf_gc, A->Buffer1,
452 Cur_Pt,CoordModeOrigin); */
453 XFillRectangle(display, A->dbuf, A->dbuf_gc, 0, 0, A->Width, A->Height);
455 XDrawPoints(display, window, gc, A->Buffer1, Cur_Pt, CoordModeOrigin);
457 if (MI_NPIXELS(mi) < 2)
458 XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
460 XSetForeground(display, gc, MI_PIXEL(mi, A->Col % MI_NPIXELS(mi)));
462 if (A->dbuf != None) {
463 XSetForeground(display, A->dbuf_gc, 1);
464 XDrawPoints(display, A->dbuf, A->dbuf_gc, A->Buffer2, A->Cur_Pt,
466 XCopyPlane(display, A->dbuf, window, gc, 0, 0, A->Width, A->Height, 0, 0, 1);
468 XDrawPoints(display, window, gc, A->Buffer2, A->Cur_Pt, CoordModeOrigin);
470 #ifdef useAccumulator
475 A->Buffer1 = A->Buffer2;
478 if ((xmax - xmin < DBL_To_PRM(.2)) && (ymax - ymin < DBL_To_PRM(.2)))
479 A->Count += 4 * A->Speed;
481 A->Count += A->Speed;
482 if (A->Count >= 1000) {
483 for (i = MAX_PRM - 1; i >= 0; --i)
484 A->Prm1[i] = A->Prm2[i];
489 mi->recursion_depth = A->Count;
493 /***************************************************************/
496 init_strange(ModeInfo * mi)
498 Display *display = MI_DISPLAY(mi);
499 Window window = MI_WINDOW(mi);
503 ATTRACTOR *Attractor;
505 #ifdef POINTS_HISTORY
511 if ((Root = (ATTRACTOR *) calloc(MI_NUM_SCREENS(mi),
512 sizeof (ATTRACTOR))) == NULL)
515 Attractor = &Root[MI_SCREEN(mi)];
517 if (Attractor->Fold == NULL) {
520 if ((Attractor->Fold = (PRM *) calloc(UNIT2 + 1,
521 sizeof (PRM))) == NULL) {
522 free_strange(display, Attractor);
525 for (i = 0; i <= UNIT2; ++i) {
528 /* x = ( DBL )(i)/UNIT2; */
529 /* x = sin( M_PI/2.0*x ); */
532 /* x = x*(1.0-x)*4.0; */
533 x = (DBL) (i) / UNIT;
535 Attractor->Fold[i] = DBL_To_PRM(x);
539 Attractor->Max_Pt = points;
541 if (Attractor->Buffer1 == NULL)
542 if ((Attractor->Buffer1 = (XPoint *) calloc(Attractor->Max_Pt,
543 sizeof (XPoint))) == NULL) {
544 free_strange(display, Attractor);
547 if (Attractor->Buffer2 == NULL)
548 if ((Attractor->Buffer2 = (XPoint *) calloc(Attractor->Max_Pt,
549 sizeof (XPoint))) == NULL) {
550 free_strange(display, Attractor);
554 Attractor->Width = MI_WIDTH(mi);
555 Attractor->Height = MI_HEIGHT(mi);
556 Attractor->Cur_Pt = 0;
557 Attractor->Count = 0;
558 Attractor->Col = NRAND(MI_NPIXELS(mi));
559 Attractor->Speed = 4;
561 Attractor->Iterate = Funcs[NRAND(2)];
562 Random_Prm(Attractor->Prm1);
563 Random_Prm(Attractor->Prm2);
565 if (Attractor->dbuf != None)
566 XFreePixmap(display, Attractor->dbuf);
567 #ifdef useAccumulator
568 #define colorDepth ( useAccumulator ? MI_DEPTH(mi) : 1 )
572 Attractor->dbuf = XCreatePixmap(display, window,
573 Attractor->Width, Attractor->Height, colorDepth);
574 /* Allocation checked */
575 if (Attractor->dbuf != None) {
581 gcv.graphics_exposures = False;
582 #endif /* HAVE_COCOA */
583 gcv.function = GXcopy;
585 if (Attractor->dbuf_gc != None)
586 XFreeGC(display, Attractor->dbuf_gc);
588 if ((Attractor->dbuf_gc = XCreateGC(display, Attractor->dbuf,
590 GCGraphicsExposures |
591 #endif /* HAVE_COCOA */
592 GCFunction | GCForeground | GCBackground,
594 XFreePixmap(display, Attractor->dbuf);
595 Attractor->dbuf = None;
597 XFillRectangle(display, Attractor->dbuf, Attractor->dbuf_gc,
598 0, 0, Attractor->Width, Attractor->Height);
599 XSetBackground(display, gc, MI_BLACK_PIXEL(mi));
600 XSetFunction(display, gc, GXcopy);
606 #ifdef useAccumulator
608 if (useAccumulator) {
609 XWindowAttributes xgwa;
611 XGetWindowAttributes (display, window, &xgwa);
612 /* cmap = xgwa.colormap; */
613 /* cmap = XCreateColormap(display, window, MI_VISUAL(mi), AllocAll); */
614 Attractor->accMap = (int**)calloc(Attractor->Width,sizeof(int*));
615 for (i=0;i<Attractor->Width;i++) {
616 Attractor->accMap[i] = (int*)calloc(Attractor->Height,sizeof(int));
617 for (j=0;j<Attractor->Height;j++) {
618 Attractor->accMap[i][j] = 0;
621 #ifdef POINTS_HISTORY
622 numOldPoints = A->Max_Pt * MERGE_FRAMES;
623 oldPointsX = (int*)calloc(numOldPoints,sizeof(int));
624 oldPointsY = (int*)calloc(numOldPoints,sizeof(int));
626 cols = (XColor*)calloc(NUM_COLS,sizeof(XColor));
627 for (i=0;i<NUM_COLS;i++) {
631 li = MINBLUE + (255.0-MINBLUE) * log(1.0 + ACC_GAMMA*(float)i/NUM_COLS) / log(1.0 + ACC_GAMMA);
635 cols[i].blue = 65536*li/FULLBLUE;
637 cols[i].red = 65536*(li-FULLBLUE)/(256-FULLBLUE);
638 cols[i].green = 65536*(li-FULLBLUE)/(256-FULLBLUE);
639 cols[i].blue = 65535;
641 XAllocColor (display, xgwa.colormap, &cols[i]);
643 if (!XAllocColor(MI_DISPLAY(mi), cmap, &cols[i])) {
644 if (!XAllocColor(display, cmap, &cols[i])) {
645 cols[i].pixel = WhitePixel (display, DefaultScreen (display));
646 cols[i].red = cols[i].green = cols[i].blue = 0xFFFF;
651 XSetWindowColormap(display, window, cmap);
652 (void) XSetWMColormapWindows(display, window, &window, 1);
653 XInstallColormap(display, cmap);
654 XStoreColors(display, cmap, cols, 256);
661 /* Do not want any exposure events from XCopyPlane */
662 XSetGraphicsExposures(display, MI_GC(mi), False);
666 reshape_strange(ModeInfo * mi, int width, int height)
668 XClearWindow (MI_DISPLAY (mi), MI_WINDOW(mi));
672 /***************************************************************/
675 release_strange(ModeInfo * mi)
680 #ifdef useAccumulator
682 (void) free((void *) cols);
683 for (i=0;i<Root->Width;i++) {
684 (void) free((void *) Root->accMap[i]);
686 (void) free((void *) Root->accMap);
688 #ifdef POINTS_HISTORY
692 for (screen = 0; screen < MI_NUM_SCREENS(mi); ++screen)
693 free_strange(MI_DISPLAY(mi), &Root[screen]);
694 (void) free((void *) Root);
695 Root = (ATTRACTOR *) NULL;
700 strange_handle_event (ModeInfo *mi, XEvent *event)
702 if (screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event))
704 reshape_strange (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
711 XSCREENSAVER_MODULE ("Strange", strange)
713 #endif /* MODE_strange */