/* -*- Mode: C; tab-width: 4; c-basic-offset: 4 -*- */ /* mandelbrot --- animated mandelbrot sets */ #if 0 static const char sccsid[] = "@(#)mandelbrot.c 5.09 2003/06/30 xlockmore"; #endif #define USE_LOG /*- * Copyright (c) 1997 Dan Stromberg * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appear in all copies and that * both that copyright notice and this permission notice appear in * supporting documentation. * * This file is provided AS IS with no warranties of any kind. The author * shall have no liability with respect to the infringement of copyrights, * trade secrets or any patents by this file or any part thereof. In no * event will the author be liable for any lost revenue or profits or * other special, indirect and consequential damages. * * See A.K. Dewdney's "Computer Recreations", Scientific American * Magazine" Aug 1985 for more info. Also A.K. Dewdney's "Computer * Recreations", Scientific American Magazine" Jul 1989, has some neat * extensions besides z^n + c (n small but >= 2) some of these are: * z^z + z^n + c <-- pow * sin(z) + z^n + c <-- sin * sin(z) + e^z + c * These were first explored by a colleague of Mandelbrot, Clifford A. * Pickover. These would make nice additions to add. * * Revision History: * 03-Jul-2003: Added pow and sin options * 30-Jun-2003: Changed writeable mode to be more consistent with * xscreensaver's starfish * 01-Nov-2000: Allocation checks * 24-Mar-1999: DEM and Binary decomp options added by Tim Auckland * . Ideas from Peitgen & Saupe's * "The Science of Fractal Images" * 17-Nov-1998: Many Changes by Stromberg, including selection of * interesting subregions, more extreme color ranges, * reduction of possible powers to smaller/more interesting * range, elimination of some unused code, slower color cycling, * longer period of color cycling after the drawing is complete. * Hopefully the longer color cycling period will make the mode * reasonable even after CPUs are so fast that the drawing * interval goes by quickly * 20-Oct-1997: Written by Dan Stromberg */ #ifdef STANDALONE #define MODE_mandelbrot #define DEFAULTS "*delay: 25000 \n" \ "*count: -8 \n" \ "*cycles: 20000 \n" \ "*ncolors: 200 \n" \ # define free_mandelbrot 0 # define reshape_mandelbrot 0 # define mandelbrot_handle_event 0 #define SMOOTH_COLORS #define WRITABLE_COLORS #include "xlockmore.h" /* from the xscreensaver distribution */ #else /* !STANDALONE */ #include "xlock.h" /* from the xlockmore distribution */ #include "color.h" #endif /* !STANDALONE */ #ifdef MODE_mandelbrot #define MINPOWER 2 #define DEF_INCREMENT "1.00" #define DEF_BINARY "False" #define DEF_DEM "False" #define DEF_LYAP "False" #define DEF_ALPHA "False" #define DEF_INDEX "False" #define DEF_POW "False" #define DEF_SIN "False" #define DEF_CYCLE "True" /* 4.0 is best for seeing if a point is inside the set, 13 is better if ** you want to get a pretty corona */ #define ESCAPE 13.0 #define FLOATRAND(min,max) ((min)+((double) LRAND()/((double) MAXRAND))*((max)-(min))) typedef enum { NONE, LYAPUNOV, ALPHA, INDEX, interior_size } interior_t; /* incr also would be nice as a parameter. It controls how fast the order is advanced. Non-integral values are not true orders, but it's a somewhat interesting function anyway */ static float increment; static Bool binary_p; static Bool dem_p; static Bool lyap_p; static Bool alpha_p; static Bool index_p; static Bool pow_p; static Bool sin_p; static Bool cycle_p; static XrmOptionDescRec opts[] = { {(char *) "-increment", (char *) ".mandelbrot.increment", XrmoptionSepArg, (caddr_t) NULL}, {(char *) "-binary", (char *) ".mandelbrot.binary", XrmoptionNoArg, (caddr_t) "on"}, {(char *) "+binary", (char *) ".mandelbrot.binary", XrmoptionNoArg, (caddr_t) "off"}, {(char *) "-dem", (char *) ".mandelbrot.dem", XrmoptionNoArg, (caddr_t) "on"}, {(char *) "+dem", (char *) ".mandelbrot.dem", XrmoptionNoArg, (caddr_t) "off"}, {(char *) "-lyap", (char *) ".mandelbrot.lyap", XrmoptionNoArg, (caddr_t) "on"}, {(char *) "+lyap", (char *) ".mandelbrot.lyap", XrmoptionNoArg, (caddr_t) "off"}, {(char *) "-alpha", (char *) ".mandelbrot.alpha", XrmoptionNoArg, (caddr_t) "on"}, {(char *) "+alpha", (char *) ".mandelbrot.alpha", XrmoptionNoArg, (caddr_t) "off"}, {(char *) "-index", (char *) ".mandelbrot.index", XrmoptionNoArg, (caddr_t) "on"}, {(char *) "+index", (char *) ".mandelbrot.index", XrmoptionNoArg, (caddr_t) "off"}, {(char *) "-pow", (char *) ".mandelbrot.pow", XrmoptionNoArg, (caddr_t) "on"}, {(char *) "+pow", (char *) ".mandelbrot.pow", XrmoptionNoArg, (caddr_t) "off"}, {(char *) "-sin", (char *) ".mandelbrot.sin", XrmoptionNoArg, (caddr_t) "on"}, {(char *) "+sin", (char *) ".mandelbrot.sin", XrmoptionNoArg, (caddr_t) "off"}, {(char *) "-cycle", (char *) ".mandelbrot.cycle", XrmoptionNoArg, (caddr_t) "on"}, {(char *) "+cycle", (char *) ".mandelbrot.cycle", XrmoptionNoArg, (caddr_t) "off"} }; static argtype vars[] = { {(void *) & increment, (char *) "increment", (char *) "Increment", (char *) DEF_INCREMENT, t_Float}, {(void *) & binary_p, (char *) "binary", (char *) "Binary", (char *) DEF_BINARY, t_Bool}, {(void *) & dem_p, (char *) "dem", (char *) "Dem", (char *) DEF_DEM, t_Bool}, {(void *) & lyap_p, (char *) "lyap", (char *) "Lyap", (char *) DEF_LYAP, t_Bool}, {(void *) & alpha_p, (char *) "alpha", (char *) "Alpha", (char *) DEF_ALPHA, t_Bool}, {(void *) & index_p, (char *) "index", (char *) "Index", (char *) DEF_INDEX, t_Bool}, {(void *) & pow_p, (char *) "pow", (char *) "Pow", (char *) DEF_POW, t_Bool}, {(void *) & sin_p, (char *) "sin", (char *) "Sin", (char *) DEF_SIN, t_Bool}, {(void *) & cycle_p, (char *) "cycle", (char *) "Cycle", (char *) DEF_CYCLE, t_Bool} }; static OptionStruct desc[] = { {(char *) "-increment value", (char *) "increasing orders"}, {(char *) "-/+binary", (char *) "turn on/off Binary Decomposition colour modulation"}, {(char *) "-/+dem", (char *) "turn on/off Distance Estimator Method (instead of escape time)"}, {(char *) "-/+lyap", (char *) "render interior with Lyapunov measure"}, {(char *) "-/+alpha", (char *) "render interior with Alpha level sets"}, {(char *) "-/+index", (char *) "render interior with Alpha indexes"}, {(char *) "-/+pow", (char *) "turn on/off adding z^z"}, {(char *) "-/+sin", (char *) "turn on/off adding sin(z)"}, {(char *) "-/+cycle", (char *) "turn on/off colour cycling"} }; ENTRYPOINT ModeSpecOpt mandelbrot_opts = {sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc}; #ifdef USE_MODULES ModStruct mandelbrot_description = {"mandelbrot", "init_mandelbrot", "draw_mandelbrot", "release_mandelbrot", (char *) NULL, "init_mandelbrot", (char *) NULL, &mandelbrot_opts, 25000, -8, 20000, 1, 64, 1.0, "", "Shows mandelbrot sets", 0, NULL}; #endif #define ROUND_FLOAT(x,a) ((float) ((int) ((x) / (a) + 0.5)) * (a)) typedef struct { double real, imag; } complex; static void add(complex * a, complex b) { a->real = a->real + b.real; a->imag = a->imag + b.imag; } static void mult(complex * a, complex b) { double tr, ti; /* a.real*b.real + i*a.real*b.imag + i*a.imag*b.real + i^2*a.imag*b.imag */ tr = a->real * b.real - a->imag * b.imag; ti = a->real * b.imag + a->imag * b.real; a->real = tr; a->imag = ti; } static void cln(complex * a) { /* ln(z) = ln ((x^2 + y^2)^(1/2)) + i * invtan(y/x) */ double tr, ti; tr = sqrt(a->real * a->real + a->imag * a->imag); ti = (a->real == 0.0 && a->imag == 0.0) ? 0.0 : atan2(a->imag, a->real); a->real = tr; a->imag = ti; } static void complex_exp(complex * a) { /* e^z = e^x * (cos(y) + i * sin(y)) */ double tr; tr = exp(a->real); a->real = tr * cos(a->imag); a->imag = tr * sin(a->imag); } static complex complex_pow(complex a, complex b) { /* a^z = e^(z * ln(a)) */ complex c, d; c = a; d = b; cln(&c); mult(&d, c); complex_exp(&d); return d; } static complex complex_sin(complex a) { /* sin(z) = -i * (e^(i * z) - e^(-i * z)) / 2 */ /* cos(z) = (e^(i * z) - e^(-i * z)) / 2 */ complex c, d, i; c = a; d = a; i.real = 0; i.imag = 1; /* inefficient but I just want to see the picture */ mult(&c, i); i.imag = -i.imag; mult(&d, i); complex_exp(&c); complex_exp(&d); d.real = -d.real; d.imag = -d.imag; add(&c, d); c.real = c.real / 2; c.imag = c.imag / 2; mult(&c, i); return c; } /* this is a true power function. */ static void ipow(complex * a, int n) { switch (n) { case 1: return; case 2: mult(a, *a); return; default: { complex a2; int t2; a2 = *a; /* Not very efficient to use: mult(a, ipow(&a2, n-1)); */ t2 = n / 2; ipow(&a2, t2); mult(&a2, a2); if (t2 * 2 != n) /* if n is odd */ mult(&a2, *a); *a = a2; } } } typedef struct { int counter; double power; int column; Bool backwards; int ncolors; unsigned int cur_color; GC gc; Colormap cmap; unsigned long blackpixel, whitepixel, fg, bg; int direction; XColor *colors; complex extreme_ul; complex extreme_lr; complex ul; complex lr; int screen_width; int screen_height; int reptop; Bool dem, pow, sin, cycle_p, mono_p, no_colors; Bool binary; interior_t interior; } mandelstruct; static mandelstruct *mandels = (mandelstruct *) NULL; /* do the iterations * if binary is true, check halfplane of last iteration. * if demrange is non zero, estimate lower bound of dist(c, M) * * DEM - Distance Estimator Method * Based on Peitgen & Saupe's "The Science of Fractal Images" * * ALPHA - level sets of closest return. * INDEX - index of ALPHA. * Based on Peitgen & Richter's "The Beauty of Fractals" (Fig 33,34) * * LYAPUNOV - lyapunov exponent estimate * Based on an idea by Jan Thor * */ static int reps(complex c, double p, int r, Bool binary, interior_t interior, double demrange, Bool zpow, Bool zsin) { int rep; int escaped = 0; complex t; int escape = (int) ((demrange == 0) ? ESCAPE : ESCAPE*ESCAPE*ESCAPE*ESCAPE); /* 2 more iterations */ complex t1; complex dt; double L = 0.0; double l2; double dl2 = 1.0; double alpha2 = ESCAPE; int index = 0; #if defined(USE_LOG) double log_top = log((double) r); #endif t = c; dt.real = 1; dt.imag = 0; for (rep = 0; rep < r; rep++) { t1 = t; ipow(&t, (int) p); add(&t, c); if (zpow) add(&t, complex_pow(t1, t1)); if (zsin) add(&t, complex_sin(t1)); l2 = t.real * t.real + t.imag * t.imag; if (l2 <= alpha2) { alpha2 = l2; index = rep; } if (l2 >= escape) { escaped = 1; break; } else if (interior == LYAPUNOV) { /* Crude estimate of Lyapunov exponent. The stronger the attractor, the more negative the exponent will be. */ /* n=N L = lim 1/N * Sum log(abs(dx(n+1)/dx(n)))/ln(2) N->inf n=1 */ L += log(sqrt(l2)); } if (demrange){ /* compute dt/dc * p-1 * dt = p * t * dt + 1 * k+1 k k */ /* Note this is incorrect for zpow or zsin, but a correct implementation is too slow to be useful. */ dt.real *= p; dt.imag *= p; if(p > 2) ipow(&t1, (int) (p - 1)); mult(&dt, t1); dt.real += 1; dl2 = dt.real * dt.real + dt.imag * dt.imag; if (dl2 >= 1e300) { escaped = 2; break; } } } if (escaped) { if(demrange) { double mt = sqrt(t1.real * t1.real + t1.imag * t1.imag); /* distance estimate */ double dist = 0.5 * mt * log(mt) / sqrt(dl2); /* scale for viewing. Allow black when showing interior. */ rep = (int) (((interior > NONE)?0:1) + 10*r*dist/demrange); if(rep > r-1) rep = r-1; /* chop into color range */ } if(binary && t.imag > 0) rep = (r + rep / 2) % r; /* binary decomp */ #ifdef USE_LOG if ( rep > 0 ) rep = (int) (r * log((double) rep)/log_top); /* Log Scale */ #endif return rep; } else if (interior == LYAPUNOV) { return -(int)(L/M_LN2) % r; } else if (interior == INDEX) { return 1 + index; } else if (interior == ALPHA) { return (int) (r * sqrt(alpha2)); } else return r; } static void Select(/* input variables first */ complex *extreme_ul, complex *extreme_lr, int width, int height, int power, int top, Bool zpow, Bool zsin, /* output variables follow */ complex *selected_ul,complex *selected_lr) { double precision; double s2; int inside; int uninteresting; int found; int tries; found = 0; while (!found) { /* select a precision - be careful with this */ precision = pow(2.0,FLOATRAND(-9.0,-18.0)); /* (void) printf("precision is %f\n",precision); */ for (tries=0;tries<10000&&!found;tries++) { /* it eventually turned out that this inner loop doesn't always ** terminate - so we just try 10000 times, and if we don't get ** anything interesting, we pick a new precision */ complex temp; int sample_step = 4; int row,column; inside = 0; uninteresting = 0; /* pick a random point in the allowable range */ temp.real = FLOATRAND(extreme_ul->real,extreme_lr->real); temp.imag = FLOATRAND(extreme_ul->imag,extreme_lr->imag); /* find upper left and lower right points */ selected_ul->real = temp.real - precision * width / 2; selected_lr->real = temp.real + precision * width / 2; selected_ul->imag = temp.imag - precision * height / 2; selected_lr->imag = temp.imag + precision * height / 2; /* sample the results we'd get from this choice, accept or reject ** accordingly */ for (row=0; rowimag + (selected_ul->imag - selected_lr->imag) * (((double)row)/sample_step); temp.real = selected_ul->real + (selected_ul->real - selected_lr->real) * (((double)column)/sample_step); r = reps(temp,(double) power,top,0,(interior_t) 0,0.0, zpow, zsin); /* Here, we just want to see if the point is in the set, ** not if we can make something pretty */ if (r == top) { inside++; } if (r < 2) { uninteresting++; } } } s2 = sample_step*sample_step; /* more than 10 percent, but less than 60 percent inside the set */ if (inside >= ceil(s2/10.0) && inside <= s2*6.0/10.0 && uninteresting <= s2/10.0) { /* this one looks interesting */ found = 1; } /* else *** this does not look like a real good combination, so back *** up to the top of the loop to try another possibility */ } } } static void free_mandelbrot_screen(ModeInfo *mi, mandelstruct *mp) { Display *display = MI_DISPLAY(mi); if (mp == NULL) { return; } if (MI_IS_INSTALL(mi) && MI_NPIXELS(mi) > 2) { MI_WHITE_PIXEL(mi) = mp->whitepixel; MI_BLACK_PIXEL(mi) = mp->blackpixel; #ifndef STANDALONE MI_FG_PIXEL(mi) = mp->fg; MI_BG_PIXEL(mi) = mp->bg; #endif if (mp->colors != NULL) { if (mp->ncolors && !mp->no_colors) free_colors( #ifdef STANDALONE MI_SCREENPTR(mi), #else display, #endif mp->cmap, mp->colors, mp->ncolors); free(mp->colors); mp->colors = (XColor *) NULL; } if (mp->cmap != None) { XFreeColormap(display, mp->cmap); mp->cmap = None; } } if (mp->gc != None) { XFreeGC(display, mp->gc); mp->gc = None; } mp = NULL; } #ifndef STANDALONE extern char *background; extern char *foreground; #endif ENTRYPOINT void init_mandelbrot(ModeInfo * mi) { Display *display = MI_DISPLAY(mi); Window window = MI_WINDOW(mi); mandelstruct *mp; MI_INIT(mi, mandels); mp = &mandels[MI_SCREEN(mi)]; mp->screen_width = MI_WIDTH(mi); mp->screen_height = MI_HEIGHT(mi); mp->backwards = (Bool) (LRAND() & 1); if (mp->backwards) mp->column = mp->screen_width - 1; else mp->column = 0; mp->power = NRAND(3) + MINPOWER; mp->counter = 0; MI_CLEARWINDOW(mi); if (MI_IS_FULLRANDOM(mi)) { mp->binary = (Bool) (LRAND() & 1); mp->dem = (Bool) (LRAND() & 1); mp->interior = (interior_t) NRAND(interior_size); #if 0 /* too slow */ mp->pow = (NRAND(5) == 0); mp->sin = (NRAND(5) == 0); #endif } else { mp->binary = binary_p; mp->dem = dem_p; if (index_p) { mp->interior = INDEX; } else if(alpha_p) { mp->interior = ALPHA; } else if(lyap_p) { mp->interior = LYAPUNOV; } else { mp->interior = NONE; } mp->pow = pow_p; mp->sin = sin_p; } mp->reptop = 300; /* these could be tuned a little closer, but the selection ** process throws out the chaf anyway, it just takes slightly ** longer */ mp->extreme_ul.real = -3.0; mp->extreme_ul.imag = -3.0; mp->extreme_lr.real = 3.0; mp->extreme_lr.imag = 3.0; if (!mp->gc) { if (MI_IS_INSTALL(mi) && MI_NPIXELS(mi) > 2) { XColor color; #ifndef STANDALONE mp->fg = MI_FG_PIXEL(mi); mp->bg = MI_BG_PIXEL(mi); #endif mp->blackpixel = MI_BLACK_PIXEL(mi); mp->whitepixel = MI_WHITE_PIXEL(mi); if ((mp->cmap = XCreateColormap(display, window, MI_VISUAL(mi), AllocNone)) == None) { free_mandelbrot_screen(mi, mp); return; } XSetWindowColormap(display, window, mp->cmap); (void) XParseColor(display, mp->cmap, "black", &color); (void) XAllocColor(display, mp->cmap, &color); MI_BLACK_PIXEL(mi) = color.pixel; (void) XParseColor(display, mp->cmap, "white", &color); (void) XAllocColor(display, mp->cmap, &color); MI_WHITE_PIXEL(mi) = color.pixel; #ifndef STANDALONE (void) XParseColor(display, mp->cmap, background, &color); (void) XAllocColor(display, mp->cmap, &color); MI_BG_PIXEL(mi) = color.pixel; (void) XParseColor(display, mp->cmap, foreground, &color); (void) XAllocColor(display, mp->cmap, &color); MI_FG_PIXEL(mi) = color.pixel; #endif mp->colors = (XColor *) NULL; mp->ncolors = 0; } if ((mp->gc = XCreateGC(display, MI_WINDOW(mi), (unsigned long) 0, (XGCValues *) NULL)) == None) { free_mandelbrot_screen(mi, mp); return; } } MI_CLEARWINDOW(mi); /* Set up colour map */ mp->direction = (LRAND() & 1) ? 1 : -1; if (MI_IS_INSTALL(mi) && MI_NPIXELS(mi) > 2) { #ifdef STANDALONE Screen *screen = MI_SCREENPTR(mi); #endif if (mp->colors != NULL) { if (mp->ncolors && !mp->no_colors) free_colors( #ifdef STANDALONE screen, #else display, #endif mp->cmap, mp->colors, mp->ncolors); free(mp->colors); mp->colors = (XColor *) NULL; } mp->ncolors = MI_NCOLORS(mi); if (mp->ncolors < 2) mp->ncolors = 2; if (mp->ncolors <= 2) mp->mono_p = True; else mp->mono_p = False; if (mp->mono_p) mp->colors = (XColor *) NULL; else if ((mp->colors = (XColor *) malloc(sizeof (*mp->colors) * (mp->ncolors + 1))) == NULL) { free_mandelbrot_screen(mi, mp); return; } mp->cycle_p = has_writable_cells( #ifdef STANDALONE MI_SCREENPTR(mi), MI_VISUAL(mi) #else mi #endif ); if (mp->cycle_p) { if (MI_IS_FULLRANDOM(mi)) { if (!NRAND(8)) mp->cycle_p = False; else mp->cycle_p = True; } else { mp->cycle_p = cycle_p; } } if (!mp->mono_p) { #ifdef STANDALONE Screen *screen = MI_SCREENPTR(mi); #endif if (!(LRAND() % 10)) make_random_colormap( #ifdef STANDALONE screen, MI_VISUAL(mi), mp->cmap, mp->colors, &mp->ncolors, True, True, &mp->cycle_p, True #else mi, mp->cmap, mp->colors, &mp->ncolors, True, True, &mp->cycle_p #endif ); else if (!(LRAND() % 2)) make_uniform_colormap( #ifdef STANDALONE screen, MI_VISUAL(mi), mp->cmap, mp->colors, &mp->ncolors, True, &mp->cycle_p, True #else mi, mp->cmap, mp->colors, &mp->ncolors, True, &mp->cycle_p #endif ); else make_smooth_colormap( #ifdef STANDALONE screen, MI_VISUAL(mi), mp->cmap, mp->colors, &mp->ncolors, True, &mp->cycle_p, True #else mi, mp->cmap, mp->colors, &mp->ncolors, True, &mp->cycle_p #endif ); } XInstallColormap(display, mp->cmap); if (mp->ncolors < 2) { mp->ncolors = 2; mp->no_colors = True; } else mp->no_colors = False; if (mp->ncolors <= 2) mp->mono_p = True; if (mp->mono_p) mp->cycle_p = False; } if (MI_IS_INSTALL(mi) && MI_NPIXELS(mi) > 2) { if (mp->mono_p) { mp->cur_color = MI_BLACK_PIXEL(mi); } } Select(&mp->extreme_ul,&mp->extreme_lr, mp->screen_width,mp->screen_height, (int) mp->power,mp->reptop, mp->pow, mp->sin, &mp->ul,&mp->lr); } ENTRYPOINT void draw_mandelbrot(ModeInfo * mi) { Display *display = MI_DISPLAY(mi); Window window = MI_WINDOW(mi); int h; complex c; double demrange; mandelstruct *mp; if (mandels == NULL) return; mp = &mandels[MI_SCREEN(mi)]; MI_IS_DRAWN(mi) = True; if (MI_IS_INSTALL(mi) && MI_NPIXELS(mi) > 2) { if (mp->mono_p) { XSetForeground(display, mp->gc, mp->cur_color); } else { mp->cur_color = (mp->cur_color + 1) % mp->ncolors; XSetForeground(display, mp->gc, mp->colors[mp->cur_color].pixel); } } else { if (MI_NPIXELS(mi) > 2) XSetForeground(display, mp->gc, MI_PIXEL(mi, mp->cur_color)); else if (mp->cur_color) XSetForeground(display, mp->gc, MI_BLACK_PIXEL(mi)); else XSetForeground(display, mp->gc, MI_WHITE_PIXEL(mi)); if (++mp->cur_color >= (unsigned int) MI_NPIXELS(mi)) mp->cur_color = 0; } /* Rotate colours */ if (mp->cycle_p) { rotate_colors( #ifdef STANDALONE MI_SCREENPTR(mi), #else display, #endif mp->cmap, mp->colors, mp->ncolors, mp->direction); if (!(LRAND() % 1000)) mp->direction = -mp->direction; } /* so we iterate columns beyond the width of the physical screen, so that ** we just wait around and show what we've done */ if ((!mp->backwards && (mp->column >= 3 * mp->screen_width)) || (mp->backwards && (mp->column < -2 * mp->screen_width))) { /* reset to left edge of screen, bump power */ mp->backwards = (Bool) (LRAND() & 1); if (mp->backwards) mp->column = mp->screen_width - 1; else mp->column = 0; mp->power = NRAND(3) + MINPOWER; /* select a new region! */ Select(&mp->extreme_ul,&mp->extreme_lr, mp->screen_width,mp->screen_height, (int) mp->power,mp->reptop, mp->pow, mp->sin, &mp->ul,&mp->lr); } else if (mp->column >= mp->screen_width || mp->column < 0) { /* delay a while */ if (mp->backwards) mp->column--; else mp->column++; mp->counter++; return; } /* demrange is used to give some idea of scale */ demrange = mp->dem ? fabs(mp->ul.real - mp->lr.real) / 2 : 0; for (h = 0; h < mp->screen_height; h++) { unsigned int color; int result; /* c.real = 1.3 - (double) mp->column / mp->screen_width * 3.4; */ /* c.imag = -1.6 + (double) h / mp->screen_height * 3.2; */ c.real = mp->ul.real + (mp->ul.real-mp->lr.real)*(((double)(mp->column))/mp->screen_width); c.imag = mp->ul.imag + (mp->ul.imag - mp->lr.imag)*(((double) h) / mp->screen_height); result = reps(c, mp->power, mp->reptop, mp->binary, mp->interior, demrange, mp->pow, mp->sin); if (result < 0 || result >= mp->reptop) XSetForeground(display, mp->gc, MI_BLACK_PIXEL(mi)); else { color=(unsigned int) ((MI_NPIXELS(mi) * (float)result) / mp->reptop); XSetForeground(display, mp->gc, MI_PIXEL(mi, color)); } /* we no longer have vertical symmetry - so we compute all points ** and don't draw with redundancy */ XDrawPoint(display, window, mp->gc, mp->column, h); } if (mp->backwards) mp->column--; else mp->column++; mp->counter++; if (mp->counter > MI_CYCLES(mi)) { init_mandelbrot(mi); } } ENTRYPOINT void release_mandelbrot(ModeInfo * mi) { if (mandels != NULL) { int screen; for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) free_mandelbrot_screen(mi, &mandels[screen]); free(mandels); mandels = (mandelstruct *) NULL; } } XSCREENSAVER_MODULE ("Mandelbrot", mandelbrot) #endif /* MODE_mandelbrot */