844 lines
24 KiB
C
844 lines
24 KiB
C
/* -*- Mode: C; tab-width: 4; c-basic-offset: 4 -*- */
|
|
/* mandelbrot --- animated mandelbrot sets */
|
|
|
|
#if !defined( lint ) && !defined( SABER )
|
|
static const char sccsid[] = "@(#)mandelbrot.c 5.09 2003/06/30 xlockmore";
|
|
|
|
#endif
|
|
|
|
#define USE_LOG
|
|
|
|
/*-
|
|
* Copyright (c) 1997 Dan Stromberg <strombrg@nis.acs.uci.edu>
|
|
*
|
|
* 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
|
|
* <tda10.geo@yahoo.com>. 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 <strombrg@nis.acs.uci.edu>
|
|
*/
|
|
|
|
|
|
#ifdef STANDALONE
|
|
#define MODE_mandelbrot
|
|
#define PROGCLASS "Mandelbrot"
|
|
#define HACK_INIT init_mandelbrot
|
|
#define HACK_DRAW draw_mandelbrot
|
|
#define mandelbrot_opts xlockmore_opts
|
|
#define DEFAULTS "*delay: 25000 \n" \
|
|
"*count: -8 \n" \
|
|
"*cycles: 20000 \n" \
|
|
"*ncolors: 200 \n"
|
|
#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"}
|
|
};
|
|
|
|
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;
|
|
ModeInfo *mi;
|
|
} 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; row<sample_step; row++) {
|
|
for (column=0; column<sample_step; column++) {
|
|
int r;
|
|
temp.imag = selected_ul->imag +
|
|
(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,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(Display *display, mandelstruct *mp)
|
|
{
|
|
ModeInfo *mi = mp->mi;
|
|
|
|
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(display, 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;
|
|
}
|
|
}
|
|
|
|
#ifndef STANDALONE
|
|
extern char *background;
|
|
extern char *foreground;
|
|
#endif
|
|
|
|
void
|
|
init_mandelbrot(ModeInfo * mi)
|
|
{
|
|
Display *display = MI_DISPLAY(mi);
|
|
Window window = MI_WINDOW(mi);
|
|
mandelstruct *mp;
|
|
|
|
if (mandels == NULL) {
|
|
if ((mandels = (mandelstruct *) calloc(MI_NUM_SCREENS(mi),
|
|
sizeof (mandelstruct))) == NULL)
|
|
return;
|
|
}
|
|
mp = &mandels[MI_SCREEN(mi)];
|
|
mp->mi = 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 = 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(display, 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(display, mp);
|
|
return;
|
|
}
|
|
}
|
|
MI_CLEARWINDOW(mi);
|
|
|
|
/* Set up colour map */
|
|
mp->direction = (LRAND() & 1) ? 1 : -1;
|
|
if (MI_IS_INSTALL(mi) && MI_NPIXELS(mi) > 2) {
|
|
if (mp->colors != NULL) {
|
|
if (mp->ncolors && !mp->no_colors)
|
|
free_colors(display, 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(display, mp);
|
|
return;
|
|
}
|
|
mp->cycle_p = has_writable_cells(mi);
|
|
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) {
|
|
if (!(LRAND() % 10))
|
|
make_random_colormap(
|
|
#if STANDALONE
|
|
display, MI_WINDOW(mi),
|
|
#else
|
|
mi,
|
|
#endif
|
|
mp->cmap, mp->colors, &mp->ncolors,
|
|
True, True, &mp->cycle_p);
|
|
else if (!(LRAND() % 2))
|
|
make_uniform_colormap(
|
|
#if STANDALONE
|
|
display, MI_WINDOW(mi),
|
|
#else
|
|
mi,
|
|
#endif
|
|
mp->cmap, mp->colors, &mp->ncolors,
|
|
True, &mp->cycle_p);
|
|
else
|
|
make_smooth_colormap(
|
|
#if STANDALONE
|
|
display, MI_WINDOW(mi),
|
|
#else
|
|
mi,
|
|
#endif
|
|
mp->cmap, mp->colors, &mp->ncolors,
|
|
True, &mp->cycle_p);
|
|
}
|
|
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);
|
|
}
|
|
|
|
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(display, 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);
|
|
}
|
|
}
|
|
|
|
void
|
|
release_mandelbrot(ModeInfo * mi)
|
|
{
|
|
if (mandels != NULL) {
|
|
int screen;
|
|
|
|
for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
|
|
free_mandelbrot(MI_DISPLAY(mi), &mandels[screen]);
|
|
free(mandels);
|
|
mandels = (mandelstruct *) NULL;
|
|
}
|
|
}
|
|
|
|
#endif /* MODE_mandelbrot */
|