71cd89cdbbc0abd8a4a1ef8831abdff168f53dd9
[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  * 10-May-97: jwz@netscape.com: turned into a standalone program.
24  *                        Made it render into an offscreen bitmap and then copy
25  *                        that onto the screen, to reduce flicker.
26  */
27
28 #ifdef STANDALONE
29 # define PROGCLASS                                      "Strange"
30 # define HACK_INIT                                      init_strange
31 # define HACK_DRAW                                      draw_strange
32 # define strange_opts                           xlockmore_opts
33 # define DEFAULTS       "*delay:                2000  \n"                       \
34                                         "*ncolors:              100   \n"
35 # define SMOOTH_COLORS
36 # include "xlockmore.h"                         /* from the xscreensaver distribution */
37 #else  /* !STANDALONE */
38 # include "xlock.h"                                     /* from the xlockmore distribution */
39 #endif /* !STANDALONE */
40
41 ModeSpecOpt strange_opts = {
42   0, NULL, 0, NULL, NULL };
43
44 /*****************************************************/
45 /*****************************************************/
46
47 typedef float DBL;
48 typedef int PRM;
49
50 #define UNIT (1<<12)
51 #define UNIT2 (1<<14)
52 /* #define UNIT2 (3140*UNIT/1000) */
53
54 #define SKIP_FIRST      100
55 #define MAX_POINTS      5500
56 #define DBL_To_PRM(x)  (PRM)( (DBL)(UNIT)*(x) )
57
58
59 #define DO_FOLD(a) (a)<0 ? -Fold[ (-(a))&(UNIT2-1) ] : Fold[ (a)&(UNIT2-1) ]
60
61 /* 
62    #define DO_FOLD(a) (a)<-UNIT2 ? -Fold[(-(a))%UNIT2] : (a)<0 ? -Fold[ -(a) ] 
63
64    :  \ (a)>UNIT2 ? Fold[ (a)%UNIT2 ] : Fold[ (a) ] */
65 /* #define DO_FOLD(a) DBL_To_PRM( sin( (DBL)(a)/UNIT ) ) */
66 /* 
67    #define DO_FOLD(a) (a)<0 ? DBL_To_PRM( exp( 16.0*(a)/UNIT2 ) )-1.0 : \
68    DBL_To_PRM( 1.0-exp( -16.0*(a)/UNIT2 ) ) */
69
70 /******************************************************************/
71
72 #define MAX_PRM 3*5
73
74 typedef struct {
75         DBL         Prm1[MAX_PRM], Prm2[MAX_PRM];
76         void        (*Iterate) (PRM, PRM, PRM *, PRM *);
77         XPoint     *Buffer1, *Buffer2;
78         int         Cur_Pt, Max_Pt;
79         int         Col, Count, Speed;
80         int         Width, Height;
81     Pixmap      dbuf;   /* jwz */
82     GC          dbuf_gc;
83 } ATTRACTOR;
84
85 static ATTRACTOR *Root;
86 static PRM  xmin, xmax, ymin, ymax;
87 static PRM  Prm[MAX_PRM];
88 static PRM *Fold = NULL;
89
90 /******************************************************************/
91 /******************************************************************/
92
93 static DBL  Amp_Prm[MAX_PRM] =
94 {
95         1.0, 3.5, 3.5, 2.5, 4.7,
96         1.0, 3.5, 3.6, 2.5, 4.7,
97         1.0, 1.5, 2.2, 2.1, 3.5
98 };
99 static DBL  Mid_Prm[MAX_PRM] =
100 {
101         0.0, 1.5, 0.0, .5, 1.5,
102         0.0, 1.5, 0.0, .5, 1.5,
103         0.0, 1.5, -1.0, -.5, 2.5,
104 };
105
106 static      DBL
107 Gauss_Rand(DBL c, DBL A, DBL S)
108 {
109         DBL         y;
110
111         y = (DBL) LRAND() / MAXRAND;
112         y = A * (1.0 - exp(-y * y * S)) / (1.0 - exp(-S));
113         if (NRAND(2))
114                 return (c + y);
115         else
116                 return (c - y);
117 }
118
119 static void
120 Random_Prm(DBL * Prm)
121 {
122         int         i;
123
124         for (i = 0; i < MAX_PRM; ++i)
125                 Prm[i] = Gauss_Rand(Mid_Prm[i], Amp_Prm[i], 4.0);
126 }
127
128 /***************************************************************/
129
130    /* 2 examples of non-linear map */
131
132 static void
133 Iterate_X2(PRM x, PRM y, PRM * xo, PRM * yo)
134 {
135         PRM         xx, yy, xy, x2y, y2x, Tmp;
136
137         xx = (x * x) / UNIT;
138         x2y = (xx * y) / UNIT;
139         yy = (y * y) / UNIT;
140         y2x = (yy * x) / UNIT;
141         xy = (x * y) / UNIT;
142
143         Tmp = Prm[1] * xx + Prm[2] * xy + Prm[3] * yy + Prm[4] * x2y;
144         Tmp = Prm[0] - y + (Tmp / UNIT);
145         *xo = DO_FOLD(Tmp);
146         Tmp = Prm[6] * xx + Prm[7] * xy + Prm[8] * yy + Prm[9] * y2x;
147         Tmp = Prm[5] + x + (Tmp / UNIT);
148         *yo = DO_FOLD(Tmp);
149 }
150
151 static void
152 Iterate_X3(PRM x, PRM y, PRM * xo, PRM * yo)
153 {
154         PRM         xx, yy, xy, x2y, y2x, Tmp_x, Tmp_y, Tmp_z;
155
156         xx = (x * x) / UNIT;
157         x2y = (xx * y) / UNIT;
158         yy = (y * y) / UNIT;
159         y2x = (yy * x) / UNIT;
160         xy = (x * y) / UNIT;
161
162         Tmp_x = Prm[1] * xx + Prm[2] * xy + Prm[3] * yy + Prm[4] * x2y;
163         Tmp_x = Prm[0] - y + (Tmp_x / UNIT);
164         Tmp_x = DO_FOLD(Tmp_x);
165
166         Tmp_y = Prm[6] * xx + Prm[7] * xy + Prm[8] * yy + Prm[9] * y2x;
167         Tmp_y = Prm[5] + x + (Tmp_y / UNIT);
168
169         Tmp_y = DO_FOLD(Tmp_y);
170
171         Tmp_z = Prm[11] * xx + Prm[12] * xy + Prm[13] * yy + Prm[14] * y2x;
172         Tmp_z = Prm[10] + x + (Tmp_z / UNIT);
173         Tmp_z = UNIT + Tmp_z * Tmp_z / UNIT;
174
175         *xo = (Tmp_x * UNIT) / Tmp_z;
176         *yo = (Tmp_y * UNIT) / Tmp_z;
177 }
178
179 static void (*Funcs[2]) (PRM, PRM, PRM *, PRM *) = {
180         Iterate_X2, Iterate_X3
181 };
182
183 /***************************************************************/
184
185 void
186 draw_strange(ModeInfo * mi)
187 {
188         int         i, j, n, Max_Colors, Cur_Pt;
189         PRM         x, y, xo, yo;
190         DBL         u;
191         ATTRACTOR  *A;
192         XPoint     *Buf;
193         Display    *display;
194         GC          gc;
195         Window      window;
196         DBL         Lx, Ly;
197         void        (*Iterate) (PRM, PRM, PRM *, PRM *);
198
199         display = MI_DISPLAY(mi);
200         window = MI_WINDOW(mi);
201         gc = MI_GC(mi);
202         Max_Colors = MI_NPIXELS(mi);
203
204         A = &Root[MI_SCREEN(mi)];
205
206         Cur_Pt = A->Cur_Pt;
207         Iterate = A->Iterate;
208
209         u = (DBL) (A->Count) / 1000.0;
210         for (j = MAX_PRM - 1; j >= 0; --j)
211                 Prm[j] = DBL_To_PRM((1.0 - u) * A->Prm1[j] + u * A->Prm2[j]);
212
213         x = y = DBL_To_PRM(.0);
214         for (n = SKIP_FIRST; n; --n) {
215                 (*Iterate) (x, y, &xo, &yo);
216                 x = xo + NRAND(8) - 4;
217                 y = yo + NRAND(8) - 4;
218         }
219
220         xmax = 0;
221         xmin = UNIT * 4;
222         ymax = 0;
223         ymin = UNIT * 4;
224         A->Cur_Pt = 0;
225         Buf = A->Buffer2;
226         Lx = (DBL) A->Width / UNIT / 2.2;
227         Ly = (DBL) A->Height / UNIT / 2.2;
228         for (n = A->Max_Pt; n; --n) {
229                 (*Iterate) (x, y, &xo, &yo);
230                 Buf->x = (short) (Lx * (x + DBL_To_PRM(1.1)));
231                 Buf->y = (short) (Ly * (DBL_To_PRM(1.1) - y));
232                 /* (void) fprintf( stderr, "X,Y: %d %d    ", Buf->x, Buf->y ); */
233                 Buf++;
234                 A->Cur_Pt++;
235                 if (xo > xmax)
236                         xmax = xo;
237                 else if (xo < xmin)
238                         xmin = xo;
239                 if (yo > ymax)
240                         ymax = yo;
241                 else if (yo < ymin)
242                         ymin = yo;
243                 x = xo + NRAND(8) - 4;
244                 y = yo + NRAND(8) - 4;
245         }
246
247         XSetForeground(display, gc, MI_WIN_BLACK_PIXEL(mi));
248
249         if (A->dbuf)    /* jwz */
250           {
251                 XSetForeground(display, A->dbuf_gc, 0);
252 /*          XDrawPoints(display, A->dbuf, A->dbuf_gc, A->Buffer1,
253                                     Cur_Pt,CoordModeOrigin);*/
254                 XFillRectangle(display, A->dbuf, A->dbuf_gc, 0,0, A->Width, A->Height);
255           }
256         else
257           XDrawPoints(display, window, gc, A->Buffer1, Cur_Pt, CoordModeOrigin);
258
259         if (Max_Colors < 2)
260                 XSetForeground(display, gc, MI_WIN_WHITE_PIXEL(mi));
261         else
262                 XSetForeground(display, gc, MI_PIXEL(mi, A->Col % Max_Colors));
263
264         if (A->dbuf)
265           {
266                 XSetForeground(display, A->dbuf_gc, 1);
267                 XDrawPoints(display, A->dbuf, A->dbuf_gc, A->Buffer2, A->Cur_Pt,
268                                         CoordModeOrigin);
269           }
270         else
271           XDrawPoints(display, window, gc, A->Buffer2, A->Cur_Pt, CoordModeOrigin);
272
273         if (A->dbuf)
274           XCopyPlane(display, A->dbuf, window, gc, 0,0,A->Width,A->Height,0,0, 1);
275
276         Buf = A->Buffer1;
277         A->Buffer1 = A->Buffer2;
278         A->Buffer2 = Buf;
279
280         if ((xmax - xmin < DBL_To_PRM(.2)) && (ymax - ymin < DBL_To_PRM(.2)))
281                 A->Count += 4 * A->Speed;
282         else
283                 A->Count += A->Speed;
284         if (A->Count >= 1000) {
285                 for (i = MAX_PRM - 1; i >= 0; --i)
286                         A->Prm1[i] = A->Prm2[i];
287                 Random_Prm(A->Prm2);
288                 A->Count = 0;
289         }
290         A->Col++;
291 }
292
293
294 /***************************************************************/
295
296 void
297 init_strange(ModeInfo * mi)
298 {
299         ATTRACTOR  *Attractor;
300
301         if (Root == NULL) {
302                 Root = (ATTRACTOR *) calloc(
303                                      MI_NUM_SCREENS(mi), sizeof (ATTRACTOR));
304                 if (Root == NULL)
305                         return;
306         }
307         if (Fold == NULL) {
308                 int         i;
309
310                 Fold = (PRM *) calloc(UNIT2 + 1, sizeof (PRM));
311                 if (Fold == NULL)
312                         return;
313                 for (i = 0; i <= UNIT2; ++i) {
314                         DBL         x;
315
316                         /* x = ( DBL )(i)/UNIT2; */
317                         /* x = sin( M_PI/2.0*x ); */
318                         /* x = sqrt( x ); */
319                         /* x = x*x; */
320                         /* x = x*(1.0-x)*4.0; */
321                         x = (DBL) (i) / UNIT;
322                         x = sin(x);
323                         Fold[i] = DBL_To_PRM(x);
324                 }
325         }
326         Attractor = &Root[MI_SCREEN(mi)];
327
328         Attractor->Buffer1 = (XPoint *) calloc(MAX_POINTS, sizeof (XPoint));
329         if (Attractor->Buffer1 == NULL)
330                 goto Abort;
331         Attractor->Buffer2 = (XPoint *) calloc(MAX_POINTS, sizeof (XPoint));
332         if (Attractor->Buffer2 == NULL)
333                 goto Abort;
334         Attractor->Max_Pt = MAX_POINTS;
335
336         Attractor->Width = MI_WIN_WIDTH(mi);
337         Attractor->Height = MI_WIN_HEIGHT(mi);
338         Attractor->Cur_Pt = 0;
339         Attractor->Count = 0;
340         Attractor->Col = NRAND(MI_NPIXELS(mi));
341         Attractor->Speed = 4;
342
343         Attractor->Iterate = Funcs[NRAND(2)];
344         Random_Prm(Attractor->Prm1);
345         Random_Prm(Attractor->Prm2);
346
347         Attractor->dbuf = XCreatePixmap(MI_DISPLAY(mi), MI_WINDOW(mi),
348                                                                         Attractor->Width, Attractor->Height, 1);
349         if (Attractor->dbuf)
350           {
351                 XGCValues gcv;
352                 gcv.foreground = 0;
353                 gcv.background = 0;
354                 gcv.function = GXcopy;
355                 Attractor->dbuf_gc = XCreateGC(MI_DISPLAY(mi), Attractor->dbuf,
356                                                                            GCForeground|GCBackground|GCFunction,
357                                                                            &gcv);
358                 XFillRectangle(MI_DISPLAY(mi), Attractor->dbuf,
359                                            Attractor->dbuf_gc, 0,0, Attractor->Width,
360                                            Attractor->Height);
361                 XSetBackground(MI_DISPLAY(mi), MI_GC(mi), MI_WIN_BLACK_PIXEL(mi));
362                 XSetFunction(MI_DISPLAY(mi), MI_GC(mi), GXcopy);
363           }
364
365         XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi));
366         return;
367
368       Abort:
369         if (Attractor->Buffer1 != NULL)
370                 free(Attractor->Buffer1);
371         if (Attractor->Buffer2 != NULL)
372                 free(Attractor->Buffer2);
373         Attractor->Buffer1 = NULL;
374         Attractor->Buffer2 = NULL;
375         Attractor->Cur_Pt = 0;
376         return;
377 }
378
379 /***************************************************************/
380
381 void
382 release_strange(ModeInfo * mi)
383 {
384         int         i;
385
386         if (Root == NULL)
387                 return;
388
389         for (i = 0; i < MI_NUM_SCREENS(mi); ++i) {
390                 if (Root[i].Buffer1 != NULL)
391                         free(Root[i].Buffer1);
392                 if (Root[i].Buffer2 != NULL)
393                         free(Root[i].Buffer2);
394                 if (Root[i].dbuf)
395                     XFreePixmap(MI_DISPLAY(mi), Root[i].dbuf);
396                 if (Root[i].dbuf_gc)
397                     XFreeGC(MI_DISPLAY(mi), Root[i].dbuf_gc);
398         }
399         free(Root);
400         Root = NULL;
401         if (Fold != NULL)
402                 free(Fold);
403         Fold = NULL;
404 }