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, (caddr_t) "on"}, |
---|
62 | {"+mouse", ".julia.mouse", XrmoptionNoArg, (caddr_t) "off"}, |
---|
63 | }; |
---|
64 | static argtype vars[] = |
---|
65 | { |
---|
66 | {(caddr_t *) & 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 | } |
---|