a1bda34f70ad7ef277fc405f86b84e81d74164c4
[xscreensaver] / hacks / strange.c
1 /* -*- Mode: C; tab-width: 4 -*-
2  * strange --- Strange attractors are not so hard to find...
3  */
4 #if !defined( lint ) && !defined( SABER )
5 static const char sccsid[] = "@(#)strange.c        4.02 97/04/01 xlockmore";
6 #endif
7
8 /* Copyright (c) 1997 by Massimino Pascal (Pascal.Massimon@ens.fr)
9  *
10  * Permission to use, copy, modify, and distribute this software and its
11  * documentation for any purpose and without fee is hereby granted,
12  * provided that the above copyright notice appear in all copies and that
13  * both that copyright notice and this permission notice appear in
14  * supporting documentation.
15  *
16  * This file is provided AS IS with no warranties of any kind.  The author
17  * shall have no liability with respect to the infringement of copyrights,
18  * trade secrets or any patents by this file or any part thereof.  In no
19  * event will the author be liable for any lost revenue or profits or
20  * other special, indirect and consequential damages.
21  *
22  * Revision History:
23  * 30-Jul-98: sineswiper@resonatorsoft.com: added curve factor (discovered
24  *         while experimenting with the Gauss_Rand function).
25  * 10-May-97: 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.
28  */
29
30 #ifdef STANDALONE
31 # define PROGCLASS                                      "Strange"
32 # define HACK_INIT                                      init_strange
33 # define HACK_DRAW                                      draw_strange
34 # define strange_opts                           xlockmore_opts
35 # define DEFAULTS       "*delay:                2000  \n"                       \
36                                         "*ncolors:              100   \n"
37 # define SMOOTH_COLORS
38 # include "xlockmore.h"                         /* from the xscreensaver distribution */
39 #else  /* !STANDALONE */
40 # include "xlock.h"                                     /* from the xlockmore distribution */
41 #endif /* !STANDALONE */
42
43 /*****************************************************/
44 /*****************************************************/
45
46 typedef float DBL;
47 typedef int PRM;
48
49 #define UNIT (1<<12)
50 #define UNIT2 (1<<14)
51 /* #define UNIT2 (3140*UNIT/1000) */
52
53 #define SKIP_FIRST      100
54 #define MAX_POINTS      5500
55 #define DBL_To_PRM(x)  (PRM)( (DBL)(UNIT)*(x) )
56
57
58 #define DO_FOLD(a) (a)<0 ? -Fold[ (-(a))&(UNIT2-1) ] : Fold[ (a)&(UNIT2-1) ]
59
60 /* 
61    #define DO_FOLD(a) (a)<-UNIT2 ? -Fold[(-(a))%UNIT2] : (a)<0 ? -Fold[ -(a) ] 
62
63    :  \ (a)>UNIT2 ? Fold[ (a)%UNIT2 ] : Fold[ (a) ] */
64 /* #define DO_FOLD(a) DBL_To_PRM( sin( (DBL)(a)/UNIT ) ) */
65 /* 
66    #define DO_FOLD(a) (a)<0 ? DBL_To_PRM( exp( 16.0*(a)/UNIT2 ) )-1.0 : \
67    DBL_To_PRM( 1.0-exp( -16.0*(a)/UNIT2 ) ) */
68
69 /******************************************************************/
70
71 #define MAX_PRM 3*5
72
73 typedef struct {
74         DBL         Prm1[MAX_PRM], Prm2[MAX_PRM];
75         void        (*Iterate) (PRM, PRM, PRM *, PRM *);
76         XPoint     *Buffer1, *Buffer2;
77         int         Cur_Pt, Max_Pt;
78         int         Col, Count, Speed;
79         int         Width, Height;
80     Pixmap      dbuf;   /* jwz */
81     GC          dbuf_gc;
82 } ATTRACTOR;
83
84 static ATTRACTOR *Root;
85 static PRM  xmin, xmax, ymin, ymax;
86 static PRM  Prm[MAX_PRM];
87 static PRM *Fold = NULL;
88
89 static int curve;
90
91 static XrmOptionDescRec opts[] =
92 {
93         {"-curve", ".strange.curve", XrmoptionSepArg, (caddr_t) "10"},
94 };
95 static OptionStruct desc[] =
96 {
97         {"-curve", "set the curve factor of the attractors"},
98 };
99
100 ModeSpecOpt strange_opts = { 1, opts, 0, NULL, desc };
101
102 /******************************************************************/
103 /******************************************************************/
104
105 static DBL  Amp_Prm[MAX_PRM] =
106 {
107         1.0, 3.5, 3.5, 2.5, 4.7,
108         1.0, 3.5, 3.6, 2.5, 4.7,
109         1.0, 1.5, 2.2, 2.1, 3.5
110 };
111 static DBL  Mid_Prm[MAX_PRM] =
112 {
113         0.0, 1.5, 0.0, .5, 1.5,
114         0.0, 1.5, 0.0, .5, 1.5,
115         0.0, 1.5, -1.0, -.5, 2.5,
116 };
117
118 static      DBL
119 Gauss_Rand(DBL c, DBL A, DBL S)
120 {
121         DBL         y,z;
122
123         y = (DBL) LRAND() / MAXRAND;
124         z = curve / 10;
125         y = A * (z - exp(-y * y * S)) / (z - exp(-S));
126         if (NRAND(2))
127                 return (c + y);
128         else
129                 return (c - y);
130 }
131
132 static void
133 Random_Prm(DBL * Prm)
134 {
135         int         i;
136
137         for (i = 0; i < MAX_PRM; ++i)
138                 Prm[i] = Gauss_Rand(Mid_Prm[i], Amp_Prm[i], 4.0);
139 }
140
141 /***************************************************************/
142
143    /* 2 examples of non-linear map */
144
145 static void
146 Iterate_X2(PRM x, PRM y, PRM * xo, PRM * yo)
147 {
148         PRM         xx, yy, xy, x2y, y2x, Tmp;
149
150         xx = (x * x) / UNIT;
151         x2y = (xx * y) / UNIT;
152         yy = (y * y) / UNIT;
153         y2x = (yy * x) / UNIT;
154         xy = (x * y) / UNIT;
155
156         Tmp = Prm[1] * xx + Prm[2] * xy + Prm[3] * yy + Prm[4] * x2y;
157         Tmp = Prm[0] - y + (Tmp / UNIT);
158         *xo = DO_FOLD(Tmp);
159         Tmp = Prm[6] * xx + Prm[7] * xy + Prm[8] * yy + Prm[9] * y2x;
160         Tmp = Prm[5] + x + (Tmp / UNIT);
161         *yo = DO_FOLD(Tmp);
162 }
163
164 static void
165 Iterate_X3(PRM x, PRM y, PRM * xo, PRM * yo)
166 {
167         PRM         xx, yy, xy, x2y, y2x, Tmp_x, Tmp_y, Tmp_z;
168
169         xx = (x * x) / UNIT;
170         x2y = (xx * y) / UNIT;
171         yy = (y * y) / UNIT;
172         y2x = (yy * x) / UNIT;
173         xy = (x * y) / UNIT;
174
175         Tmp_x = Prm[1] * xx + Prm[2] * xy + Prm[3] * yy + Prm[4] * x2y;
176         Tmp_x = Prm[0] - y + (Tmp_x / UNIT);
177         Tmp_x = DO_FOLD(Tmp_x);
178
179         Tmp_y = Prm[6] * xx + Prm[7] * xy + Prm[8] * yy + Prm[9] * y2x;
180         Tmp_y = Prm[5] + x + (Tmp_y / UNIT);
181
182         Tmp_y = DO_FOLD(Tmp_y);
183
184         Tmp_z = Prm[11] * xx + Prm[12] * xy + Prm[13] * yy + Prm[14] * y2x;
185         Tmp_z = Prm[10] + x + (Tmp_z / UNIT);
186         Tmp_z = UNIT + Tmp_z * Tmp_z / UNIT;
187
188         *xo = (Tmp_x * UNIT) / Tmp_z;
189         *yo = (Tmp_y * UNIT) / Tmp_z;
190 }
191
192 static void (*Funcs[2]) (PRM, PRM, PRM *, PRM *) = {
193         Iterate_X2, Iterate_X3
194 };
195
196 /***************************************************************/
197
198 void
199 draw_strange(ModeInfo * mi)
200 {
201         int         i, j, n, Max_Colors, Cur_Pt;
202         PRM         x, y, xo, yo;
203         DBL         u;
204         ATTRACTOR  *A;
205         XPoint     *Buf;
206         Display    *display;
207         GC          gc;
208         Window      window;
209         DBL         Lx, Ly;
210         void        (*Iterate) (PRM, PRM, PRM *, PRM *);
211
212         display = MI_DISPLAY(mi);
213         window = MI_WINDOW(mi);
214         gc = MI_GC(mi);
215         Max_Colors = MI_NPIXELS(mi);
216
217         A = &Root[MI_SCREEN(mi)];
218
219         Cur_Pt = A->Cur_Pt;
220         Iterate = A->Iterate;
221
222         u = (DBL) (A->Count) / 1000.0;
223         for (j = MAX_PRM - 1; j >= 0; --j)
224                 Prm[j] = DBL_To_PRM((1.0 - u) * A->Prm1[j] + u * A->Prm2[j]);
225
226         x = y = DBL_To_PRM(.0);
227         for (n = SKIP_FIRST; n; --n) {
228                 (*Iterate) (x, y, &xo, &yo);
229                 x = xo + NRAND(8) - 4;
230                 y = yo + NRAND(8) - 4;
231         }
232
233         xmax = 0;
234         xmin = UNIT * 4;
235         ymax = 0;
236         ymin = UNIT * 4;
237         A->Cur_Pt = 0;
238         Buf = A->Buffer2;
239         Lx = (DBL) A->Width / UNIT / 2.2;
240         Ly = (DBL) A->Height / UNIT / 2.2;
241         for (n = A->Max_Pt; n; --n) {
242                 (*Iterate) (x, y, &xo, &yo);
243                 Buf->x = (short) (Lx * (x + DBL_To_PRM(1.1)));
244                 Buf->y = (short) (Ly * (DBL_To_PRM(1.1) - y));
245                 /* (void) fprintf( stderr, "X,Y: %d %d    ", Buf->x, Buf->y ); */
246                 Buf++;
247                 A->Cur_Pt++;
248                 if (xo > xmax)
249                         xmax = xo;
250                 else if (xo < xmin)
251                         xmin = xo;
252                 if (yo > ymax)
253                         ymax = yo;
254                 else if (yo < ymin)
255                         ymin = yo;
256                 x = xo + NRAND(8) - 4;
257                 y = yo + NRAND(8) - 4;
258         }
259
260         XSetForeground(display, gc, MI_WIN_BLACK_PIXEL(mi));
261
262         if (A->dbuf)    /* jwz */
263           {
264                 XSetForeground(display, A->dbuf_gc, 0);
265 /*          XDrawPoints(display, A->dbuf, A->dbuf_gc, A->Buffer1,
266                                     Cur_Pt,CoordModeOrigin);*/
267                 XFillRectangle(display, A->dbuf, A->dbuf_gc, 0,0, A->Width, A->Height);
268           }
269         else
270           XDrawPoints(display, window, gc, A->Buffer1, Cur_Pt, CoordModeOrigin);
271
272         if (Max_Colors < 2)
273                 XSetForeground(display, gc, MI_WIN_WHITE_PIXEL(mi));
274         else
275                 XSetForeground(display, gc, MI_PIXEL(mi, A->Col % Max_Colors));
276
277         if (A->dbuf)
278           {
279                 XSetForeground(display, A->dbuf_gc, 1);
280                 XDrawPoints(display, A->dbuf, A->dbuf_gc, A->Buffer2, A->Cur_Pt,
281                                         CoordModeOrigin);
282           }
283         else
284           XDrawPoints(display, window, gc, A->Buffer2, A->Cur_Pt, CoordModeOrigin);
285
286         if (A->dbuf)
287           XCopyPlane(display, A->dbuf, window, gc, 0,0,A->Width,A->Height,0,0, 1);
288
289         Buf = A->Buffer1;
290         A->Buffer1 = A->Buffer2;
291         A->Buffer2 = Buf;
292
293         if ((xmax - xmin < DBL_To_PRM(.2)) && (ymax - ymin < DBL_To_PRM(.2)))
294                 A->Count += 4 * A->Speed;
295         else
296                 A->Count += A->Speed;
297         if (A->Count >= 1000) {
298                 for (i = MAX_PRM - 1; i >= 0; --i)
299                         A->Prm1[i] = A->Prm2[i];
300                 Random_Prm(A->Prm2);
301                 A->Count = 0;
302         }
303         A->Col++;
304 }
305
306
307 /***************************************************************/
308
309 void
310 init_strange(ModeInfo * mi)
311 {
312         ATTRACTOR  *Attractor;
313
314         curve = get_integer_resource ("curve", "Integer");
315         if (curve <= 0) curve = 10;
316
317         if (Root == NULL) {
318                 Root = (ATTRACTOR *) calloc(
319                                      MI_NUM_SCREENS(mi), sizeof (ATTRACTOR));
320                 if (Root == NULL)
321                         return;
322         }
323         if (Fold == NULL) {
324                 int         i;
325
326                 Fold = (PRM *) calloc(UNIT2 + 1, sizeof (PRM));
327                 if (Fold == NULL)
328                         return;
329                 for (i = 0; i <= UNIT2; ++i) {
330                         DBL         x;
331
332                         /* x = ( DBL )(i)/UNIT2; */
333                         /* x = sin( M_PI/2.0*x ); */
334                         /* x = sqrt( x ); */
335                         /* x = x*x; */
336                         /* x = x*(1.0-x)*4.0; */
337                         x = (DBL) (i) / UNIT;
338                         x = sin(x);
339                         Fold[i] = DBL_To_PRM(x);
340                 }
341         }
342         Attractor = &Root[MI_SCREEN(mi)];
343
344         Attractor->Buffer1 = (XPoint *) calloc(MAX_POINTS, sizeof (XPoint));
345         if (Attractor->Buffer1 == NULL)
346                 goto Abort;
347         Attractor->Buffer2 = (XPoint *) calloc(MAX_POINTS, sizeof (XPoint));
348         if (Attractor->Buffer2 == NULL)
349                 goto Abort;
350         Attractor->Max_Pt = MAX_POINTS;
351
352         Attractor->Width = MI_WIN_WIDTH(mi);
353         Attractor->Height = MI_WIN_HEIGHT(mi);
354         Attractor->Cur_Pt = 0;
355         Attractor->Count = 0;
356         Attractor->Col = NRAND(MI_NPIXELS(mi));
357         Attractor->Speed = 4;
358
359         Attractor->Iterate = Funcs[NRAND(2)];
360         Random_Prm(Attractor->Prm1);
361         Random_Prm(Attractor->Prm2);
362
363         Attractor->dbuf = XCreatePixmap(MI_DISPLAY(mi), MI_WINDOW(mi),
364                                                                         Attractor->Width, Attractor->Height, 1);
365         if (Attractor->dbuf)
366           {
367                 XGCValues gcv;
368                 gcv.foreground = 0;
369                 gcv.background = 0;
370                 gcv.function = GXcopy;
371                 Attractor->dbuf_gc = XCreateGC(MI_DISPLAY(mi), Attractor->dbuf,
372                                                                            GCForeground|GCBackground|GCFunction,
373                                                                            &gcv);
374                 XFillRectangle(MI_DISPLAY(mi), Attractor->dbuf,
375                                            Attractor->dbuf_gc, 0,0, Attractor->Width,
376                                            Attractor->Height);
377                 XSetBackground(MI_DISPLAY(mi), MI_GC(mi), MI_WIN_BLACK_PIXEL(mi));
378                 XSetFunction(MI_DISPLAY(mi), MI_GC(mi), GXcopy);
379           }
380
381         XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi));
382         return;
383
384       Abort:
385         if (Attractor->Buffer1 != NULL)
386                 free(Attractor->Buffer1);
387         if (Attractor->Buffer2 != NULL)
388                 free(Attractor->Buffer2);
389         Attractor->Buffer1 = NULL;
390         Attractor->Buffer2 = NULL;
391         Attractor->Cur_Pt = 0;
392         return;
393 }
394
395 /***************************************************************/
396
397 void
398 release_strange(ModeInfo * mi)
399 {
400         int         i;
401
402         if (Root == NULL)
403                 return;
404
405         for (i = 0; i < MI_NUM_SCREENS(mi); ++i) {
406                 if (Root[i].Buffer1 != NULL)
407                         free(Root[i].Buffer1);
408                 if (Root[i].Buffer2 != NULL)
409                         free(Root[i].Buffer2);
410                 if (Root[i].dbuf)
411                     XFreePixmap(MI_DISPLAY(mi), Root[i].dbuf);
412                 if (Root[i].dbuf_gc)
413                     XFreeGC(MI_DISPLAY(mi), Root[i].dbuf_gc);
414         }
415         free(Root);
416         Root = NULL;
417         if (Fold != NULL)
418                 free(Fold);
419         Fold = NULL;
420 }