3bccf872073170a619a1d67f8d570a63a0da3b97
[xscreensaver] / hacks / ifs.c
1 /* -*- Mode: C; tab-width: 4 -*-
2  * ifs --- Modified iterated functions system.
3  */
4 #if !defined( lint ) && !defined( SABER )
5 static const char sccsid[] = "@(#)ifs.c    4.02 97/04/01 xlockmore";
6 #endif
7
8 /* Copyright (c) 1997 by Massimino Pascal (Pascal.Massimon@ens.fr)
9  *
10  *
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.
16  *
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.
22  *
23  * Revision History:
24  * 10-May-97: jwz@jwz.org: turned into a standalone program.
25  *                        Made it render into an offscreen bitmap and then copy
26  *                        that onto the screen, to reduce flicker.
27  */
28
29 #ifdef STANDALONE
30 # define PROGCLASS                                      "IFS"
31 # define HACK_INIT                                      init_ifs
32 # define HACK_DRAW                                      draw_ifs
33 # define ifs_opts                                       xlockmore_opts
34 # define DEFAULTS       "*delay:                20000 \n"                       \
35                                         "*ncolors:              100   \n"
36 # define SMOOTH_COLORS
37 # include "xlockmore.h"                         /* from the xscreensaver distribution */
38 #else  /* !STANDALONE */
39 # include "xlock.h"                                     /* from the xlockmore distribution */
40 #endif /* !STANDALONE */
41
42 ModeSpecOpt ifs_opts = {
43   0, NULL, 0, NULL, NULL };
44
45 /*****************************************************/
46 /*****************************************************/
47
48 typedef float DBL;
49 typedef short int F_PT;
50
51 /* typedef float               F_PT; */
52
53 /*****************************************************/
54
55 #define FIX 12
56 #define UNIT   ( 1<<FIX )
57 #define MAX_SIMI  6
58
59    /* settings for a PC 120Mhz... */
60 #define MAX_DEPTH_2  10
61 #define MAX_DEPTH_3  6
62 #define MAX_DEPTH_4  4
63 #define MAX_DEPTH_5  3
64
65 #define DBL_To_F_PT(x)  (F_PT)( (DBL)(UNIT)*(x) )
66
67 /*****************************************************/
68
69 static int  Max_Colors;
70 static ModeInfo *The_MI;
71 static F_PT Lx, Ly;
72 static int  D;
73 static Display *display;
74 static GC   gc;
75 static Window window;
76
77 /*****************************************************/
78
79 typedef struct Similitude_Struct SIMI;
80 typedef struct Fractal_Struct FRACTAL;
81
82 struct Similitude_Struct {
83
84         DBL         c_x, c_y;
85         DBL         r, r2, A, A2;
86         F_PT        Ct, St, Ct2, St2;
87         F_PT        Cx, Cy;
88         F_PT        R, R2;
89 };
90
91 struct Fractal_Struct {
92
93         int         Nb_Simi;
94         SIMI        Components[5 * MAX_SIMI];
95         int         Depth, Col;
96         int         Count, Speed;
97         int         Width, Height, Lx, Ly;
98         DBL         r_mean, dr_mean, dr2_mean;
99         int         Cur_Pt, Max_Pt;
100         XPoint     *Buffer1, *Buffer2;
101     Pixmap      dbuf;   /* jwz */
102     GC          dbuf_gc;
103 };
104
105 static FRACTAL *Root = NULL, *Cur_F;
106 static XPoint *Buf;
107 static int  Cur_Pt;
108
109
110 /*****************************************************/
111 /*****************************************************/
112
113 static      DBL
114 Gauss_Rand(DBL c, DBL A, DBL S)
115 {
116         DBL         y;
117
118         y = (DBL) LRAND() / MAXRAND;
119         y = A * (1.0 - exp(-y * y * S)) / (1.0 - exp(-S));
120         if (NRAND(2))
121                 return (c + y);
122         return (c - y);
123 }
124
125 static      DBL
126 Half_Gauss_Rand(DBL c, DBL A, DBL S)
127 {
128         DBL         y;
129
130         y = (DBL) LRAND() / MAXRAND;
131         y = A * (1.0 - exp(-y * y * S)) / (1.0 - exp(-S));
132         return (c + y);
133 }
134
135 static void
136 Random_Simis(FRACTAL * F, SIMI * Cur, int i)
137 {
138         while (i--) {
139                 Cur->c_x = Gauss_Rand(0.0, .8, 4.0);
140                 Cur->c_y = Gauss_Rand(0.0, .8, 4.0);
141                 Cur->r = Gauss_Rand(F->r_mean, F->dr_mean, 3.0);
142                 Cur->r2 = Half_Gauss_Rand(0.0, F->dr2_mean, 2.0);
143                 Cur->A = Gauss_Rand(0.0, 360.0, 4.0) * (M_PI / 180.0);
144                 Cur->A2 = Gauss_Rand(0.0, 360.0, 4.0) * (M_PI / 180.0);
145                 Cur++;
146         }
147 }
148
149 /***************************************************************/
150
151 void
152 init_ifs(ModeInfo * mi)
153 {
154         int         i;
155         FRACTAL    *Fractal;
156
157         if (Root == NULL) {
158                 Root = (FRACTAL *) calloc(
159                                        MI_NUM_SCREENS(mi), sizeof (FRACTAL));
160                 if (Root == NULL)
161                         return;
162         }
163         Fractal = &Root[MI_SCREEN(mi)];
164
165         if (Fractal->Max_Pt) {
166                 free(Fractal->Buffer1);
167                 free(Fractal->Buffer2);
168         }
169         i = (NRAND(4)) + 2;     /* Number of centers */
170         switch (i) {
171                 case 3:
172                         Fractal->Depth = MAX_DEPTH_3;
173                         Fractal->r_mean = .6;
174                         Fractal->dr_mean = .4;
175                         Fractal->dr2_mean = .3;
176                         break;
177
178                 case 4:
179                         Fractal->Depth = MAX_DEPTH_4;
180                         Fractal->r_mean = .5;
181                         Fractal->dr_mean = .4;
182                         Fractal->dr2_mean = .3;
183                         break;
184
185                 case 5:
186                         Fractal->Depth = MAX_DEPTH_5;
187                         Fractal->r_mean = .5;
188                         Fractal->dr_mean = .4;
189                         Fractal->dr2_mean = .3;
190                         break;
191
192                 default:
193                 case 2:
194                         Fractal->Depth = MAX_DEPTH_2;
195                         Fractal->r_mean = .7;
196                         Fractal->dr_mean = .3;
197                         Fractal->dr2_mean = .4;
198                         break;
199         }
200         /* (void) fprintf( stderr, "N=%d\n", i ); */
201         Fractal->Nb_Simi = i;
202         Fractal->Max_Pt = Fractal->Nb_Simi - 1;
203         for (i = 0; i <= Fractal->Depth + 2; ++i)
204                 Fractal->Max_Pt *= Fractal->Nb_Simi;
205
206         Fractal->Buffer1 = (XPoint *) calloc(Fractal->Max_Pt, sizeof (XPoint));
207         if (Fractal->Buffer1 == NULL)
208                 goto Abort;
209         Fractal->Buffer2 = (XPoint *) calloc(Fractal->Max_Pt, sizeof (XPoint));
210         if (Fractal->Buffer2 == NULL)
211                 goto Abort;
212
213         Fractal->Speed = 6;
214         Fractal->Width = MI_WIN_WIDTH(mi);
215         Fractal->Height = MI_WIN_HEIGHT(mi);
216         Fractal->Cur_Pt = 0;
217         Fractal->Count = 0;
218         Fractal->Lx = (Fractal->Width - 1) / 2;
219         Fractal->Ly = (Fractal->Height - 1) / 2;
220         Fractal->Col = NRAND(MI_NPIXELS(mi) - 1) + 1;
221
222         Random_Simis(Fractal, Fractal->Components, 5 * MAX_SIMI);
223
224         Fractal->dbuf = XCreatePixmap(MI_DISPLAY(mi), MI_WINDOW(mi),
225                                                                   Fractal->Width, Fractal->Height, 1);
226         if (Fractal->dbuf)
227           {
228                 XGCValues gcv;
229                 gcv.foreground = 0;
230                 gcv.background = 0;
231                 gcv.function = GXcopy;
232                 Fractal->dbuf_gc = XCreateGC(MI_DISPLAY(mi), Fractal->dbuf,
233                                                                          GCForeground|GCBackground|GCFunction,
234                                                                          &gcv);
235                 XFillRectangle(MI_DISPLAY(mi), Fractal->dbuf,
236                                            Fractal->dbuf_gc, 0,0, Fractal->Width, Fractal->Height);
237
238                 XSetBackground(MI_DISPLAY(mi), MI_GC(mi), MI_WIN_BLACK_PIXEL(mi));
239                 XSetFunction(MI_DISPLAY(mi), MI_GC(mi), GXcopy);
240           }
241
242         XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi));
243         return;
244
245       Abort:
246         if (Fractal->Buffer1 != NULL)
247                 free(Fractal->Buffer1);
248         if (Fractal->Buffer2 != NULL)
249                 free(Fractal->Buffer2);
250         Fractal->Buffer1 = NULL;
251         Fractal->Buffer2 = NULL;
252         Fractal->Max_Pt = 0;
253         return;
254 }
255
256
257 /***************************************************************/
258
259 #ifndef __GNUC__
260 # undef inline
261 # define inline /* */
262 #endif
263
264 static inline void
265 Transform(SIMI * Simi, F_PT xo, F_PT yo, F_PT * x, F_PT * y)
266 {
267         F_PT        xx, yy;
268
269         xo = xo - Simi->Cx;
270         xo = (xo * Simi->R) / UNIT;
271         yo = yo - Simi->Cy;
272         yo = (yo * Simi->R) / UNIT;
273
274         xx = xo - Simi->Cx;
275         xx = (xx * Simi->R2) / UNIT;
276         yy = -yo - Simi->Cy;
277         yy = (yy * Simi->R2) / UNIT;
278
279         *x = ((xo * Simi->Ct - yo * Simi->St + xx * Simi->Ct2 - yy * Simi->St2) / UNIT) + Simi->Cx;
280         *y = ((xo * Simi->St + yo * Simi->Ct + xx * Simi->St2 + yy * Simi->Ct2) / UNIT) + Simi->Cy;
281 }
282
283 /***************************************************************/
284
285 static void
286 Trace(F_PT xo, F_PT yo)
287 {
288         F_PT        x, y, i;
289         SIMI       *Cur;
290
291         Cur = Cur_F->Components;
292         for (i = Cur_F->Nb_Simi; i; --i, Cur++) {
293                 Transform(Cur, xo, yo, &x, &y);
294                 Buf->x = Lx + (x * Lx / (UNIT * 2));
295                 Buf->y = Ly - (y * Ly / (UNIT * 2));
296                 Buf++;
297                 Cur_Pt++;
298
299                 if (D && ((x - xo) >> 4) && ((y - yo) >> 4)) {
300                         D--;
301                         Trace(x, y);
302                         D++;
303                 }
304         }
305 }
306
307 static void
308 Draw_Fractal(FRACTAL * F)
309 {
310         int         i, j;
311         F_PT        x, y, xo, yo;
312         SIMI       *Cur, *Simi;
313
314         for (Cur = F->Components, i = F->Nb_Simi; i; --i, Cur++) {
315                 Cur->Cx = DBL_To_F_PT(Cur->c_x);
316                 Cur->Cy = DBL_To_F_PT(Cur->c_y);
317
318                 Cur->Ct = DBL_To_F_PT(cos(Cur->A));
319                 Cur->St = DBL_To_F_PT(sin(Cur->A));
320                 Cur->Ct2 = DBL_To_F_PT(cos(Cur->A2));
321                 Cur->St2 = DBL_To_F_PT(sin(Cur->A2));
322
323                 Cur->R = DBL_To_F_PT(Cur->r);
324                 Cur->R2 = DBL_To_F_PT(Cur->r2);
325         }
326
327
328         Cur_Pt = 0;
329         Cur_F = F;
330         Buf = F->Buffer2;
331         Lx = F->Lx;
332         Ly = F->Ly;
333         D = F->Depth;
334         for (Cur = F->Components, i = F->Nb_Simi; i; --i, Cur++) {
335                 xo = Cur->Cx;
336                 yo = Cur->Cy;
337                 for (Simi = F->Components, j = F->Nb_Simi; j; --j, Simi++) {
338                         if (Simi == Cur)
339                                 continue;
340                         Transform(Simi, xo, yo, &x, &y);
341                         Trace(x, y);
342                 }
343         }
344
345         /* Erase previous */
346
347         if (F->Cur_Pt) {
348                 XSetForeground(display, gc, MI_WIN_BLACK_PIXEL(The_MI));
349                 if (F->dbuf)    /* jwz */
350                   {
351                         XSetForeground(display, F->dbuf_gc, 0);
352 /*                  XDrawPoints(display, F->dbuf, F->dbuf_gc, F->Buffer1, F->Cur_Pt,
353                                             CoordModeOrigin); */
354                     XFillRectangle(display, F->dbuf, F->dbuf_gc, 0, 0,
355                                                    F->Width, F->Height);
356                   }
357                 else
358                   XDrawPoints(display, window, gc, F->Buffer1, F->Cur_Pt,
359                                           CoordModeOrigin);
360         }
361         if (Max_Colors < 2)
362                 XSetForeground(display, gc, MI_WIN_WHITE_PIXEL(The_MI));
363         else
364                 XSetForeground(display, gc, MI_PIXEL(The_MI, F->Col % Max_Colors));
365         if (Cur_Pt) {
366           if (F->dbuf)
367                 {
368                   XSetForeground(display, F->dbuf_gc, 1);
369                   XDrawPoints(display, F->dbuf, F->dbuf_gc, F->Buffer2, Cur_Pt,
370                                           CoordModeOrigin);
371                 }
372           else
373                 XDrawPoints(display, window, gc, F->Buffer2, Cur_Pt, CoordModeOrigin);
374         }
375
376         if (F->dbuf)
377           XCopyPlane(display, F->dbuf, window, gc, 0,0,F->Width,F->Height,0,0, 1);
378
379         F->Cur_Pt = Cur_Pt;
380         Buf = F->Buffer1;
381         F->Buffer1 = F->Buffer2;
382         F->Buffer2 = Buf;
383 }
384
385
386 void
387 draw_ifs(ModeInfo * mi)
388 {
389         int         i;
390         FRACTAL    *F;
391         DBL         u, uu, v, vv, u0, u1, u2, u3;
392         SIMI       *S, *S1, *S2, *S3, *S4;
393
394         The_MI = mi;
395         display = MI_DISPLAY(mi);
396         window = MI_WINDOW(mi);
397         gc = MI_GC(mi);
398         Max_Colors = MI_NPIXELS(mi);
399
400         F = &Root[MI_SCREEN(mi)];
401
402         u = (DBL) (F->Count) * (DBL) (F->Speed) / 1000.0;
403         uu = u * u;
404         v = 1.0 - u;
405         vv = v * v;
406         u0 = vv * v;
407         u1 = 3.0 * vv * u;
408         u2 = 3.0 * v * uu;
409         u3 = u * uu;
410
411         S = F->Components;
412         S1 = S + F->Nb_Simi;
413         S2 = S1 + F->Nb_Simi;
414         S3 = S2 + F->Nb_Simi;
415         S4 = S3 + F->Nb_Simi;
416
417         for (i = F->Nb_Simi; i; --i, S++, S1++, S2++, S3++, S4++) {
418                 S->c_x = u0 * S1->c_x + u1 * S2->c_x + u2 * S3->c_x + u3 * S4->c_x;
419                 S->c_y = u0 * S1->c_y + u1 * S2->c_y + u2 * S3->c_y + u3 * S4->c_y;
420                 S->r = u0 * S1->r + u1 * S2->r + u2 * S3->r + u3 * S4->r;
421                 S->r2 = u0 * S1->r2 + u1 * S2->r2 + u2 * S3->r2 + u3 * S4->r2;
422                 S->A = u0 * S1->A + u1 * S2->A + u2 * S3->A + u3 * S4->A;
423                 S->A2 = u0 * S1->A2 + u1 * S2->A2 + u2 * S3->A2 + u3 * S4->A2;
424         }
425
426         Draw_Fractal(F);
427
428         if (F->Count >= 1000 / F->Speed) {
429                 S = F->Components;
430                 S1 = S + F->Nb_Simi;
431                 S2 = S1 + F->Nb_Simi;
432                 S3 = S2 + F->Nb_Simi;
433                 S4 = S3 + F->Nb_Simi;
434
435                 for (i = F->Nb_Simi; i; --i, S++, S1++, S2++, S3++, S4++) {
436                         S2->c_x = 2.0 * S4->c_x - S3->c_x;
437                         S2->c_y = 2.0 * S4->c_y - S3->c_y;
438                         S2->r = 2.0 * S4->r - S3->r;
439                         S2->r2 = 2.0 * S4->r2 - S3->r2;
440                         S2->A = 2.0 * S4->A - S3->A;
441                         S2->A2 = 2.0 * S4->A2 - S3->A2;
442
443                         *S1 = *S4;
444                 }
445                 Random_Simis(F, F->Components + 3 * F->Nb_Simi, F->Nb_Simi);
446
447                 Random_Simis(F, F->Components + 4 * F->Nb_Simi, F->Nb_Simi);
448
449                 F->Count = 0;
450         } else
451                 F->Count++;
452
453         F->Col++;
454 }
455
456
457 /***************************************************************/
458
459 void
460 release_ifs(ModeInfo * mi)
461 {
462         int         i;
463
464         if (Root == NULL)
465                 return;
466
467         for (i = 0; i < MI_NUM_SCREENS(mi); ++i) {
468                 if (Root[i].Buffer1 != NULL)
469                         free(Root[i].Buffer1);
470                 if (Root[i].Buffer2 != NULL)
471                         free(Root[i].Buffer2);
472                 if (Root[i].dbuf)
473                         XFreePixmap(MI_DISPLAY(mi), Root[i].dbuf);
474                 if (Root[i].dbuf_gc)
475                         XFreeGC(MI_DISPLAY(mi), Root[i].dbuf_gc);
476         }
477         free(Root);
478         Root = NULL;
479 }