home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / compsrcs / sun / volume01 / fastmand < prev    next >
Encoding:
Internet Message Format  |  1991-08-27  |  24.6 KB

  1. From decwrl!sun-barr!rutgers!aramis.rutgers.edu!dartagnan.rutgers.edu!mcgrew Sat Sep 23 15:59:57 PDT 1989
  2. Article 62 of comp.sources.sun:
  3. Path: decwrl!sun-barr!rutgers!aramis.rutgers.edu!dartagnan.rutgers.edu!mcgrew
  4. From: mcgrew@dartagnan.rutgers.edu (Charles Mcgrew)
  5. Newsgroups: comp.sources.sun
  6. Subject: v01i060:  FastMtool - a SunView mandelbrot image maker
  7. Message-ID: <Sep.13.00.08.30.1989.8740@dartagnan.rutgers.edu>
  8. Date: 13 Sep 89 04:08:33 GMT
  9. Organization: Rutgers Univ., New Brunswick, N.J.
  10. Lines: 768
  11. Approved: mcgrew@aramis.rutgers.edu
  12.  
  13. Submitted-by: P{r Emanuelsson <pell@isy.liu.se>
  14. Posting-number: Volume 1, Issue 60
  15. Archive-name: FastMand
  16.  
  17.  
  18. I have written a program to implement the FastM algorithm as described
  19. in the book "The Science of Fractal Images" edited by Peitgen and Saupe.
  20.  
  21. The FastM algorithm is good for fast calculation of a (rough) outline of
  22. the Mandelbrot set and thus suitable for interactive manipulations. This
  23. makes it possible to iterate e.g. only every 10:th row and column of the image,
  24. reducing the time by a factor of 100 compared to the standard Mandelbrot
  25. algorithm. It's also fun to watch, since it's recursive...
  26.  
  27. The shar file to "FastMtool" follows.
  28.  
  29. Have fun! But beware - it's addictive!
  30.  
  31.    /Pell
  32. --
  33. #!/bin/sh
  34. # shar:    Shell Archiver  (v1.22)
  35. #
  36. #    Run the following text with /bin/sh to create:
  37. #      README
  38. #      Makefile
  39. #      FastMtool.1
  40. #      FastMtool.c
  41. #
  42. if test -f README; then echo "File README exists"; else
  43. sed 's/^X//' << 'SHAR_EOF' > README &&
  44. X    This is a program for interactive Mandelbrot exploration for Sun
  45. Xworkstations using SunView.
  46. X
  47. X    What's new and exciting with this program (IMHO :-) ) is that it implements
  48. Xa new algorithm. This is good for fast calculation of a (rough) outline of
  49. Xthe Mandelbrot set and thus suitable for interactive manipulations. This
  50. Xmakes it possible to iterate e.g. only every 10:th row and column of the image,
  51. Xreducing the time by a factor of 100 compared to the standard Mandelbrot
  52. Xalgorithm. It's also fun to watch, since it's recursive...
  53. X
  54. X    It has been tested on the following configurations:
  55. X
  56. XSun-2/120 under SunOS 3.5, Sun-3/50, 3/75, 3/110 with FPA, 3/280, 4/110
  57. Xand 4/280, all using SunOS 4.0(.1). Hopefully it should work on others too.
  58. X
  59. X    So how does it work? Well, using some intricate mathematics you can
  60. Xarrive at an equation that estimates the distance from a point outside the
  61. XMandelbrot set to the nearest point IN the set. Using this knowledge, it's
  62. Xpossible to exclude all points lying inside a disk with the radius equal to
  63. Xthis distance. You know they can't be part of the set and mark them as
  64. Xuninteresting. Then you repeat the calculation with a couple of points
  65. Xpicked from the edge of this disk. In this fashion, you will get an outline
  66. Xof the set in a very short time.
  67. X
  68. X    Time for the bad news: if you want an image with the same resolution
  69. Xas one generated with the standard algorithm, this algorithm will be just
  70. Xas slow. This is because this algorithm quickly excludes points that are
  71. Xnot part of the Mandelbrot set. Unfortunately, the calculations for these
  72. Xpoints will usually be fast since they rapidly diverges to infinity. So the
  73. Xpoints left that need to be iterated are all very time-consuming. Furthermore,
  74. Xthis algorithm cannot provide those good-looking color images you have all
  75. Xseen. It only provides information whether a point is in the set or not.
  76. XOf course, on a B/W screen this will usually suffice. Using this program, you
  77. Xcan quickly find interesting areas and then run off to your cray and feed the
  78. Xcoordinates to a standard Mandelbrot algorithm.
  79. X
  80. X    I certainly didn't invent this myself. I urge you to get the book
  81. X"The Science of Fractal Images" edited by Peitgen and Saupe. Springer Verlag,
  82. XISBN 0-387-96608-0. It's filled with good stuff like this.
  83. X
  84. X    A possible enhancement would be for the program to draw disks INSIDE
  85. Xthe Mandelbrot set too. Unfortunately, this problem is quite non-deterministic
  86. Xand does not have a simple solution. Besides, the set, at larger magnifications,
  87. Xhas a very intricate structure and it would probably not be possible to
  88. Xdraw any large disks inside it.
  89. X
  90. X    I refer you to the man-page for more instructions about running the stuff.
  91. XTo get going, just hit the DRAW button as soon as it starts.
  92. X
  93. X    You will probably see a lot of inefficient coding, e.g. using array
  94. Xsubscripting instead of pointers etc. Let me just say that profiling shows
  95. Xthat without the Sun-specific stuff, it can spend as much as 98% of the
  96. Xtime in the MSetDist routine (most of which are floating-point
  97. Xcalculations)... That is why I have kept the inefficient, but more
  98. Xreadable, code. Using GCC, you cannot achive much better speed hand-coding
  99. XMSetDist in assembler, but you are welcome to try...
  100. X
  101. X    Finally, don't flame me for the coding. I just wanted to try this
  102. Xalgorithm, and whipped together a demonstration program using parts from
  103. Xother programs in a very short time. Specifically: don't run it through lint!
  104. X(I haven't :-). I don't have the time to work on this anymore but I thought
  105. Xthis was interesting enough to warrant posting.
  106. X
  107. X    One thing I would really love is an X version of this (hint, hint!).
  108. XPoor me only knows the workings of SunView... :-(
  109. X
  110. XHave fun!
  111. X
  112. X    /Pell
  113. X--
  114. X"Don't think; let the machine do it for you!"
  115. X                                   -- E. C. Berkeley
  116. XDept. of Electrical Engineering             ====>>>    pell@isy.liu.se
  117. XUniversity of Linkoping, Sweden         ...!uunet!enea!isy.liu.se!pell
  118. SHAR_EOF
  119. chmod 0644 README || echo "restore of README fails"
  120. fi
  121. if test -f Makefile; then echo "File Makefile exists"; else
  122. sed 's/^X//' << 'SHAR_EOF' > Makefile &&
  123. XLIBS = -lm -lsuntool -lsunwindow -lpixrect
  124. X
  125. X# Choose the compile line that suits you best.
  126. X# Note that cc -O4 gives 25% faster code that gcc 1.30. I'm not sure why.
  127. X# If you have a later version you may want to try gcc anyway, perhaps
  128. X# with fancier optimizations...
  129. X
  130. XFastMtool: FastMtool.c
  131. X# GCC, Sun-3 with 68881 math chip.
  132. X#    gcc -O -traditional -m68881 -o FastMtool FastMtool.c $(LIBS)
  133. X# Sun-3 with SunOS 4, 68881 math chip:
  134. X    cc -O4 -f68881 -o FastMtool FastMtool.c $(LIBS)
  135. X# Sun-4 with SunOS 4:
  136. X#    cc -O4 -o FastMtool FastMtool.c $(LIBS)
  137. X# Other suns, configure for maximum speed yourself:
  138. X#    cc -O -o FastMtool FastMtool.c $(LIBS)
  139. SHAR_EOF
  140. chmod 0644 Makefile || echo "restore of Makefile fails"
  141. fi
  142. if test -f FastMtool.1; then echo "File FastMtool.1 exists"; else
  143. sed 's/^X//' << 'SHAR_EOF' > FastMtool.1 &&
  144. X.TH FastMtool 1 "1989 February 25"
  145. X.UC 4
  146. X.SH NAME
  147. XFastMtool \- Explore the Mandelbrot set under SunView(1)
  148. X.SH SYNOPSIS
  149. X.B FastMtool
  150. X.SH DESCRIPTION
  151. X.I FastMtool
  152. Xuses a new algorithm, presented in the book
  153. X.B
  154. X"The Science of Fractal Images"
  155. Xpublished by Springer Verlag. This algorithm is well suited to interactive
  156. Xexplorations, providing a rough outline of the Mandelbrot set very quickly.
  157. XUsing the mouse, you can then select and enlarge interesting regions.
  158. X.PP
  159. X.B Controls:
  160. X.PP
  161. XThere are three sliders. Their functions are:
  162. X.IP Recur
  163. XAffects the recursive part of the algorithm.
  164. X.I Recur
  165. Xis the minimum distance to the Mandelbrot set at which the recursion
  166. Xshould stop. I.e. if
  167. X.I Recur
  168. Xis equal to 10 then the recursive part of the algorithm will never try
  169. Xto get closer than 10 pixels to the set. 5 is probably a good value
  170. Xto start with.
  171. X.IP Increment
  172. XAffects the iterative part of the algorithm. The algorithm starts by scanning
  173. Xthe image line after line, pixel for pixel. When it hits a pixel not part
  174. Xof the set, it invokes the recursive algorithm. Then it continues scanning.
  175. X
  176. XIf
  177. X.I Increment
  178. Xis set e.g. to 5, then only every 5:th line and pixel will be scanned,
  179. Xreducing the time 25-fold.
  180. X.IP Maxiter
  181. XThis determines how many iterations are required before a pixel is considered
  182. Xpart of the set. One can view this parameter as the "focusing". This parameter
  183. Xneeds to be increased as the magnification increases. If it is set too low
  184. Xyou will not get any details in the image; too many pixels will erroneously
  185. Xbe considered part of the set. Increasing this also increases the time.
  186. XExperiment!
  187. X.PP
  188. X.B Buttons:
  189. X.PP
  190. XThere are four buttons:
  191. X.IP DRAW
  192. XDraws a new image. If you haven't selected new coordinates the old image
  193. Xis redrawn, perhaps with new parameters.
  194. X.IP UP
  195. XReturns you to your previous image, before the last
  196. X.I DRAW
  197. Xwith new coordinates.
  198. X.IP STOP
  199. XEmergency stop. It's only possible to stop the iterative refinement. The
  200. Xrecursive refinement is usually fast anyway.
  201. X.IP Quit
  202. XQuits the program (without confirm on SunOS older than 4.0).
  203. X.PP
  204. X.B
  205. XMouse usage:
  206. X.PP
  207. XTo mark a new area for exploration, press the
  208. X.I left
  209. Xmouse button and move the mouse, keeping the button down. When you release
  210. Xthe button a new area has been marked. Now you can select the
  211. X.I DRAW
  212. Xbutton to magnify this area, perhaps changing some of the sliders first.
  213. X
  214. XYou will see the coordinates in the panel change when you move the mouse.
  215. X
  216. XIf you hit the
  217. X.I middle
  218. Xbutton, your selected area will be cancelled and the coordinates restored.
  219. X.SH RESTRICTIONS
  220. XYou cannot save the images to file.
  221. X.SH SEE ALSO
  222. Xsunview(1)
  223. X.SH AUTHORS
  224. X.PP
  225. XP. Emanuelsson, pell@isy.liu.se.
  226. X.br
  227. X.I
  228. X"The Science of Fractal Images"
  229. X\(em Peitgen & Saupe (eds.)
  230. SHAR_EOF
  231. chmod 0644 FastMtool.1 || echo "restore of FastMtool.1 fails"
  232. fi
  233. if test -f FastMtool.c; then echo "File FastMtool.c exists"; else
  234. sed 's/^X//' << 'SHAR_EOF' > FastMtool.c &&
  235. X/*
  236. X * FastMtool - Perform interactive exploring of the Mandelbrot set on
  237. X * Sun workstations.
  238. X * Copyright (c) 1989 P{r Emanuelsson, pell@isy.liu.se. All Rights Reserved.
  239. X * This program is free software; you can redistribute it and/or modify
  240. X * it under the terms of the GNU General Public License as published by
  241. X * the Free Software Foundation; either version 1, or any later version.
  242. X * This program is distributed in the hope that it will be useful,
  243. X * but WITHOUT ANY WARRANTY; without even the implied warranty of
  244. X * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  245. X * GNU General Public License for more details.
  246. X * You can receive a copy of the GNU General Public License from the
  247. X * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  248. X * See the README for more information.
  249. X */
  250. X
  251. X#include <suntool/sunview.h>
  252. X#include <suntool/panel.h>
  253. X#include <suntool/canvas.h>
  254. X
  255. X/* Change event_action to event_id(event) for pre 4.0 */
  256. X#ifndef event_action
  257. X#define event_action event_id
  258. X#define oldSunOS
  259. X#endif
  260. X
  261. X#ifndef oldSunOS
  262. X#include <suntool/alert.h>    /* Only for SunOS 4 */
  263. X#include <alloca.h>        /* Cannot find this on 3.5 */
  264. X#endif
  265. X
  266. X#include <math.h>
  267. X
  268. Xtypedef unsigned char Bool;
  269. X
  270. X#define Stacksize 100        /* Dynamic allocation would be cleaner */
  271. X
  272. Xstruct stack {
  273. X  Pixrect *pr;
  274. X  double xmin, xmax, ymin, ymax;
  275. X} Stack [Stacksize];
  276. X
  277. Xstatic int sp = 0;        /* Stackpointer */
  278. X
  279. X#define MAX(a,b) ((a) > (b) ? (a) : (b))
  280. X#define MIN(a,b) ((a) < (b) ? (a) : (b))
  281. X#define sgn(x) ((x) >= 0 ? 1 : -1)
  282. X
  283. X#define SQRT_2 1.41421356237
  284. X#define Nmax 800        /* Maximum image size */
  285. X
  286. Xstatic Bool MSet[Nmax][Nmax];
  287. Xstatic double xmin = -2.0, xmax=0.5, ymin = -1.25, ymax=1.25,
  288. X              side=2.5, delta, recur;
  289. Xstatic int maxiter=20, incr=5, rec=5;
  290. Xstatic int start_x, start_y, new_x, new_y; /* For region select */
  291. Xstatic Bool selected = FALSE, draw_new_image=FALSE, abort=FALSE;
  292. X
  293. X#define BUTTONFONT "/usr/lib/fonts/fixedwidthfonts/gallant.r.19"
  294. Xstatic char *default_font =
  295. X  "DEFAULT_FONT=/usr/lib/fonts/fixedwidthfonts/screen.r.13";
  296. X
  297. Xstatic Frame fr;
  298. Xstatic Canvas cv;
  299. Xstatic Pixwin *pw;
  300. Xstatic Pixfont *bf, *pf;
  301. Xstatic Panel pn;
  302. Xstatic Panel_item recur_p, incr_p, maxiter_p, stop_p;
  303. X
  304. X#ifdef notdef            /* Future enhancement */
  305. Xstatic int N=512;        /* Current image size */
  306. X#else
  307. X#define N 512
  308. X#endif
  309. X
  310. Xstatic char Nmax_str[10];
  311. X
  312. Xextern int panel_pw_text(), panel_fonthome(); /* Undocumented, but nice */
  313. X
  314. Xdouble MSetDist();
  315. Xvoid quit(), stop(), up(), draw(), sel_region();
  316. X
  317. Xvoid main()
  318. X{
  319. X  (void) putenv(default_font);
  320. X  bf = pf_open(BUTTONFONT);
  321. X  pf = pf_default();
  322. X
  323. X/* This code is full of strange magic numbers. I apologize for that!!! */
  324. X
  325. X  fr = window_create(0, FRAME,
  326. X             WIN_X, 400,
  327. X             WIN_Y, 150,
  328. X             WIN_SHOW, TRUE,
  329. X             FRAME_LABEL, "FastMtool - Mandelbrot exploring",
  330. X             0);
  331. X  pn = window_create(fr, PANEL, 0);
  332. X
  333. X/* The brain-damaged SunView uses the PANEL_MAX_VALUE to determine the
  334. X   position of the slider, instead of e.g. always allocate 10 positions
  335. X   for this and right-justify or something. This means that I have to
  336. X   use delicate (ugly) pixel-positioning to get the sliders to line up. */
  337. X
  338. X  recur_p = panel_create_item(pn, PANEL_SLIDER,
  339. X                  PANEL_LABEL_X, 5,
  340. X                  PANEL_LABEL_Y, 5,
  341. X                  PANEL_VALUE_X, 110,
  342. X                  PANEL_VALUE_Y, 5,
  343. X                  PANEL_LABEL_STRING, "Recur:",
  344. X                  PANEL_VALUE, rec,
  345. X                  PANEL_MIN_VALUE, 1,
  346. X                  PANEL_MAX_VALUE, 50,
  347. X                  0);
  348. X  incr_p = panel_create_item(pn, PANEL_SLIDER,
  349. X                 PANEL_LABEL_X, 5,
  350. X                 PANEL_LABEL_Y, 25,
  351. X                 PANEL_VALUE_X, 110,
  352. X                 PANEL_VALUE_Y, 25,
  353. X                 PANEL_LABEL_STRING, "Increment:",
  354. X                 PANEL_VALUE, incr,
  355. X                 PANEL_MIN_VALUE, 1,
  356. X                 PANEL_MAX_VALUE, 10,
  357. X                 0);
  358. X  maxiter_p = panel_create_item(pn, PANEL_SLIDER,
  359. X                PANEL_LABEL_X, 5,
  360. X                PANEL_LABEL_Y, 45,
  361. X                PANEL_VALUE_X, 78,
  362. X                PANEL_VALUE_Y, 45,
  363. X                PANEL_LABEL_STRING, "Maxiter:",
  364. X                PANEL_VALUE, maxiter,
  365. X                PANEL_MIN_VALUE, 10,
  366. X                PANEL_MAX_VALUE, 1000,
  367. X                0);
  368. X  panel_create_item(pn, PANEL_MESSAGE,
  369. X            PANEL_ITEM_X, 5,
  370. X            PANEL_ITEM_Y, 100,
  371. X            PANEL_LABEL_STRING, "Xmin:",
  372. X            PANEL_LABEL_BOLD, TRUE,
  373. X            0);
  374. X  panel_create_item(pn, PANEL_MESSAGE,
  375. X            PANEL_ITEM_X, 200,
  376. X            PANEL_ITEM_Y, 100,
  377. X            PANEL_LABEL_STRING, "Xmax:",
  378. X            PANEL_LABEL_BOLD, TRUE,
  379. X            0);
  380. X  panel_create_item(pn, PANEL_MESSAGE,
  381. X            PANEL_ITEM_X, 5,
  382. X            PANEL_ITEM_Y, 120,
  383. X            PANEL_LABEL_STRING, "Ymin:",
  384. X            PANEL_LABEL_BOLD, TRUE,
  385. X            0);
  386. X  panel_create_item(pn, PANEL_MESSAGE,
  387. X            PANEL_ITEM_X, 200,
  388. X            PANEL_ITEM_Y, 120,
  389. X            PANEL_LABEL_STRING, "Ymax:",
  390. X            PANEL_LABEL_BOLD, TRUE,
  391. X            0);
  392. X
  393. X#ifdef notdef            /* Possible future enhancement... */
  394. X  sprintf(Nmax_str, "%d", Nmax);
  395. X  panel_create_item(pn, PANEL_CYCLE,
  396. X            PANEL_ITEM_X, 350,
  397. X            PANEL_ITEM_Y, 5,
  398. X            PANEL_LABEL_STRING, "Image size:",
  399. X            PANEL_LABEL_BOLD, TRUE,
  400. X            PANEL_CHOICE_STRINGS, Nmax_str, "512", "256", 0,
  401. X            PANEL_VALUE, 1,
  402. X            0);
  403. X#endif
  404. X
  405. X  panel_create_item(pn, PANEL_BUTTON,
  406. X            PANEL_ITEM_X, 360,
  407. X            PANEL_ITEM_Y, 30,
  408. X            PANEL_LABEL_IMAGE, panel_button_image(pn, "DRAW", 0, bf),
  409. X            PANEL_NOTIFY_PROC, draw,
  410. X            0);
  411. X  panel_create_item(pn, PANEL_BUTTON,
  412. X            PANEL_ITEM_X, 360,
  413. X            PANEL_ITEM_Y, 70,
  414. X            PANEL_LABEL_IMAGE, panel_button_image(pn, " UP ", 0, bf),
  415. X            PANEL_NOTIFY_PROC, up,
  416. X            0);
  417. X  panel_create_item(pn, PANEL_BUTTON,
  418. X            PANEL_ITEM_X, 360,
  419. X            PANEL_ITEM_Y, 110,
  420. X            PANEL_LABEL_IMAGE, panel_button_image(pn, "STOP", 0, bf),
  421. X            PANEL_NOTIFY_PROC, stop,
  422. X            0);
  423. X  panel_create_item(pn, PANEL_BUTTON,
  424. X            PANEL_ITEM_X, 450,
  425. X            PANEL_ITEM_Y, 72,
  426. X            PANEL_LABEL_IMAGE, panel_button_image(pn, "Quit", 0, pf),
  427. X            PANEL_NOTIFY_PROC, quit,
  428. X            0);
  429. X  window_fit_height(pn);
  430. X  cv = window_create(fr, CANVAS,
  431. X             WIN_WIDTH, N,
  432. X             WIN_HEIGHT, N,
  433. X             WIN_CONSUME_PICK_EVENT, LOC_DRAG,
  434. X             WIN_EVENT_PROC, sel_region,
  435. X             0);
  436. X
  437. X  window_fit(fr);
  438. X  notify_interpose_destroy_func(fr, quit);
  439. X
  440. X  pw = canvas_pixwin(cv);
  441. X  notify_dispatch();        /* To make the next put_coords work */
  442. X  put_coords(0, N-1, N-1, 0);
  443. X
  444. X  bzero((char *) MSet, sizeof(MSet));
  445. X  pw_writebackground(pw, 0, 0, N, N, PIX_SRC | PIX_COLOR(1));
  446. X
  447. X  /* Cannot use window_main_loop() because the notifier can't dispatch
  448. X     events inside a PANEL_NOTIFY_PROC. */
  449. X
  450. X  while (1) {
  451. X    notify_dispatch();
  452. X    if (draw_new_image) {
  453. X      panel_pw_text(pn, 347, 5 + panel_fonthome(bf), PIX_SRC, bf, "Drawing!");
  454. X      compute();
  455. X      panel_pw_text(pn, 347, 5 + panel_fonthome(bf), PIX_SRC, bf, "        ");
  456. X      draw_new_image = FALSE;
  457. X    }
  458. X    usleep(50000);
  459. X  }
  460. X}
  461. X
  462. Xput_coords(ixmin, ixmax, iymin, iymax)
  463. X     int ixmin, ixmax, iymin, iymax;
  464. X{
  465. X  char str[20];
  466. X
  467. X  sprintf(str, "%10.7lf", xmin + side * ixmin/(N-1));
  468. X  panel_pw_text(pn, 50, 100 + panel_fonthome(pf), PIX_SRC, pf, str);
  469. X  sprintf(str, "%10.7lf", xmin + side * ixmax/(N-1));
  470. X  panel_pw_text(pn, 245, 100 + panel_fonthome(pf), PIX_SRC, pf, str);
  471. X  sprintf(str, "%10.7lf", ymin + side * (N-1-iymin)/(N-1));
  472. X  panel_pw_text(pn, 50, 120 + panel_fonthome(pf), PIX_SRC, pf, str);
  473. X  sprintf(str, "%10.7lf", ymin + side * (N-1-iymax)/(N-1));
  474. X  panel_pw_text(pn, 245, 120 + panel_fonthome(pf), PIX_SRC, pf, str);
  475. X}
  476. X
  477. Xvoid sel_region(canvas, event, arg)
  478. X     Canvas canvas;
  479. X     Event *event;
  480. X     char *arg;
  481. X{
  482. X  static Bool mouseing = FALSE;
  483. X  register int maxdist, tmpx, tmpy;
  484. X
  485. X#define mkbox(a,b,c,d) \
  486. X  pw_vector(pw, a, b, c, b, PIX_SRC^PIX_DST, 1);\
  487. X  pw_vector(pw, a, b, a, d, PIX_SRC^PIX_DST, 1);\
  488. X  pw_vector(pw, c, b, c, d, PIX_SRC^PIX_DST, 1);\
  489. X  pw_vector(pw, a, d, c, d, PIX_SRC^PIX_DST, 1)
  490. X
  491. X  switch (event_action(event)) {
  492. X    case MS_LEFT:
  493. X      if (event_is_down(event)) {
  494. X    if (selected) {        /* Remove old box first */
  495. X      mkbox(start_x, start_y, new_x, new_y);
  496. X    }
  497. X    start_x = new_x = event_x(event);
  498. X    start_y = new_y = event_y(event);
  499. X    put_coords(new_x, new_x, new_y, new_y);
  500. X    mouseing = TRUE;
  501. X      } else {
  502. X    mouseing = FALSE;
  503. X    selected = TRUE;
  504. X      }
  505. X      break;
  506. X    case LOC_DRAG:
  507. X      if (mouseing) {
  508. X    mkbox(start_x, start_y, new_x, new_y); /* Remove old box */
  509. X
  510. X    /* We want to restrict the size to be square */
  511. X    tmpx = event_x(event) - start_x;
  512. X    tmpy = start_y - event_y(event);
  513. X    maxdist = MIN(tmpx * sgn(tmpx), tmpy * sgn(tmpy));
  514. X    new_x = start_x + maxdist * sgn(tmpx);
  515. X    new_y = start_y - maxdist * sgn(tmpy);
  516. X
  517. X    mkbox(start_x, start_y, new_x, new_y); /* Draw new box */
  518. X    put_coords(MIN(start_x, new_x), MAX(start_x, new_x),
  519. X           MAX(start_y, new_y), MIN(start_y, new_y));
  520. X      }
  521. X      break;
  522. X    case MS_MIDDLE:
  523. X      if (selected) {
  524. X    mkbox(start_x, start_y, new_x, new_y);
  525. X    selected = FALSE;
  526. X      }
  527. X    }
  528. X}
  529. X
  530. Xvoid stop()
  531. X{
  532. X  abort = TRUE;
  533. X}
  534. X
  535. Xvoid up()
  536. X{
  537. X  if (sp > 0) {
  538. X    Pixrect *old;
  539. X    register int i, j, k;
  540. X    Bool *mem;
  541. X
  542. X    selected = FALSE;
  543. X    sp--;
  544. X    xmin = Stack[sp].xmin;
  545. X    xmax = Stack[sp].xmax;
  546. X    ymin = Stack[sp].ymin;
  547. X    ymax = Stack[sp].ymax;
  548. X    side = xmax - xmin;
  549. X    put_coords(0, N-1, N-1, 0);
  550. X    old = Stack[sp].pr;
  551. X    pw_write(pw, 0, 0, N, N, PIX_SRC, old, 0, 0);
  552. X
  553. X    /* Restore MSet */
  554. X    /* This is ugly - I'm assuming that sizeof(Bool) == 1. Shame on me! */
  555. X    mem = (Bool *) mpr_d(old)->md_image;
  556. X    for (i=0; i<N; i++) {
  557. X      for (j=0; j<N; j+=8) {
  558. X    for (k=0; k<8; k++)
  559. X      MSet[j+k][N-1-i] = ((*mem & (1<<(7-k))) >> (7-k)) == 0 ? 1 : 0;
  560. X    mem++;
  561. X      }
  562. X    }
  563. X    pr_destroy(old);
  564. X  }
  565. X}
  566. X    
  567. Xvoid draw()
  568. X{
  569. X  draw_new_image = TRUE;
  570. X}
  571. X
  572. Xvoid quit()
  573. X{
  574. X#ifdef oldSunOS
  575. X  exit(0);            /* You won't miss the following fancy stuff.. */
  576. X#else
  577. X  if (alert_prompt(fr, (Event *)0,
  578. X           ALERT_MESSAGE_STRINGS,
  579. X             "Please confirm:",
  580. X             "Do you know what you're doing??",
  581. X             0,
  582. X           ALERT_BUTTON_YES, "Of course, quit bugging me!",
  583. X           ALERT_BUTTON_NO, "Sorry, I hit the wrong button...",
  584. X           ALERT_MESSAGE_FONT, bf,
  585. X           ALERT_BUTTON_FONT, pf,
  586. X           0)
  587. X      == ALERT_YES) {
  588. X    exit(0);
  589. X  } else
  590. X    notify_veto_destroy(fr);
  591. X#endif
  592. X}
  593. X
  594. Xcompute()
  595. X{
  596. X  register int ix, iy;
  597. X
  598. X  if (selected && start_x != new_x) { /* New region selected */
  599. X    Pixrect *save = mem_create(N, N, 1);
  600. X    mkbox(start_x, start_y, new_x, new_y); /* Remove the box first */
  601. X    pw_read(save, 0, 0, N, N, PIX_SRC, pw, 0, 0);
  602. X    Stack[sp].pr = save;
  603. X    Stack[sp].xmin = xmin;
  604. X    Stack[sp].xmax = xmax;
  605. X    Stack[sp].ymin = ymin;
  606. X    Stack[sp].ymax = ymax;
  607. X    if (sp < Stacksize) sp++;    /* Hard to imagine this happen, but... */
  608. X    bzero((char *) MSet, sizeof(MSet));
  609. X    pw_writebackground(pw, 0, 0, N, N, PIX_SRC | PIX_COLOR(1));
  610. X
  611. X    xmax = xmin + side * MAX(start_x, new_x) /(N-1);
  612. X    xmin += side * MIN(start_x, new_x) /(N-1);
  613. X    ymax = ymin + side * (N-1-MIN(start_y, new_y)) /(N-1);
  614. X    ymin += side * (N-1-MAX(start_y, new_y)) /(N-1);
  615. X    selected = FALSE;
  616. X  } else {
  617. X    /* No region selected, just redraw. Perhaps using new parameters. */
  618. X    put_coords(0, N-1, N-1, 0);
  619. X  }
  620. X
  621. X  rec = (int) panel_get_value(recur_p);
  622. X  incr = (int) panel_get_value(incr_p);
  623. X  maxiter = (int) panel_get_value(maxiter_p);
  624. X
  625. X  side = xmax - xmin;
  626. X  delta = 0.25 * side / (N-1);    /* 0.25 seems OK */
  627. X  recur = rec * delta;
  628. X
  629. X  abort = FALSE;
  630. X
  631. X/*************************************************************************/
  632. X/*************************************************************************/
  633. X
  634. X/* From now on, you will find the new Mandelbrot algorithm. No Sun specific
  635. X   stuff, except the notify_dispatch() and some pw_put() and pw_line(). */
  636. X
  637. X  for (iy = 0; iy < N; iy += incr) {
  638. X    notify_dispatch();        /* Allow user to hit the STOP button */
  639. X    if (abort) break;
  640. X    for (ix = 0; ix < N; ix += incr)
  641. X      if (!MSet[ix][iy])
  642. X    MDisk(ix,iy);
  643. X  }
  644. X}
  645. X
  646. XMDisk(ix, iy)
  647. X     register int ix, iy;
  648. X{
  649. X  register double cx, cy, dist;
  650. X  register int irad;
  651. X
  652. X  if (ix<0 || ix>=N || iy<0 || iy>=N || MSet[ix][iy]) return;
  653. X
  654. X  cx = xmin + (side * ix) / (N-1);
  655. X  cy = ymin + (side * iy) / (N-1);
  656. X  dist = 0.25 * MSetDist(cx, cy, maxiter);
  657. X  irad = dist / side * (N-1);    /* Bug in the original algorithm */
  658. X
  659. X  if (irad == 1) {
  660. X    MSet[ix][iy] = 1;
  661. X    pw_put(pw, ix, N-1-iy, 0);    /* Sun specific */
  662. X  } else if (irad > 1)
  663. X    FILLDISK(ix, iy, irad);
  664. X  else if (dist > delta) {
  665. X    MSet[ix][iy] = 1;
  666. X    pw_put(pw, ix, N-1-iy, 0);    /* Sun specific */
  667. X  }
  668. X
  669. X  if (dist > recur) {
  670. X    if (irad > 1) irad++;
  671. X    MDisk(ix, iy + irad);
  672. X    MDisk(ix, iy - irad);
  673. X    MDisk(ix + irad, iy);
  674. X    MDisk(ix - irad, iy);
  675. X
  676. X/* It will be slightly faster if I leave out the following "45-degree"
  677. X   recursions. The reason is that most of these points will probably
  678. X   be filled already and MDisk will return immediately. But since
  679. X   they are in the original algorithm and the improvement is only marginal
  680. X   I will leave them here. */
  681. X
  682. X    irad = 0.5 + irad / SQRT_2;
  683. X    MDisk(ix + irad, iy + irad);
  684. X    MDisk(ix - irad, iy - irad);
  685. X    MDisk(ix - irad, iy + irad);
  686. X    MDisk(ix + irad, iy - irad);
  687. X  }
  688. X}
  689. X
  690. Xdouble MSetDist(cx, cy, maxiter)
  691. X     register double cx, cy;
  692. X     register int maxiter;
  693. X{
  694. X# define overflow 10e10        /* Don't know if this is foolproof */
  695. X
  696. X  register int iter=0;
  697. X  register double zx, zy, zx2, zy2;
  698. X  register double *xorbit, *yorbit;
  699. X
  700. X  /* Could use a static array for this, if you don't have alloca */
  701. X  xorbit = (double *) alloca(maxiter * sizeof(double));
  702. X  yorbit = (double *) alloca(maxiter * sizeof(double));
  703. X
  704. X  /* This is the standard Mandelbrot iteration */
  705. X  zx = zy = zx2 = zy2 = xorbit[0] = yorbit[0] = 0.0;
  706. X  do {
  707. X    zy = (zx * zy) + (zx * zy) + cy; /* gcc generates only one mult for this */
  708. X    zx = zx2 - zy2 + cx;
  709. X    iter++;
  710. X    xorbit[iter] = zx;        /* Save the iteration orbits for later */
  711. X    yorbit[iter] = zy;
  712. X    zx2 = zx * zx;
  713. X    zy2 = zy * zy;
  714. X  } while ((zx2 + zy2) < 1000.0 && iter<maxiter);
  715. X
  716. X  if (iter < maxiter) {        /* Generate derivatives */
  717. X    register double zpx, zpy, tmp;
  718. X    register int i;
  719. X
  720. X    zpx = zpy = 0.0;
  721. X
  722. X    for (i=0; i<iter; i++) {
  723. X      tmp = 2 * (xorbit[i] * zpx - yorbit[i] * zpy) + 1.0;
  724. X      zpy = 2 * (xorbit[i] * zpy + yorbit[i] * zpx);
  725. X      zpx = tmp;
  726. X      if (fabs(zpx) > overflow || fabs(zpy) > overflow)
  727. X    return 0.0;
  728. X    }
  729. X    /* This is the distance estimation */
  730. X    return log(zx2 + zy2) * sqrt(zx2 + zy2) / sqrt(zpx*zpx + zpy*zpy);
  731. X  }
  732. X  return 0.0;
  733. X}
  734. X
  735. XFILLDISK(ix, iy, irad)
  736. X     register int ix, iy;
  737. X     int irad;
  738. X{
  739. X  register int x, y, e;
  740. X
  741. X  /* The "Mini"-algorithm. Perhaps I should use display locking around the
  742. X     plotline's, but after all, the fun is watching it work... */
  743. X
  744. X  x = 0;
  745. X  y = irad;
  746. X  e = irad / 2;
  747. X  while (x <= y) {
  748. X    plotline(ix - x, ix + x, iy + y);
  749. X    plotline(ix - y, ix + y, iy + x);
  750. X    plotline(ix - x, ix + x, iy - y);
  751. X    plotline(ix - y, ix + y, iy - x);
  752. X    e -= x;
  753. X    if (e < 0) {
  754. X      e += y;
  755. X      y--;
  756. X    }
  757. X    x++;
  758. X  }
  759. X}
  760. X
  761. Xplotline(x1, x2, y)
  762. X     register int x1, x2, y;
  763. X{
  764. X  register int i;
  765. X  if (y<0 || y>N-1 || (x1<0 && x2<0) || (x1>=N-1 && x2 >=N-1)) return;
  766. X
  767. X  if (x1 < 0) x1 = 0;
  768. X  if (x1 > N-1) x1 = N-1;
  769. X  if (x2 < 0) x2 = 0;
  770. X  if (x2 > N-1) x2 = N-1;
  771. X
  772. X  pw_vector(pw, x1, N-1-y, x2, N-1-y, PIX_SRC, 0); /* Sun specific */
  773. X
  774. X  for (i=x1; i<=x2; i++)
  775. X    MSet[i][y] = 1;
  776. X}
  777. SHAR_EOF
  778. chmod 0644 FastMtool.c || echo "restore of FastMtool.c fails"
  779. fi
  780. exit 0
  781.  
  782.  
  783.