# To unbundle, run this file echo mandel.c sed 's/^X//' >mandel.c <<'!' X#include X#include X#include X#include X#include X#include "mandel.h" static int nsteps = NSTEPSMIN, colbrush, imodmins, imodmaxs; static int nstepsx, nstepsy; static Fp stepx, stepy; static Complex minpnt = { -1.5, 1 }, X maxpnt = { 0.5, -1 }; static Complex mins = {MIN, MIN}, maxs = {MAX, MAX}; static Complex pixarray[MASRESX][MASRESY]; static Point pmin, pmax; static Rectangle sweeprect; static Image *background; static Image *palette[NCOLS]; static Image *imagebuf = nil; X/* X * takes a number which is the position of the X * byte for that primary color in the 4 bytes of the color int X * colbrush and a number to sum to that color. X */ int addcolor(int colpos, int desvcol, int colbrush) X{ X int colormsk, thiscol; X colormsk = 0xFF; X colormsk <<= colpos * 8; X thiscol = colbrush & colormsk; X thiscol >>= colpos * 8; X thiscol += desvcol; X thiscol <<= colpos * 8; X thiscol &= colormsk; X colbrush &= ~colormsk; X colbrush |= thiscol; X return colbrush; X} X#ifdef SLOW Complex compmul(Complex *a, Complex *b) X{ X Fp are = a->re, aim = a->im, bre = b->re, bim = b->im; X return (Complex){are*bre - aim*bim, are*bim + aim*bre}; X} Complex compsum(Complex *a, Complex *b) X{ X return (Complex){a->re + b->re, a->im + b->im}; X} XFp module(Complex *a) X{ X Fp re = a->re, im = a->im; X return sqrt(re*re + im*im); X// return hypot(a->re, a->im); X} Complex iterate(Complex a, int numsteps) X{ X Fp modz; X Complex z = a, z2; X while (numsteps-- > 0) { X z2 = compmul(&z, &z); X z = compsum(&z2, &a); X modz = module(&z); X if ( /* (int) */ modz > MAX) X return (Complex){MAX, MAX}; X if (modz < MIN) X return (Complex){MIN, MIN}; X } X return z; X} X#else /* SLOW */ X#define compmul(a, b) (Complex){(a)->re*(b)->re - (a)->im*(b)->im,\ X (a)->re*(b)->im + (a)->im*(b)->re} X#define compsum(a, b) (Complex){(a)->re + (b)->re, (a)->im + (b)->im} X// #define module(a) (Fp)hypot((a)->re, (a)->im) X#define module(a) (Fp)sqrt((a)->re*(a)->re + (a)->im*(a)->im) X#endif /* SLOW */ static void drawcol(int isteps, int x, Image *pal, Point *pixelp, Complex *pixpntp) X{ X int y, numsteps, dodraw; X Fp modz, incy = stepy; X Complex z, z2; X Complex *pixp = pixarray[x]; X pixelp->x++; // for x = 1 X pixelp->y = pmin.y; X pixpntp->re += stepx; // for x = 1 X pixpntp->im = minpnt.im; X for (y = 1; y < nstepsy - 1; y++) { X pixelp->y++; // for y = 1 X pixpntp->im -= incy; // for y = 1 X // about half of all points pass these tests. X // the module(pixp)!=MIN test is costly and never true. X ++pixp; // for y = 1 X if (pixp->re != MAX && pixp->im != MAX X /* && module(pixp) != MIN */) { X#ifdef SLOW X *pixp = iterate(pixelpnt, isteps); X dodraw = ((int)module(pixp) < MAX); X#else X z = *pixpntp; X for (numsteps = isteps; numsteps > 0; numsteps--) { X z2 = compmul(&z, &z); X z = compsum(&z2, pixpntp); X modz = module(&z); X if (modz > MAX) { X *pixp = maxs; X dodraw = 0; X break; X } X if (modz < MIN) { X *pixp = mins; X dodraw = 1; X break; X } X } X if (numsteps <= 0) { X *pixp = z; X dodraw = ((int)modz < MAX); X } X // module(pixp)==modz X#endif /* SLOW */ X } else X dodraw = ((int)module(pixp) < MAX); X if (dodraw) X draw(imagebuf, Rect(pixelp->x, pixelp->y, pixelp->x + 1, X pixelp->y + 1), pal, nil, ZP); X } X if (0 && x%200 == 0) X draw(screen, screen->r, imagebuf, nil, screen->r.min); X} void redraw(void) X{ X int x, y, isteps; X Point pixel; X Complex pixelpnt; X Complex *colp; X Image *pal; X // tile pixarray with MIN X for (x = 0; x < MASRESX; x++) { X colp = pixarray[x]; X for (y = MASRESY; y-- > 0; ) X *colp++ = mins; X } X draw(screen, screen->r, background, nil, ZP); X if (imagebuf != nil) X freeimage(imagebuf); X if (screen->r.max.x - screen->r.min.x > MASRESX) X screen->r.max.x = screen->r.min.x + MASRESX; X if (screen->r.max.y - screen->r.min.y > MASRESY) X screen->r.max.y = screen->r.min.y + MASRESY; X imagebuf = allocimage(display, screen->r, CMAP8, 0, DBlack); X if (imagebuf == 0) X exits("out of image memory"); X assert(imagebuf); X /* create two points on the corners of the square*/ X pmin = Pt(imagebuf->r.min.x, imagebuf->r.min.y); X pmax = Pt(imagebuf->r.max.x, imagebuf->r.max.y); X nstepsx = pmax.x - pmin.x; X nstepsy = pmax.y - pmin.y; X stepx = (maxpnt.re - minpnt.re) / nstepsx; X stepy = (minpnt.im - maxpnt.im) / nstepsy; X imodmins = module(&mins); X imodmaxs = module(&maxs); X for (isteps = 11; isteps < nsteps + 11; isteps++) { X pal = palette[(isteps*19) % NCOLS]; X pixel = pmin; X pixelpnt = minpnt; X for (x = 1; x < nstepsx - 1; x++) X drawcol(isteps, x, pal, &pixel, &pixelpnt); X draw(screen, screen->r, imagebuf, nil, screen->r.min); X } X} void eresized(int new) X{ X if (new && getwindow(display, Refnone) < 0) X exits("getwindow failed"); X redraw(); X} void main(int argc, char *argv[]) X{ X int colmap = MINCOLOR; X int key, isteps; X Mouse mev; X USED(argc, argv); X if (initdraw(nil, nil, "mandel") < 0) X exits("initdraw failed"); X /* X * I allocate the color palette to draw X */ X for (isteps = 10; isteps < NCOLS; isteps++) { X palette[isteps] = allocimage(display, Rect(0, 0, 1, 1), X CMAP8, 1, colmap); X if (palette[isteps] == 0) X exits("out of image memory"); X colmap = addcolor(OFFGREEN, 1, colmap); X } X background = allocimage(display, Rect(0, 0, 1, 1), CMAP8, 1, DBlack); X if (background == 0) X exits("out of image memory"); X einit(Emouse|Ekeyboard); X redraw(); X for (; ; ) { X if (ecanmouse()) { X mev = emouse(); X switch (mev.buttons) { X case 4: X sweeprect = egetrect(3, &mev); X if (sweeprect.min.x == 0 && sweeprect.min.y == 0 X && sweeprect.max.x == 0 && X sweeprect.max.y == 0) X break; X minpnt.re += stepx * (sweeprect.min.x - pmin.x); X minpnt.im -= stepy * (sweeprect.min.y - pmin.y); X maxpnt.re = minpnt.re + stepx * X (sweeprect.max.x - pmin.x); X maxpnt.im = minpnt.im - stepy * X (sweeprect.max.y - pmin.y); X redraw(); X break; X } X } X if (ecankbd()) { X key = ekbd(); X switch (key) { X case DELETEKEY: X exits(nil); X break; X case LESSKEY: X nsteps -= 5; X redraw(); X break; X case PLUSKEY: X nsteps += 5; X redraw(); X break; X case RDRAWKEY: X minpnt = (Complex){-1.5, 1}; X maxpnt = (Complex){0.5, -1}; X redraw(); X break; X } X } X sleep(100); X } X} ! echo mandel.h sed 's/^X//' >mandel.h <<'!' X// typedef double Fp; typedef float Fp; typedef struct Complex { X Fp re; X Fp im; X} Complex; enum { X MASRESX = 1024, X MASRESY = 768, X NCOLS = 0xFF, X NSTEPSMIN = 10, X MAX = 500, // module to consider non-convergence X MIN = 0.001, // module to consider convergence X MINCOLOR = DBlue, X OFFBLUE = 1, X MAXCOLOR = DGreen, X OFFGREEN = 2, X OFFRED = 1, X DELETEKEY = '\177', X PLUSKEY = '+', X LESSKEY = '-', X RDRAWKEY = 'r', X}; ! echo mkfile sed 's/^X//' >mkfile <<'!' Xreadme <<'!' Mandel is a program to explore the mandelbrot set. The mandelbrot set is the set of c numbers in the complex plane on which y=z²+c starting with z=0 and iterating by making y=z converges. Quoting Roger Penrose freely, "why can't we explore mathematical objects as we explore mountains or rivers?". Mandel can be zoomed in by using the right button mouse in the same way you resize a window on rio. You can also make the number of steps greater or less the with + and - keys. To redraw the original picture use the key r. Suggestions will be welcomed at paurea@gsyc.escet.urjc.es. X Saludos, X paurea playing with RESX and RESY on mandel.h will use less memory if you are on lower resolutions or plan to run mandelbrot in a little window. !