1 /* -*- Mode: C; tab-width: 4 -*-
2 * julia --- continuously varying Julia set.
5 static const char sccsid[] = "@(#)julia.c 4.03 97/04/10 xlockmore";
8 /* Copyright (c) 1995 Sean McCullough <bankshot@mailhost.nmt.edu>.
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.
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.
23 * 10-Jun-06: j.grahl@ucl.ac.uk: tweaked functions for parameter of Julia set
24 * 28-May-97: jwz@jwz.org: added interactive frobbing with the mouse.
25 * 10-May-97: jwz@jwz.org: turned into a standalone program.
26 * 02-Dec-95: snagged boilerplate from hop.c
27 * used ifs {w0 = sqrt(x-c), w1 = -sqrt(x-c)} with random iteration
28 * to plot the julia set, and sinusoidially varied parameter for set
29 * and plotted parameter with a circle.
33 * One thing to note is that batchcount is the *depth* of the search tree,
34 * so the number of points computed is 2^batchcount - 1. I use 8 or 9
35 * on a dx266 and it looks okay. The sinusoidal variation of the parameter
36 * might not be as interesting as it could, but it still gives an idea of
37 * the effect of the parameter.
41 # define DEFAULTS "*count: 1000 \n" \
45 "*fpsSolid: true \n" \
46 "*ignoreRotation: True \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 */
55 #define DEF_MOUSE "False"
57 ENTRYPOINT ModeSpecOpt julia_opts = { 0, };
60 #define numpoints ((0x2<<jp->depth)-1)
64 int centery; /* center of the screen */
66 double ci; /* julia params */
75 int redrawing, redrawpos;
81 XPoint **pointBuffer; /* pointer for XDrawPoints */
87 static juliastruct *julias = NULL;
89 /* How many segments to draw per cycle when redrawing */
93 apply(juliastruct * jp, register double xr, register double xi, int d)
97 jp->pointBuffer[jp->buffer][jp->itree].x =
98 (int) (0.5 * xr * jp->centerx + jp->centerx);
99 jp->pointBuffer[jp->buffer][jp->itree].y =
100 (int) (0.5 * xi * jp->centery + jp->centery);
107 /* Avoid atan2: DOMAIN error message */
108 if (xi == 0.0 && xr == 0.0)
111 theta = atan2(xi, xr) / 2.0;
113 /*r = pow(xi * xi + xr * xr, 0.25); */
114 r = sqrt(sqrt(xi * xi + xr * xr)); /* 3 times faster */
120 apply(jp, xr, xi, d);
121 apply(jp, -xr, -xi, d);
126 incr(ModeInfo * mi, juliastruct * jp)
128 if (jp->button_down_p)
130 jp->cr = ((double) (jp->mouse_x + 2 - jp->centerx)) * 2 / jp->centerx;
131 jp->ci = ((double) (jp->mouse_y + 2 - jp->centery)) * 2 / jp->centery;
136 jp->cr = 1.5 * (sin(M_PI * (jp->inc / 300.0)) *
137 sin(jp->inc * M_PI / 200.0));
138 jp->ci = 1.5 * (cos(M_PI * (jp->inc / 300.0)) *
139 cos(jp->inc * M_PI / 200.0));
141 jp->cr += 0.5 * cos(M_PI * jp->inc / 400.0);
142 jp->ci += 0.5 * sin(M_PI * jp->inc / 400.0);
144 jp->cr = 1.5 * (sin(M_PI * (jp->inc / 290.0)) *
145 sin(jp->inc * M_PI / 210.0));
146 jp->ci = 1.5 * (cos(M_PI * (jp->inc / 310.0)) *
147 cos(jp->inc * M_PI / 190.0));
149 jp->cr += 0.5 * cos(M_PI * jp->inc / 395.0);
150 jp->ci += 0.5 * sin(M_PI * jp->inc / 410.0);
156 init_julia(ModeInfo * mi)
158 Display *display = MI_DISPLAY(mi);
159 Window window = MI_WINDOW(mi);
164 if (julias == NULL) {
165 if ((julias = (juliastruct *) calloc(MI_NUM_SCREENS(mi),
166 sizeof (juliastruct))) == NULL)
169 jp = &julias[MI_SCREEN(mi)];
171 jp->centerx = MI_WIN_WIDTH(mi) / 2;
172 jp->centery = MI_WIN_HEIGHT(mi) / 2;
174 jp->depth = MI_BATCHCOUNT(mi);
180 if (jp->button_down_p && !jp->cursor && !jp->cursor)
184 black.red = black.green = black.blue = 0;
185 black.flags = DoRed|DoGreen|DoBlue;
186 bit = XCreatePixmapFromBitmapData (display, window, "\000", 1, 1,
187 MI_WIN_BLACK_PIXEL(mi),
188 MI_WIN_BLACK_PIXEL(mi), 1);
189 jp->cursor = XCreatePixmapCursor (display, bit, bit, &black, &black,
191 XFreePixmap (display, bit);
193 #endif /* HAVE_COCOA */
195 if (jp->pixmap != None &&
196 jp->circsize != (MIN(jp->centerx, jp->centery) / 60) * 2 + 1) {
197 XFreePixmap(display, jp->pixmap);
200 if (jp->pixmap == None) {
201 GC fg_gc = None, bg_gc = None;
203 jp->circsize = MAX(8, (MIN(jp->centerx, jp->centery) / 96) * 2 + 1);
204 jp->pixmap = XCreatePixmap(display, window, jp->circsize, jp->circsize, 1);
206 fg_gc = XCreateGC(display, jp->pixmap, GCForeground, &gcv);
208 bg_gc = XCreateGC(display, jp->pixmap, GCForeground, &gcv);
209 XFillRectangle(display, jp->pixmap, bg_gc,
210 0, 0, jp->circsize, jp->circsize);
211 if (jp->circsize < 2)
212 XDrawPoint(display, jp->pixmap, fg_gc, 0, 0);
214 XFillArc(display, jp->pixmap, fg_gc,
215 0, 0, jp->circsize, jp->circsize, 0, 23040);
217 XFreeGC(display, fg_gc);
219 XFreeGC(display, bg_gc);
223 if (MI_WIN_IS_INROOT(mi))
225 else if (jp->circsize > 0)
226 XDefineCursor (display, window, jp->cursor);
228 XUndefineCursor (display, window);
229 #endif /* HAVE_COCOA */
231 if (!jp->stippledGC) {
232 gcv.foreground = MI_WIN_BLACK_PIXEL(mi);
233 gcv.background = MI_WIN_BLACK_PIXEL(mi);
234 if ((jp->stippledGC = XCreateGC(display, window,
235 GCForeground | GCBackground, &gcv)) == None)
238 if (MI_NPIXELS(mi) > 2)
239 jp->pix = NRAND(MI_NPIXELS(mi));
240 jp->inc = ((LRAND() & 1) * 2 - 1) * NRAND(200);
241 jp->nbuffers = (MI_CYCLES(mi) + 1);
242 if (!jp->pointBuffer)
243 jp->pointBuffer = (XPoint **) calloc(jp->nbuffers, sizeof (XPoint *));
244 for (i = 0; i < jp->nbuffers; ++i)
245 if (jp->pointBuffer[i])
246 (void) memset((char *) jp->pointBuffer[i], 0,
247 numpoints * sizeof (XPoint));
249 jp->pointBuffer[i] = (XPoint *) calloc(numpoints, sizeof (XPoint));
253 XClearWindow(display, window);
258 reshape_julia (ModeInfo *mi, int w, int h)
265 julia_handle_event (ModeInfo *mi, XEvent *event)
267 juliastruct *jp = &julias[MI_SCREEN(mi)];
269 if (event->xany.type == ButtonPress &&
270 event->xbutton.button == Button1)
272 jp->button_down_p = True;
273 jp->mouse_x = event->xbutton.x;
274 jp->mouse_y = event->xbutton.y;
277 else if (event->xany.type == ButtonRelease &&
278 event->xbutton.button == Button1)
280 jp->button_down_p = False;
283 else if (event->xany.type == MotionNotify && jp->button_down_p)
285 jp->mouse_x = event->xmotion.x;
286 jp->mouse_y = event->xmotion.y;
295 /* hack: moved here by jwz. */
296 #define ERASE_IMAGE(d,w,g,x,y,xl,yl,xs,ys) \
298 (y<yl+ys)?XFillRectangle(d,w,g,xl,yl,xs,y-yl): \
299 XFillRectangle(d,w,g,xl,yl,xs,ys); \
301 (y>yl-ys)?XFillRectangle(d,w,g,xl,y+ys,xs,yl-y): \
302 XFillRectangle(d,w,g,xl,yl,xs,ys); \
304 (x<xl+xs)?XFillRectangle(d,w,g,xl,yl,x-xl,ys): \
305 XFillRectangle(d,w,g,xl,yl,xs,ys); \
307 (x>xl-xs)?XFillRectangle(d,w,g,x+xs,yl,xl-x,ys): \
308 XFillRectangle(d,w,g,xl,yl,xs,ys)
312 draw_julia (ModeInfo * mi)
314 Display *display = MI_DISPLAY(mi);
315 Window window = MI_WINDOW(mi);
317 juliastruct *jp = &julias[MI_SCREEN(mi)];
319 register double xr = 0.0, xi = 0.0;
320 int k = 64, rnd = 0, i, j;
321 XPoint *xp = jp->pointBuffer[jp->buffer], old_circle, new_circle;
323 old_circle.x = (int) (jp->centerx * jp->cr / 2) + jp->centerx - 2;
324 old_circle.y = (int) (jp->centery * jp->ci / 2) + jp->centery - 2;
326 new_circle.x = (int) (jp->centerx * jp->cr / 2) + jp->centerx - 2;
327 new_circle.y = (int) (jp->centery * jp->ci / 2) + jp->centery - 2;
328 XSetForeground(display, gc, MI_WIN_BLACK_PIXEL(mi));
329 XFillArc(display, window, gc,
330 old_circle.x-jp->circsize/2-2,
331 old_circle.y-jp->circsize/2-2,
332 jp->circsize+4, jp->circsize+4,
334 /* draw a circle at the c-parameter so you can see it's effect on the
335 structure of the julia set */
336 XSetForeground(display, jp->stippledGC, MI_WIN_WHITE_PIXEL(mi));
338 XSetTSOrigin(display, jp->stippledGC, new_circle.x, new_circle.y);
339 XSetStipple(display, jp->stippledGC, jp->pixmap);
340 XSetFillStyle(display, jp->stippledGC, FillOpaqueStippled);
341 #endif /* HAVE_COCOA */
342 XDrawArc(display, window, jp->stippledGC,
343 new_circle.x-jp->circsize/2,
344 new_circle.y-jp->circsize/2,
345 jp->circsize, jp->circsize,
348 if (jp->erase == 1) {
349 XDrawPoints(display, window, gc,
350 jp->pointBuffer[jp->buffer], numpoints, CoordModeOrigin);
353 if (MI_NPIXELS(mi) > 2) {
354 XSetForeground(display, gc, MI_PIXEL(mi, jp->pix));
355 if (++jp->pix >= MI_NPIXELS(mi))
358 XSetForeground(display, gc, MI_WIN_WHITE_PIXEL(mi));
361 /* save calls to LRAND by using bit shifts over and over on the same
362 int for 32 iterations, then get a new random int */
366 /* complex sqrt: x^0.5 = radius^0.5*(cos(theta/2) + i*sin(theta/2)) */
371 /* Avoid atan2: DOMAIN error message */
372 if (xi == 0.0 && xr == 0.0)
375 theta = atan2(xi, xr) / 2.0;
377 /*r = pow(xi * xi + xr * xr, 0.25); */
378 r = sqrt(sqrt(xi * xi + xr * xr)); /* 3 times faster */
383 if ((rnd >> (k % 32)) & 0x1) {
387 xp->x = jp->centerx + (int) ((jp->centerx >> 1) * xr);
388 xp->y = jp->centery + (int) ((jp->centery >> 1) * xi);
393 apply(jp, xr, xi, jp->depth);
395 XDrawPoints(display, window, gc,
396 jp->pointBuffer[jp->buffer], numpoints, CoordModeOrigin);
399 if (jp->buffer > jp->nbuffers - 1) {
400 jp->buffer -= jp->nbuffers;
404 for (i = 0; i < REDRAWSTEP; i++) {
405 j = (jp->buffer - jp->redrawpos + jp->nbuffers) % jp->nbuffers;
406 XDrawPoints(display, window, gc,
407 jp->pointBuffer[j], numpoints, CoordModeOrigin);
409 if (++(jp->redrawpos) >= jp->nbuffers) {
418 release_julia (ModeInfo * mi)
420 if (julias != NULL) {
423 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
424 Display *display = MI_DISPLAY(mi);
425 juliastruct *jp = &julias[screen];
428 if (jp->pointBuffer) {
429 for (buffer = 0; buffer < jp->nbuffers; buffer++)
430 if (jp->pointBuffer[buffer])
431 (void) free((void *) jp->pointBuffer[buffer]);
432 (void) free((void *) jp->pointBuffer);
434 if (jp->stippledGC != None)
435 XFreeGC(display, jp->stippledGC);
436 if (jp->pixmap != None)
437 XFreePixmap(display, jp->pixmap);
440 XFreeCursor (display, jp->cursor);
443 (void) free((void *) julias);
449 refresh_julia (ModeInfo * mi)
451 juliastruct *jp = &julias[MI_SCREEN(mi)];
457 XSCREENSAVER_MODULE ("Julia", julia)