ftp://ftp.linux.ncsu.edu/mirror/ftp.redhat.com/pub/redhat/linux/enterprise/4/en/os...
[xscreensaver] / hacks / julia.c
1 /* -*- Mode: C; tab-width: 4 -*-
2  * julia --- continuously varying Julia set.
3  */
4 #if 0
5 static const char sccsid[] = "@(#)julia.c       4.03 97/04/10 xlockmore";
6 #endif
7
8 /* Copyright (c) 1995 Sean McCullough <bankshot@mailhost.nmt.edu>.
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  * 28-May-97: jwz@jwz.org: added interactive frobbing with the mouse.
24  * 10-May-97: jwz@jwz.org: turned into a standalone program.
25  * 02-Dec-95: snagged boilerplate from hop.c
26  *           used ifs {w0 = sqrt(x-c), w1 = -sqrt(x-c)} with random iteration 
27  *           to plot the julia set, and sinusoidially varied parameter for set 
28  *           and plotted parameter with a circle.
29  */
30
31 /*-
32  * One thing to note is that batchcount is the *depth* of the search tree,
33  * so the number of points computed is 2^batchcount - 1.  I use 8 or 9
34  * on a dx266 and it looks okay.  The sinusoidal variation of the parameter
35  * might not be as interesting as it could, but it still gives an idea of
36  * the effect of the parameter.
37  */
38
39 #ifdef STANDALONE
40 # define PROGCLASS                                      "Julia"
41 # define HACK_INIT                                      init_julia
42 # define HACK_DRAW                                      draw_julia
43 # define julia_opts                                     xlockmore_opts
44 # define DEFAULTS       "*count:                1000  \n"                       \
45                                         "*cycles:               20    \n"                       \
46                                         "*delay:                10000 \n"                       \
47                                         "*ncolors:              200   \n"
48 # define UNIFORM_COLORS
49 # include "xlockmore.h"                         /* in xscreensaver distribution */
50 #else  /* !STANDALONE */
51 # include "xlock.h"                                     /* in xlockmore distribution */
52 #endif /* !STANDALONE */
53
54
55 static Bool track_p;
56
57 #define DEF_MOUSE "False"
58
59 static XrmOptionDescRec opts[] =
60 {
61         {"-mouse", ".julia.mouse", XrmoptionNoArg, "on"},
62         {"+mouse", ".julia.mouse", XrmoptionNoArg, "off"},
63 };
64 static argtype vars[] =
65 {
66         {&track_p, "mouse", "Mouse", DEF_MOUSE, t_Bool},
67 };
68 static OptionStruct desc[] =
69 {
70         {"-/+mouse", "turn on/off mouse tracking"},
71 };
72
73 ModeSpecOpt julia_opts = { 2, opts, 1, vars, desc };
74
75
76 #define numpoints ((0x2<<jp->depth)-1)
77
78 typedef struct {
79         int         centerx;
80         int         centery;    /* center of the screen */
81         double      cr;
82         double      ci;         /* julia params */
83         int         depth;
84         int         inc;
85         int         circsize;
86         int         erase;
87         int         pix;
88         long        itree;
89         int         buffer;
90         int         nbuffers;
91         int         redrawing, redrawpos;
92         Pixmap      pixmap;
93         Cursor      cursor;
94         GC          stippledGC;
95         XPoint    **pointBuffer;        /* pointer for XDrawPoints */
96
97 } juliastruct;
98
99 static juliastruct *julias = NULL;
100
101 /* How many segments to draw per cycle when redrawing */
102 #define REDRAWSTEP 3
103
104 static void
105 apply(juliastruct * jp, register double xr, register double xi, int d)
106 {
107         double      theta, r;
108
109         jp->pointBuffer[jp->buffer][jp->itree].x =
110                 (int) (0.5 * xr * jp->centerx + jp->centerx);
111         jp->pointBuffer[jp->buffer][jp->itree].y =
112                 (int) (0.5 * xi * jp->centery + jp->centery);
113         jp->itree++;
114
115         if (d > 0) {
116                 xi -= jp->ci;
117                 xr -= jp->cr;
118
119 /* Avoid atan2: DOMAIN error message */
120                 if (xi == 0.0 && xr == 0.0)
121                         theta = 0.0;
122                 else
123                         theta = atan2(xi, xr) / 2.0;
124
125                 /*r = pow(xi * xi + xr * xr, 0.25); */
126                 r = sqrt(sqrt(xi * xi + xr * xr));      /* 3 times faster */
127
128                 xr = r * cos(theta);
129                 xi = r * sin(theta);
130
131                 d--;
132                 apply(jp, xr, xi, d);
133                 apply(jp, -xr, -xi, d);
134         }
135 }
136
137 static void
138 incr(ModeInfo * mi, juliastruct * jp)
139 {
140         int cx, cy;
141
142         if (track_p)
143           {
144                 Window r, c;
145                 int rx, ry;
146                 unsigned int m;
147                 XQueryPointer(MI_DISPLAY(mi), MI_WINDOW(mi),
148                                           &r, &c, &rx, &ry, &cx, &cy, &m);
149                 if (cx <= 0 || cy <= 0 ||
150                         cx >= MI_WIN_WIDTH(mi) || cy >= MI_WIN_HEIGHT(mi))
151                   goto NOTRACK;
152           }
153
154         if (track_p)
155           {
156                 jp->cr = ((double) (cx + 2 - jp->centerx)) * 2 / jp->centerx;
157                 jp->ci = ((double) (cy + 2 - jp->centery)) * 2 / jp->centery;
158           }
159         else
160           {
161           NOTRACK:
162                 jp->cr = 1.5 * (sin(M_PI * (jp->inc / 300.0)) *
163                                                 sin(jp->inc * M_PI / 200.0));
164                 jp->ci = 1.5 * (cos(M_PI * (jp->inc / 300.0)) *
165                                                 cos(jp->inc * M_PI / 200.0));
166
167                 jp->cr += 0.5 * cos(M_PI * jp->inc / 400.0);
168                 jp->ci += 0.5 * sin(M_PI * jp->inc / 400.0);
169           }
170 }
171
172 void
173 init_julia(ModeInfo * mi)
174 {
175         Display    *display = MI_DISPLAY(mi);
176         Window      window = MI_WINDOW(mi);
177         juliastruct *jp;
178         XGCValues   gcv;
179         int         i;
180
181         if (julias == NULL) {
182                 if ((julias = (juliastruct *) calloc(MI_NUM_SCREENS(mi),
183                                               sizeof (juliastruct))) == NULL)
184                         return;
185         }
186         jp = &julias[MI_SCREEN(mi)];
187
188         jp->centerx = MI_WIN_WIDTH(mi) / 2;
189         jp->centery = MI_WIN_HEIGHT(mi) / 2;
190
191         jp->depth = MI_BATCHCOUNT(mi);
192         if (jp->depth > 10)
193                 jp->depth = 10;
194
195
196         if (track_p && !jp->cursor)
197           {
198                 Pixmap bit;
199                 XColor black;
200                 black.red = black.green = black.blue = 0;
201                 black.flags = DoRed|DoGreen|DoBlue;
202                 bit = XCreatePixmapFromBitmapData (display, window, "\000", 1, 1,
203                                                                                    MI_WIN_BLACK_PIXEL(mi),
204                                                                                    MI_WIN_BLACK_PIXEL(mi), 1);
205                 jp->cursor = XCreatePixmapCursor (display, bit, bit, &black, &black,
206                                                                                   0, 0);
207                 XFreePixmap (display, bit);
208           }
209
210         if (jp->pixmap != None &&
211             jp->circsize != (MIN(jp->centerx, jp->centery) / 60) * 2 + 1) {
212                 XFreePixmap(display, jp->pixmap);
213                 jp->pixmap = None;
214         }
215         if (jp->pixmap == None) {
216                 GC          fg_gc = None, bg_gc = None;
217
218                 jp->circsize = (MIN(jp->centerx, jp->centery) / 96) * 2 + 1;
219                 jp->pixmap = XCreatePixmap(display, window, jp->circsize, jp->circsize, 1);
220                 gcv.foreground = 1;
221                 fg_gc = XCreateGC(display, jp->pixmap, GCForeground, &gcv);
222                 gcv.foreground = 0;
223                 bg_gc = XCreateGC(display, jp->pixmap, GCForeground, &gcv);
224                 XFillRectangle(display, jp->pixmap, bg_gc,
225                                0, 0, jp->circsize, jp->circsize);
226                 if (jp->circsize < 2)
227                         XDrawPoint(display, jp->pixmap, fg_gc, 0, 0);
228                 else
229                         XFillArc(display, jp->pixmap, fg_gc,
230                                  0, 0, jp->circsize, jp->circsize, 0, 23040);
231                 if (fg_gc != None)
232                         XFreeGC(display, fg_gc);
233                 if (bg_gc != None)
234                         XFreeGC(display, bg_gc);
235         }
236
237         if (MI_WIN_IS_INROOT(mi))
238           ;
239         else if (jp->circsize > 0)
240           XDefineCursor (display, window, jp->cursor);
241         else
242           XUndefineCursor (display, window);
243
244         if (!jp->stippledGC) {
245                 gcv.foreground = MI_WIN_BLACK_PIXEL(mi);
246                 gcv.background = MI_WIN_BLACK_PIXEL(mi);
247                 if ((jp->stippledGC = XCreateGC(display, window,
248                                  GCForeground | GCBackground, &gcv)) == None)
249                         return;
250         }
251         if (MI_NPIXELS(mi) > 2)
252                 jp->pix = NRAND(MI_NPIXELS(mi));
253         jp->inc = ((LRAND() & 1) * 2 - 1) * NRAND(200);
254         jp->nbuffers = (MI_CYCLES(mi) + 1);
255         if (!jp->pointBuffer)
256                 jp->pointBuffer = (XPoint **) calloc(jp->nbuffers, sizeof (XPoint *));
257         for (i = 0; i < jp->nbuffers; ++i)
258                 if (jp->pointBuffer[i])
259                         (void) memset((char *) jp->pointBuffer[i], 0,
260                                       numpoints * sizeof (XPoint));
261                 else
262                         jp->pointBuffer[i] = (XPoint *) calloc(numpoints, sizeof (XPoint));
263         jp->buffer = 0;
264         jp->redrawing = 0;
265         jp->erase = 0;
266         XClearWindow(display, window);
267 }
268
269
270 /* hack: moved here by jwz. */
271 #define ERASE_IMAGE(d,w,g,x,y,xl,yl,xs,ys) \
272 if (yl<y) \
273 (y<yl+ys)?XFillRectangle(d,w,g,xl,yl,xs,y-yl): \
274 XFillRectangle(d,w,g,xl,yl,xs,ys); \
275 else if (yl>y) \
276 (y>yl-ys)?XFillRectangle(d,w,g,xl,y+ys,xs,yl-y): \
277 XFillRectangle(d,w,g,xl,yl,xs,ys); \
278 if (xl<x) \
279 (x<xl+xs)?XFillRectangle(d,w,g,xl,yl,x-xl,ys): \
280 XFillRectangle(d,w,g,xl,yl,xs,ys); \
281 else if (xl>x) \
282 (x>xl-xs)?XFillRectangle(d,w,g,x+xs,yl,xl-x,ys): \
283 XFillRectangle(d,w,g,xl,yl,xs,ys)
284
285
286 void
287 draw_julia(ModeInfo * mi)
288 {
289         Display    *display = MI_DISPLAY(mi);
290         Window      window = MI_WINDOW(mi);
291         GC          gc = MI_GC(mi);
292         juliastruct *jp = &julias[MI_SCREEN(mi)];
293         double      r, theta;
294         register double xr = 0.0, xi = 0.0;
295         int         k = 64, rnd = 0, i, j;
296         XPoint     *xp = jp->pointBuffer[jp->buffer], old_circle, new_circle;
297
298         old_circle.x = (int) (jp->centerx * jp->cr / 2) + jp->centerx - 2;
299         old_circle.y = (int) (jp->centery * jp->ci / 2) + jp->centery - 2;
300         incr(mi, jp);
301         new_circle.x = (int) (jp->centerx * jp->cr / 2) + jp->centerx - 2;
302         new_circle.y = (int) (jp->centery * jp->ci / 2) + jp->centery - 2;
303         XSetForeground(display, gc, MI_WIN_BLACK_PIXEL(mi));
304         ERASE_IMAGE(display, window, gc, new_circle.x, new_circle.y,
305                     old_circle.x, old_circle.y, jp->circsize, jp->circsize);
306         /* draw a circle at the c-parameter so you can see it's effect on the
307            structure of the julia set */
308         XSetTSOrigin(display, jp->stippledGC, new_circle.x, new_circle.y);
309         XSetForeground(display, jp->stippledGC, MI_WIN_WHITE_PIXEL(mi));
310         XSetStipple(display, jp->stippledGC, jp->pixmap);
311         XSetFillStyle(display, jp->stippledGC, FillOpaqueStippled);
312         XFillRectangle(display, window, jp->stippledGC, new_circle.x, new_circle.y,
313                        jp->circsize, jp->circsize);
314         XFlush(display);
315         if (jp->erase == 1) {
316                 XDrawPoints(display, window, gc,
317                     jp->pointBuffer[jp->buffer], numpoints, CoordModeOrigin);
318         }
319         jp->inc++;
320         if (MI_NPIXELS(mi) > 2) {
321                 XSetForeground(display, gc, MI_PIXEL(mi, jp->pix));
322                 if (++jp->pix >= MI_NPIXELS(mi))
323                         jp->pix = 0;
324         } else
325                 XSetForeground(display, gc, MI_WIN_WHITE_PIXEL(mi));
326         while (k--) {
327
328                 /* save calls to LRAND by using bit shifts over and over on the same
329                    int for 32 iterations, then get a new random int */
330                 if (!(k % 32))
331                         rnd = LRAND();
332
333                 /* complex sqrt: x^0.5 = radius^0.5*(cos(theta/2) + i*sin(theta/2)) */
334
335                 xi -= jp->ci;
336                 xr -= jp->cr;
337
338                 /* Avoid atan2: DOMAIN error message */
339                 if (xi == 0.0 && xr == 0.0)
340                         theta = 0.0;
341                 else
342                         theta = atan2(xi, xr) / 2.0;
343
344                 /*r = pow(xi * xi + xr * xr, 0.25); */
345                 r = sqrt(sqrt(xi * xi + xr * xr));      /* 3 times faster */
346
347                 xr = r * cos(theta);
348                 xi = r * sin(theta);
349
350                 if ((rnd >> (k % 32)) & 0x1) {
351                         xi = -xi;
352                         xr = -xr;
353                 }
354                 xp->x = jp->centerx + (int) ((jp->centerx >> 1) * xr);
355                 xp->y = jp->centery + (int) ((jp->centery >> 1) * xi);
356                 xp++;
357         }
358
359         jp->itree = 0;
360         apply(jp, xr, xi, jp->depth);
361
362         XDrawPoints(display, window, gc,
363                     jp->pointBuffer[jp->buffer], numpoints, CoordModeOrigin);
364
365         jp->buffer++;
366         if (jp->buffer > jp->nbuffers - 1) {
367                 jp->buffer -= jp->nbuffers;
368                 jp->erase = 1;
369         }
370         if (jp->redrawing) {
371                 for (i = 0; i < REDRAWSTEP; i++) {
372                         j = (jp->buffer - jp->redrawpos + jp->nbuffers) % jp->nbuffers;
373                         XDrawPoints(display, window, gc,
374                              jp->pointBuffer[j], numpoints, CoordModeOrigin);
375
376                         if (++(jp->redrawpos) >= jp->nbuffers) {
377                                 jp->redrawing = 0;
378                                 break;
379                         }
380                 }
381         }
382 }
383
384 void
385 release_julia(ModeInfo * mi)
386 {
387         if (julias != NULL) {
388                 int         screen;
389
390                 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
391                         Display    *display = MI_DISPLAY(mi);
392                         juliastruct *jp = &julias[screen];
393                         int         buffer;
394
395                         if (jp->pointBuffer) {
396                                 for (buffer = 0; buffer < jp->nbuffers; buffer++)
397                                         if (jp->pointBuffer[buffer])
398                                                 (void) free((void *) jp->pointBuffer[buffer]);
399                                 (void) free((void *) jp->pointBuffer);
400                         }
401                         if (jp->stippledGC != None)
402                                 XFreeGC(display, jp->stippledGC);
403                         if (jp->pixmap != None)
404                                 XFreePixmap(display, jp->pixmap);
405                         if (jp->cursor)
406                           XFreeCursor (display, jp->cursor);
407                 }
408                 (void) free((void *) julias);
409                 julias = NULL;
410         }
411 }
412
413 void
414 refresh_julia(ModeInfo * mi)
415 {
416         juliastruct *jp = &julias[MI_SCREEN(mi)];
417
418         jp->redrawing = 1;
419         jp->redrawpos = 0;
420 }