mcgrew@dartagnan.rutgers.edu (Charles Mcgrew) (09/13/89)
Submitted-by: P{r Emanuelsson <pell@isy.liu.se> Posting-number: Volume 1, Issue 60 Archive-name: FastMand I have written a program to implement the FastM algorithm as described in the book "The Science of Fractal Images" edited by Peitgen and Saupe. The FastM algorithm is good for fast calculation of a (rough) outline of the Mandelbrot set and thus suitable for interactive manipulations. This makes it possible to iterate e.g. only every 10:th row and column of the image, reducing the time by a factor of 100 compared to the standard Mandelbrot algorithm. It's also fun to watch, since it's recursive... The shar file to "FastMtool" follows. Have fun! But beware - it's addictive! /Pell -- #!/bin/sh # shar: Shell Archiver (v1.22) # # Run the following text with /bin/sh to create: # README # Makefile # FastMtool.1 # FastMtool.c # if test -f README; then echo "File README exists"; else sed 's/^X//' << 'SHAR_EOF' > README && X This is a program for interactive Mandelbrot exploration for Sun Xworkstations using SunView. X X What's new and exciting with this program (IMHO :-) ) is that it implements Xa new algorithm. This is good for fast calculation of a (rough) outline of Xthe Mandelbrot set and thus suitable for interactive manipulations. This Xmakes it possible to iterate e.g. only every 10:th row and column of the image, Xreducing the time by a factor of 100 compared to the standard Mandelbrot Xalgorithm. It's also fun to watch, since it's recursive... X X It has been tested on the following configurations: X XSun-2/120 under SunOS 3.5, Sun-3/50, 3/75, 3/110 with FPA, 3/280, 4/110 Xand 4/280, all using SunOS 4.0(.1). Hopefully it should work on others too. X X So how does it work? Well, using some intricate mathematics you can Xarrive at an equation that estimates the distance from a point outside the XMandelbrot set to the nearest point IN the set. Using this knowledge, it's Xpossible to exclude all points lying inside a disk with the radius equal to Xthis distance. You know they can't be part of the set and mark them as Xuninteresting. Then you repeat the calculation with a couple of points Xpicked from the edge of this disk. In this fashion, you will get an outline Xof the set in a very short time. X X Time for the bad news: if you want an image with the same resolution Xas one generated with the standard algorithm, this algorithm will be just Xas slow. This is because this algorithm quickly excludes points that are Xnot part of the Mandelbrot set. Unfortunately, the calculations for these Xpoints will usually be fast since they rapidly diverges to infinity. So the Xpoints left that need to be iterated are all very time-consuming. Furthermore, Xthis algorithm cannot provide those good-looking color images you have all Xseen. It only provides information whether a point is in the set or not. XOf course, on a B/W screen this will usually suffice. Using this program, you Xcan quickly find interesting areas and then run off to your cray and feed the Xcoordinates to a standard Mandelbrot algorithm. X X I certainly didn't invent this myself. I urge you to get the book X"The Science of Fractal Images" edited by Peitgen and Saupe. Springer Verlag, XISBN 0-387-96608-0. It's filled with good stuff like this. X X A possible enhancement would be for the program to draw disks INSIDE Xthe Mandelbrot set too. Unfortunately, this problem is quite non-deterministic Xand does not have a simple solution. Besides, the set, at larger magnifications, Xhas a very intricate structure and it would probably not be possible to Xdraw any large disks inside it. X X I refer you to the man-page for more instructions about running the stuff. XTo get going, just hit the DRAW button as soon as it starts. X X You will probably see a lot of inefficient coding, e.g. using array Xsubscripting instead of pointers etc. Let me just say that profiling shows Xthat without the Sun-specific stuff, it can spend as much as 98% of the Xtime in the MSetDist routine (most of which are floating-point Xcalculations)... That is why I have kept the inefficient, but more Xreadable, code. Using GCC, you cannot achive much better speed hand-coding XMSetDist in assembler, but you are welcome to try... X X Finally, don't flame me for the coding. I just wanted to try this Xalgorithm, and whipped together a demonstration program using parts from Xother programs in a very short time. Specifically: don't run it through lint! X(I haven't :-). I don't have the time to work on this anymore but I thought Xthis was interesting enough to warrant posting. X X One thing I would really love is an X version of this (hint, hint!). XPoor me only knows the workings of SunView... :-( X XHave fun! X X /Pell X-- X"Don't think; let the machine do it for you!" X -- E. C. Berkeley XDept. of Electrical Engineering ====>>> pell@isy.liu.se XUniversity of Linkoping, Sweden ...!uunet!enea!isy.liu.se!pell SHAR_EOF chmod 0644 README || echo "restore of README fails" fi if test -f Makefile; then echo "File Makefile exists"; else sed 's/^X//' << 'SHAR_EOF' > Makefile && XLIBS = -lm -lsuntool -lsunwindow -lpixrect X X# Choose the compile line that suits you best. X# Note that cc -O4 gives 25% faster code that gcc 1.30. I'm not sure why. X# If you have a later version you may want to try gcc anyway, perhaps X# with fancier optimizations... X XFastMtool: FastMtool.c X# GCC, Sun-3 with 68881 math chip. X# gcc -O -traditional -m68881 -o FastMtool FastMtool.c $(LIBS) X# Sun-3 with SunOS 4, 68881 math chip: X cc -O4 -f68881 -o FastMtool FastMtool.c $(LIBS) X# Sun-4 with SunOS 4: X# cc -O4 -o FastMtool FastMtool.c $(LIBS) X# Other suns, configure for maximum speed yourself: X# cc -O -o FastMtool FastMtool.c $(LIBS) SHAR_EOF chmod 0644 Makefile || echo "restore of Makefile fails" fi if test -f FastMtool.1; then echo "File FastMtool.1 exists"; else sed 's/^X//' << 'SHAR_EOF' > FastMtool.1 && X.TH FastMtool 1 "1989 February 25" X.UC 4 X.SH NAME XFastMtool \- Explore the Mandelbrot set under SunView(1) X.SH SYNOPSIS X.B FastMtool X.SH DESCRIPTION X.I FastMtool Xuses a new algorithm, presented in the book X.B X"The Science of Fractal Images" Xpublished by Springer Verlag. This algorithm is well suited to interactive Xexplorations, providing a rough outline of the Mandelbrot set very quickly. XUsing the mouse, you can then select and enlarge interesting regions. X.PP X.B Controls: X.PP XThere are three sliders. Their functions are: X.IP Recur XAffects the recursive part of the algorithm. X.I Recur Xis the minimum distance to the Mandelbrot set at which the recursion Xshould stop. I.e. if X.I Recur Xis equal to 10 then the recursive part of the algorithm will never try Xto get closer than 10 pixels to the set. 5 is probably a good value Xto start with. X.IP Increment XAffects the iterative part of the algorithm. The algorithm starts by scanning Xthe image line after line, pixel for pixel. When it hits a pixel not part Xof the set, it invokes the recursive algorithm. Then it continues scanning. X XIf X.I Increment Xis set e.g. to 5, then only every 5:th line and pixel will be scanned, Xreducing the time 25-fold. X.IP Maxiter XThis determines how many iterations are required before a pixel is considered Xpart of the set. One can view this parameter as the "focusing". This parameter Xneeds to be increased as the magnification increases. If it is set too low Xyou will not get any details in the image; too many pixels will erroneously Xbe considered part of the set. Increasing this also increases the time. XExperiment! X.PP X.B Buttons: X.PP XThere are four buttons: X.IP DRAW XDraws a new image. If you haven't selected new coordinates the old image Xis redrawn, perhaps with new parameters. X.IP UP XReturns you to your previous image, before the last X.I DRAW Xwith new coordinates. X.IP STOP XEmergency stop. It's only possible to stop the iterative refinement. The Xrecursive refinement is usually fast anyway. X.IP Quit XQuits the program (without confirm on SunOS older than 4.0). X.PP X.B XMouse usage: X.PP XTo mark a new area for exploration, press the X.I left Xmouse button and move the mouse, keeping the button down. When you release Xthe button a new area has been marked. Now you can select the X.I DRAW Xbutton to magnify this area, perhaps changing some of the sliders first. X XYou will see the coordinates in the panel change when you move the mouse. X XIf you hit the X.I middle Xbutton, your selected area will be cancelled and the coordinates restored. X.SH RESTRICTIONS XYou cannot save the images to file. X.SH SEE ALSO Xsunview(1) X.SH AUTHORS X.PP XP. Emanuelsson, pell@isy.liu.se. X.br X.I X"The Science of Fractal Images" X\(em Peitgen & Saupe (eds.) SHAR_EOF chmod 0644 FastMtool.1 || echo "restore of FastMtool.1 fails" fi if test -f FastMtool.c; then echo "File FastMtool.c exists"; else sed 's/^X//' << 'SHAR_EOF' > FastMtool.c && X/* X * FastMtool - Perform interactive exploring of the Mandelbrot set on X * Sun workstations. X * Copyright (c) 1989 P{r Emanuelsson, pell@isy.liu.se. All Rights Reserved. X * This program is free software; you can redistribute it and/or modify X * it under the terms of the GNU General Public License as published by X * the Free Software Foundation; either version 1, or any later version. X * This program is distributed in the hope that it will be useful, X * but WITHOUT ANY WARRANTY; without even the implied warranty of X * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the X * GNU General Public License for more details. X * You can receive a copy of the GNU General Public License from the X * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. X * See the README for more information. X */ X X#include <suntool/sunview.h> X#include <suntool/panel.h> X#include <suntool/canvas.h> X X/* Change event_action to event_id(event) for pre 4.0 */ X#ifndef event_action X#define event_action event_id X#define oldSunOS X#endif X X#ifndef oldSunOS X#include <suntool/alert.h> /* Only for SunOS 4 */ X#include <alloca.h> /* Cannot find this on 3.5 */ X#endif X X#include <math.h> X Xtypedef unsigned char Bool; X X#define Stacksize 100 /* Dynamic allocation would be cleaner */ X Xstruct stack { X Pixrect *pr; X double xmin, xmax, ymin, ymax; X} Stack [Stacksize]; X Xstatic int sp = 0; /* Stackpointer */ X X#define MAX(a,b) ((a) > (b) ? (a) : (b)) X#define MIN(a,b) ((a) < (b) ? (a) : (b)) X#define sgn(x) ((x) >= 0 ? 1 : -1) X X#define SQRT_2 1.41421356237 X#define Nmax 800 /* Maximum image size */ X Xstatic Bool MSet[Nmax][Nmax]; Xstatic double xmin = -2.0, xmax=0.5, ymin = -1.25, ymax=1.25, X side=2.5, delta, recur; Xstatic int maxiter=20, incr=5, rec=5; Xstatic int start_x, start_y, new_x, new_y; /* For region select */ Xstatic Bool selected = FALSE, draw_new_image=FALSE, abort=FALSE; X X#define BUTTONFONT "/usr/lib/fonts/fixedwidthfonts/gallant.r.19" Xstatic char *default_font = X "DEFAULT_FONT=/usr/lib/fonts/fixedwidthfonts/screen.r.13"; X Xstatic Frame fr; Xstatic Canvas cv; Xstatic Pixwin *pw; Xstatic Pixfont *bf, *pf; Xstatic Panel pn; Xstatic Panel_item recur_p, incr_p, maxiter_p, stop_p; X X#ifdef notdef /* Future enhancement */ Xstatic int N=512; /* Current image size */ X#else X#define N 512 X#endif X Xstatic char Nmax_str[10]; X Xextern int panel_pw_text(), panel_fonthome(); /* Undocumented, but nice */ X Xdouble MSetDist(); Xvoid quit(), stop(), up(), draw(), sel_region(); X Xvoid main() X{ X (void) putenv(default_font); X bf = pf_open(BUTTONFONT); X pf = pf_default(); X X/* This code is full of strange magic numbers. I apologize for that!!! */ X X fr = window_create(0, FRAME, X WIN_X, 400, X WIN_Y, 150, X WIN_SHOW, TRUE, X FRAME_LABEL, "FastMtool - Mandelbrot exploring", X 0); X pn = window_create(fr, PANEL, 0); X X/* The brain-damaged SunView uses the PANEL_MAX_VALUE to determine the X position of the slider, instead of e.g. always allocate 10 positions X for this and right-justify or something. This means that I have to X use delicate (ugly) pixel-positioning to get the sliders to line up. */ X X recur_p = panel_create_item(pn, PANEL_SLIDER, X PANEL_LABEL_X, 5, X PANEL_LABEL_Y, 5, X PANEL_VALUE_X, 110, X PANEL_VALUE_Y, 5, X PANEL_LABEL_STRING, "Recur:", X PANEL_VALUE, rec, X PANEL_MIN_VALUE, 1, X PANEL_MAX_VALUE, 50, X 0); X incr_p = panel_create_item(pn, PANEL_SLIDER, X PANEL_LABEL_X, 5, X PANEL_LABEL_Y, 25, X PANEL_VALUE_X, 110, X PANEL_VALUE_Y, 25, X PANEL_LABEL_STRING, "Increment:", X PANEL_VALUE, incr, X PANEL_MIN_VALUE, 1, X PANEL_MAX_VALUE, 10, X 0); X maxiter_p = panel_create_item(pn, PANEL_SLIDER, X PANEL_LABEL_X, 5, X PANEL_LABEL_Y, 45, X PANEL_VALUE_X, 78, X PANEL_VALUE_Y, 45, X PANEL_LABEL_STRING, "Maxiter:", X PANEL_VALUE, maxiter, X PANEL_MIN_VALUE, 10, X PANEL_MAX_VALUE, 1000, X 0); X panel_create_item(pn, PANEL_MESSAGE, X PANEL_ITEM_X, 5, X PANEL_ITEM_Y, 100, X PANEL_LABEL_STRING, "Xmin:", X PANEL_LABEL_BOLD, TRUE, X 0); X panel_create_item(pn, PANEL_MESSAGE, X PANEL_ITEM_X, 200, X PANEL_ITEM_Y, 100, X PANEL_LABEL_STRING, "Xmax:", X PANEL_LABEL_BOLD, TRUE, X 0); X panel_create_item(pn, PANEL_MESSAGE, X PANEL_ITEM_X, 5, X PANEL_ITEM_Y, 120, X PANEL_LABEL_STRING, "Ymin:", X PANEL_LABEL_BOLD, TRUE, X 0); X panel_create_item(pn, PANEL_MESSAGE, X PANEL_ITEM_X, 200, X PANEL_ITEM_Y, 120, X PANEL_LABEL_STRING, "Ymax:", X PANEL_LABEL_BOLD, TRUE, X 0); X X#ifdef notdef /* Possible future enhancement... */ X sprintf(Nmax_str, "%d", Nmax); X panel_create_item(pn, PANEL_CYCLE, X PANEL_ITEM_X, 350, X PANEL_ITEM_Y, 5, X PANEL_LABEL_STRING, "Image size:", X PANEL_LABEL_BOLD, TRUE, X PANEL_CHOICE_STRINGS, Nmax_str, "512", "256", 0, X PANEL_VALUE, 1, X 0); X#endif X X panel_create_item(pn, PANEL_BUTTON, X PANEL_ITEM_X, 360, X PANEL_ITEM_Y, 30, X PANEL_LABEL_IMAGE, panel_button_image(pn, "DRAW", 0, bf), X PANEL_NOTIFY_PROC, draw, X 0); X panel_create_item(pn, PANEL_BUTTON, X PANEL_ITEM_X, 360, X PANEL_ITEM_Y, 70, X PANEL_LABEL_IMAGE, panel_button_image(pn, " UP ", 0, bf), X PANEL_NOTIFY_PROC, up, X 0); X panel_create_item(pn, PANEL_BUTTON, X PANEL_ITEM_X, 360, X PANEL_ITEM_Y, 110, X PANEL_LABEL_IMAGE, panel_button_image(pn, "STOP", 0, bf), X PANEL_NOTIFY_PROC, stop, X 0); X panel_create_item(pn, PANEL_BUTTON, X PANEL_ITEM_X, 450, X PANEL_ITEM_Y, 72, X PANEL_LABEL_IMAGE, panel_button_image(pn, "Quit", 0, pf), X PANEL_NOTIFY_PROC, quit, X 0); X window_fit_height(pn); X cv = window_create(fr, CANVAS, X WIN_WIDTH, N, X WIN_HEIGHT, N, X WIN_CONSUME_PICK_EVENT, LOC_DRAG, X WIN_EVENT_PROC, sel_region, X 0); X X window_fit(fr); X notify_interpose_destroy_func(fr, quit); X X pw = canvas_pixwin(cv); X notify_dispatch(); /* To make the next put_coords work */ X put_coords(0, N-1, N-1, 0); X X bzero((char *) MSet, sizeof(MSet)); X pw_writebackground(pw, 0, 0, N, N, PIX_SRC | PIX_COLOR(1)); X X /* Cannot use window_main_loop() because the notifier can't dispatch X events inside a PANEL_NOTIFY_PROC. */ X X while (1) { X notify_dispatch(); X if (draw_new_image) { X panel_pw_text(pn, 347, 5 + panel_fonthome(bf), PIX_SRC, bf, "Drawing!"); X compute(); X panel_pw_text(pn, 347, 5 + panel_fonthome(bf), PIX_SRC, bf, " "); X draw_new_image = FALSE; X } X usleep(50000); X } X} X Xput_coords(ixmin, ixmax, iymin, iymax) X int ixmin, ixmax, iymin, iymax; X{ X char str[20]; X X sprintf(str, "%10.7lf", xmin + side * ixmin/(N-1)); X panel_pw_text(pn, 50, 100 + panel_fonthome(pf), PIX_SRC, pf, str); X sprintf(str, "%10.7lf", xmin + side * ixmax/(N-1)); X panel_pw_text(pn, 245, 100 + panel_fonthome(pf), PIX_SRC, pf, str); X sprintf(str, "%10.7lf", ymin + side * (N-1-iymin)/(N-1)); X panel_pw_text(pn, 50, 120 + panel_fonthome(pf), PIX_SRC, pf, str); X sprintf(str, "%10.7lf", ymin + side * (N-1-iymax)/(N-1)); X panel_pw_text(pn, 245, 120 + panel_fonthome(pf), PIX_SRC, pf, str); X} X Xvoid sel_region(canvas, event, arg) X Canvas canvas; X Event *event; X char *arg; X{ X static Bool mouseing = FALSE; X register int maxdist, tmpx, tmpy; X X#define mkbox(a,b,c,d) \ X pw_vector(pw, a, b, c, b, PIX_SRC^PIX_DST, 1);\ X pw_vector(pw, a, b, a, d, PIX_SRC^PIX_DST, 1);\ X pw_vector(pw, c, b, c, d, PIX_SRC^PIX_DST, 1);\ X pw_vector(pw, a, d, c, d, PIX_SRC^PIX_DST, 1) X X switch (event_action(event)) { X case MS_LEFT: X if (event_is_down(event)) { X if (selected) { /* Remove old box first */ X mkbox(start_x, start_y, new_x, new_y); X } X start_x = new_x = event_x(event); X start_y = new_y = event_y(event); X put_coords(new_x, new_x, new_y, new_y); X mouseing = TRUE; X } else { X mouseing = FALSE; X selected = TRUE; X } X break; X case LOC_DRAG: X if (mouseing) { X mkbox(start_x, start_y, new_x, new_y); /* Remove old box */ X X /* We want to restrict the size to be square */ X tmpx = event_x(event) - start_x; X tmpy = start_y - event_y(event); X maxdist = MIN(tmpx * sgn(tmpx), tmpy * sgn(tmpy)); X new_x = start_x + maxdist * sgn(tmpx); X new_y = start_y - maxdist * sgn(tmpy); X X mkbox(start_x, start_y, new_x, new_y); /* Draw new box */ X put_coords(MIN(start_x, new_x), MAX(start_x, new_x), X MAX(start_y, new_y), MIN(start_y, new_y)); X } X break; X case MS_MIDDLE: X if (selected) { X mkbox(start_x, start_y, new_x, new_y); X selected = FALSE; X } X } X} X Xvoid stop() X{ X abort = TRUE; X} X Xvoid up() X{ X if (sp > 0) { X Pixrect *old; X register int i, j, k; X Bool *mem; X X selected = FALSE; X sp--; X xmin = Stack[sp].xmin; X xmax = Stack[sp].xmax; X ymin = Stack[sp].ymin; X ymax = Stack[sp].ymax; X side = xmax - xmin; X put_coords(0, N-1, N-1, 0); X old = Stack[sp].pr; X pw_write(pw, 0, 0, N, N, PIX_SRC, old, 0, 0); X X /* Restore MSet */ X /* This is ugly - I'm assuming that sizeof(Bool) == 1. Shame on me! */ X mem = (Bool *) mpr_d(old)->md_image; X for (i=0; i<N; i++) { X for (j=0; j<N; j+=8) { X for (k=0; k<8; k++) X MSet[j+k][N-1-i] = ((*mem & (1<<(7-k))) >> (7-k)) == 0 ? 1 : 0; X mem++; X } X } X pr_destroy(old); X } X} X Xvoid draw() X{ X draw_new_image = TRUE; X} X Xvoid quit() X{ X#ifdef oldSunOS X exit(0); /* You won't miss the following fancy stuff.. */ X#else X if (alert_prompt(fr, (Event *)0, X ALERT_MESSAGE_STRINGS, X "Please confirm:", X "Do you know what you're doing??", X 0, X ALERT_BUTTON_YES, "Of course, quit bugging me!", X ALERT_BUTTON_NO, "Sorry, I hit the wrong button...", X ALERT_MESSAGE_FONT, bf, X ALERT_BUTTON_FONT, pf, X 0) X == ALERT_YES) { X exit(0); X } else X notify_veto_destroy(fr); X#endif X} X Xcompute() X{ X register int ix, iy; X X if (selected && start_x != new_x) { /* New region selected */ X Pixrect *save = mem_create(N, N, 1); X mkbox(start_x, start_y, new_x, new_y); /* Remove the box first */ X pw_read(save, 0, 0, N, N, PIX_SRC, pw, 0, 0); X Stack[sp].pr = save; X Stack[sp].xmin = xmin; X Stack[sp].xmax = xmax; X Stack[sp].ymin = ymin; X Stack[sp].ymax = ymax; X if (sp < Stacksize) sp++; /* Hard to imagine this happen, but... */ X bzero((char *) MSet, sizeof(MSet)); X pw_writebackground(pw, 0, 0, N, N, PIX_SRC | PIX_COLOR(1)); X X xmax = xmin + side * MAX(start_x, new_x) /(N-1); X xmin += side * MIN(start_x, new_x) /(N-1); X ymax = ymin + side * (N-1-MIN(start_y, new_y)) /(N-1); X ymin += side * (N-1-MAX(start_y, new_y)) /(N-1); X selected = FALSE; X } else { X /* No region selected, just redraw. Perhaps using new parameters. */ X put_coords(0, N-1, N-1, 0); X } X X rec = (int) panel_get_value(recur_p); X incr = (int) panel_get_value(incr_p); X maxiter = (int) panel_get_value(maxiter_p); X X side = xmax - xmin; X delta = 0.25 * side / (N-1); /* 0.25 seems OK */ X recur = rec * delta; X X abort = FALSE; X X/*************************************************************************/ X/*************************************************************************/ X X/* From now on, you will find the new Mandelbrot algorithm. No Sun specific X stuff, except the notify_dispatch() and some pw_put() and pw_line(). */ X X for (iy = 0; iy < N; iy += incr) { X notify_dispatch(); /* Allow user to hit the STOP button */ X if (abort) break; X for (ix = 0; ix < N; ix += incr) X if (!MSet[ix][iy]) X MDisk(ix,iy); X } X} X XMDisk(ix, iy) X register int ix, iy; X{ X register double cx, cy, dist; X register int irad; X X if (ix<0 || ix>=N || iy<0 || iy>=N || MSet[ix][iy]) return; X X cx = xmin + (side * ix) / (N-1); X cy = ymin + (side * iy) / (N-1); X dist = 0.25 * MSetDist(cx, cy, maxiter); X irad = dist / side * (N-1); /* Bug in the original algorithm */ X X if (irad == 1) { X MSet[ix][iy] = 1; X pw_put(pw, ix, N-1-iy, 0); /* Sun specific */ X } else if (irad > 1) X FILLDISK(ix, iy, irad); X else if (dist > delta) { X MSet[ix][iy] = 1; X pw_put(pw, ix, N-1-iy, 0); /* Sun specific */ X } X X if (dist > recur) { X if (irad > 1) irad++; X MDisk(ix, iy + irad); X MDisk(ix, iy - irad); X MDisk(ix + irad, iy); X MDisk(ix - irad, iy); X X/* It will be slightly faster if I leave out the following "45-degree" X recursions. The reason is that most of these points will probably X be filled already and MDisk will return immediately. But since X they are in the original algorithm and the improvement is only marginal X I will leave them here. */ X X irad = 0.5 + irad / SQRT_2; X MDisk(ix + irad, iy + irad); X MDisk(ix - irad, iy - irad); X MDisk(ix - irad, iy + irad); X MDisk(ix + irad, iy - irad); X } X} X Xdouble MSetDist(cx, cy, maxiter) X register double cx, cy; X register int maxiter; X{ X# define overflow 10e10 /* Don't know if this is foolproof */ X X register int iter=0; X register double zx, zy, zx2, zy2; X register double *xorbit, *yorbit; X X /* Could use a static array for this, if you don't have alloca */ X xorbit = (double *) alloca(maxiter * sizeof(double)); X yorbit = (double *) alloca(maxiter * sizeof(double)); X X /* This is the standard Mandelbrot iteration */ X zx = zy = zx2 = zy2 = xorbit[0] = yorbit[0] = 0.0; X do { X zy = (zx * zy) + (zx * zy) + cy; /* gcc generates only one mult for this */ X zx = zx2 - zy2 + cx; X iter++; X xorbit[iter] = zx; /* Save the iteration orbits for later */ X yorbit[iter] = zy; X zx2 = zx * zx; X zy2 = zy * zy; X } while ((zx2 + zy2) < 1000.0 && iter<maxiter); X X if (iter < maxiter) { /* Generate derivatives */ X register double zpx, zpy, tmp; X register int i; X X zpx = zpy = 0.0; X X for (i=0; i<iter; i++) { X tmp = 2 * (xorbit[i] * zpx - yorbit[i] * zpy) + 1.0; X zpy = 2 * (xorbit[i] * zpy + yorbit[i] * zpx); X zpx = tmp; X if (fabs(zpx) > overflow || fabs(zpy) > overflow) X return 0.0; X } X /* This is the distance estimation */ X return log(zx2 + zy2) * sqrt(zx2 + zy2) / sqrt(zpx*zpx + zpy*zpy); X } X return 0.0; X} X XFILLDISK(ix, iy, irad) X register int ix, iy; X int irad; X{ X register int x, y, e; X X /* The "Mini"-algorithm. Perhaps I should use display locking around the X plotline's, but after all, the fun is watching it work... */ X X x = 0; X y = irad; X e = irad / 2; X while (x <= y) { X plotline(ix - x, ix + x, iy + y); X plotline(ix - y, ix + y, iy + x); X plotline(ix - x, ix + x, iy - y); X plotline(ix - y, ix + y, iy - x); X e -= x; X if (e < 0) { X e += y; X y--; X } X x++; X } X} X Xplotline(x1, x2, y) X register int x1, x2, y; X{ X register int i; X if (y<0 || y>N-1 || (x1<0 && x2<0) || (x1>=N-1 && x2 >=N-1)) return; X X if (x1 < 0) x1 = 0; X if (x1 > N-1) x1 = N-1; X if (x2 < 0) x2 = 0; X if (x2 > N-1) x2 = N-1; X X pw_vector(pw, x1, N-1-y, x2, N-1-y, PIX_SRC, 0); /* Sun specific */ X X for (i=x1; i<=x2; i++) X MSet[i][y] = 1; X} SHAR_EOF chmod 0644 FastMtool.c || echo "restore of FastMtool.c fails" fi exit 0