home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume6 / fastmtool.sun < prev    next >
Text File  |  1989-03-07  |  25KB  |  768 lines

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