home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-385-Vol-1of3.iso / x / xscm105.zip / xscm / xmandel.scm < prev    next >
Text File  |  1992-08-29  |  17KB  |  511 lines

  1. #! /usr/local/bin/xmscm
  2. ;
  3. ; $Header: /home/campbell/Languages/Scheme/scm/x-scm/RCS/xmandel.scm,v 1.7 1992/08/18 00:29:38 campbell Beta $
  4. ;
  5. ; Sample xmscm program for computing and displaying a Mandelbrot set
  6. ; (actually, the points _near_ the Mandelbrot set;  points in the set
  7. ; itself come out black).
  8. ;
  9. ;  Author: Larry Campbell (campbell@redsox.bsw.com)
  10. ;  Copyright 1992 by The Boston Software Works, Inc.
  11. ;  Permission to use for any purpose whatsoever granted, as long
  12. ;  as this copyright notice remains intact.  Please send bug fixes
  13. ;  or enhancements to the above email address.
  14.  
  15. (require 'format)
  16.  
  17. (require 'x11)
  18. (require 'xt)
  19. (require 'xm)
  20. (require 'xmsubs)
  21. (require 'xevent)
  22.  
  23. (define call/cc call-with-current-continuation)        ; save typing
  24.  
  25. (define origin '())            ; center of area being drawn
  26. (define depth '())            ; how many iterations before giving up
  27. (define width 0)            ; width in pixels of drawing area
  28. (define height 0)            ; height in pixels of drawing area
  29. (define magnification '())        ; how much to magnify (zoom in)
  30. (define pixmap '())            ; pixmap into which we draw
  31. (define continuation '())        ; where the computation left off
  32. (define work-proc-registered #f)    ; whether a work proc is registered
  33. (define window-origin '())        ; coordinates of upper left corner
  34. (define quantum '())            ; how much real space each pixel represents
  35. (define ncolors 16)            ; how many colors to use
  36.  
  37.  
  38. ; Define widgets
  39.  
  40. (define top-level
  41.   (if (defined? vs:top-level)
  42.       (xt:app-create-shell "xmandel" "Xmandel"
  43.                xt:application-shell
  44.                (xt:display vs:top-level))
  45.       (xt:initialize "xmandel" "Xmandel")))
  46.  
  47. (xt:set-values top-level xm:n-allow-shell-resize #t)
  48.  
  49. (define panel
  50.   (xt:create-managed-widget
  51.    "top" xm:form top-level))
  52.  
  53. (define controls
  54.   (xt:create-managed-widget
  55.    "control" xm:form panel
  56.    xm:n-left-attachment xm:attach-form
  57.    xm:n-top-attachment xm:attach-form
  58.    xm:n-bottom-attachment xm:attach-form))
  59.  
  60. (define button-frame
  61.   (xt:create-managed-widget
  62.    "button-frame" xm:frame controls
  63.    xm:n-left-attachment xm:attach-form
  64.    xm:n-right-attachment xm:attach-form
  65.    xm:n-top-attachment xm:attach-form))
  66.  
  67. (define button-box
  68.   (xt:create-managed-widget
  69.    "button-box" xm:row-column button-frame
  70.    xm:n-orientation xm:vertical
  71.    xm:n-num-columns 2
  72.    xm:n-packing xm:pack-column))
  73.  
  74. (define reset-button
  75.   (make-button
  76.    "Reset" button-box
  77.    (lambda (w)
  78.      (origin-object 'set origin)
  79.      (magnification-object 'set magnification)
  80.      (depth-object 'set depth)
  81.      (xt:set-values restart-button xm:n-sensitive #f))))
  82.  
  83. (define restart-button
  84.   (make-button
  85.    "Restart" button-box
  86.    (lambda (w)
  87.      (resize-handler drawing-area)
  88.      #t)))
  89.  
  90. (define (value-change-handler w)
  91.   (xt:set-values
  92.    restart-button
  93.    xm:n-sensitive
  94.    (not
  95.     (and
  96.      (= origin (origin-object 'get))
  97.      (= depth (depth-object 'get))
  98.      (= magnification (magnification-object 'get))))))
  99.  
  100. (define paused #f)
  101.  
  102. (define pause-button
  103.   (make-toggle-button
  104.    "Pause" button-box
  105.    (lambda (w)
  106.      (let ((old-paused paused))
  107.        (set! paused (xt:get-value w xm:n-set xt:boolean))
  108.        (if (and old-paused (not paused))
  109.        (register-work-proc))))
  110.    xm:n-shadow-thickness 2))
  111.  
  112. (define exit-button
  113.   (make-button
  114.    "Exit" button-box
  115.    (lambda (w)
  116.      (set! continuation '())
  117.      (if (defined? vs:top-level)
  118.      (xt:unmap-widget top-level)
  119.      (quit)))))
  120.  
  121. (define param-frame
  122.   (xt:create-managed-widget
  123.    "param-frame" xm:frame controls
  124.    xm:n-left-attachment xm:attach-form
  125.    xm:n-right-attachment xm:attach-form
  126.    xm:n-top-attachment xm:attach-widget
  127.    xm:n-top-widget button-frame
  128.    xm:n-bottom-attachment xm:attach-form))
  129.  
  130. (define param-box
  131.   (xt:create-managed-widget
  132.    "param-box" xm:row-column param-frame
  133.    xm:n-orientation xm:vertical))
  134.  
  135. ; This function creates an origin object, consisting of two sliders (one
  136. ; for the imaginary axis and one for the real axis), some state variables,
  137. ; and a method dispatch function.  The object responds to three messages:
  138. ;
  139. ;  (origin-object 'get)        returns complex origin defined by sliders
  140. ;  (origin-object 'set o)    sets sliders to specified origin
  141. ;  (origin-object 'rescale w h)    rescales sliders so they both appear in
  142. ;                 the middle and so the sliders exactly span
  143. ;                 the specified range (which is typically the
  144. ;                 drawing area)
  145. ;
  146. (define (make-origin)
  147.   (let* ((digits 3)
  148.      (mult (expt 10 digits))
  149.      (widget-value
  150.       (lambda (value)
  151.         (inexact->exact (round (* mult value)))))
  152.      (x-widget
  153.       (xt:create-managed-widget
  154.        "origin-x" xm:scale param-box
  155.        xm:n-orientation xm:horizontal
  156.        xm:n-minimum (widget-value -2)
  157.        xm:n-maximum (widget-value 2)
  158.        xm:n-value 0
  159.        xm:n-decimal-points digits
  160.        xm:n-show-value #t
  161.        xm:n-title-string (xm:string-create "Real origin")))
  162.      (y-widget
  163.       (xt:create-managed-widget
  164.        "origin-y" xm:scale param-box
  165.        xm:n-orientation xm:horizontal
  166.        xm:n-minimum (widget-value -2)
  167.        xm:n-maximum (widget-value 2)
  168.        xm:n-value 0
  169.        xm:n-decimal-points digits
  170.        xm:n-show-value #t
  171.        xm:n-title-string (xm:string-create "Imaginary origin"))))
  172.     (letrec
  173.     ((self
  174.       (lambda (selector . args)
  175.         (case selector
  176.           ((get)
  177.            (let ((sx
  178.               (/ (xt:get-value x-widget xm:n-value xt:integer) mult))
  179.              (sy
  180.               (/ (xt:get-value y-widget xm:n-value xt:integer) mult)))
  181.          (make-rectangular sx sy)))
  182.           ((set)
  183.            (let ((x (real-part (car args)))
  184.              (y (imag-part (car args))))
  185.          (xt:set-values x-widget xm:n-value (widget-value x))
  186.          (xt:set-values y-widget xm:n-value (widget-value y)))
  187.            (value-change-handler x-widget)
  188.            (value-change-handler y-widget))
  189.           ((rescale)
  190.            (let* ((real-width (car args))
  191.               (real-height (cadr args))
  192.               (origin (self 'get))
  193.               (ox (real-part origin))
  194.               (oy (imag-part origin)))
  195.          (xt:set-values
  196.           x-widget xm:n-minimum (widget-value (- ox (/ real-width 2))))
  197.          (xt:set-values
  198.           x-widget xm:n-maximum (widget-value (+ ox (/ real-width 2))))
  199.          (xt:set-values
  200.           y-widget xm:n-minimum (widget-value (- oy (/ real-width 2))))
  201.          (xt:set-values
  202.           y-widget xm:n-maximum (widget-value (+ oy (/ real-width 2))))))
  203.           (else (error "invalid origin method" selector))))))
  204.       (xt:add-callback x-widget xm:n-value-changed-callback value-change-handler)
  205.       (xt:add-callback y-widget xm:n-value-changed-callback value-change-handler)
  206.       self)))
  207.  
  208. (define origin-object (make-origin))
  209.  
  210. ; This function creates a magnification object, which consists of a slider and
  211. ; a get method.
  212. ;
  213. (define (make-magnification initial)
  214.   (let* ((digits 4)
  215.      (mult (expt 10 digits))
  216.      (widget-value
  217.       (lambda (value)
  218.         (inexact->exact (round (* mult value)))))
  219.      (widget
  220.       (xt:create-managed-widget
  221.        "magnification" xm:scale param-box
  222.        xm:n-orientation xm:horizontal
  223.        xm:n-minimum (inexact->exact (* .1  mult))
  224.        xm:n-maximum (inexact->exact (* 40 mult))
  225.        xm:n-value (widget-value initial)
  226.        xm:n-decimal-points digits
  227.        xm:n-show-value #t
  228.        xm:n-title-string (xm:string-create "Magnification"))))
  229.     (xt:add-callback widget xm:n-value-changed-callback value-change-handler)
  230.     (lambda (selector . args)        ; args not (yet) used
  231.       (case selector
  232.     ((get) (/ (xt:get-value widget xm:n-value xt:integer) mult))
  233.     ((set) (xt:set-values widget xm:n-value (widget-value (car args))))
  234.     (else (error "invalid origin method" selector))))))
  235.  
  236. (define magnification-object (make-magnification .1))
  237.  
  238. ; This function creates and returns a depth object, which consists of a slider
  239. ; and a get method.
  240. ;
  241. (define (make-depth initial)
  242.   (let* ((widget
  243.       (xt:create-managed-widget
  244.        "depth" xm:scale param-box
  245.        xm:n-orientation xm:horizontal
  246.        xm:n-minimum 1
  247.        xm:n-maximum 200
  248.        xm:n-value initial
  249.        xm:n-decimal-points 0
  250.        xm:n-show-value #t
  251.        xm:n-title-string (xm:string-create "Depth"))))
  252.     (xt:add-callback widget xm:n-value-changed-callback value-change-handler)
  253.     (lambda (selector . args)        ; args not (yet) used
  254.       (case selector
  255.     ((get) (xt:get-value widget xm:n-value xt:integer))
  256.     ((set) (xt:set-values widget xm:n-value (car args)))
  257.     (else (error "invalid origin method" selector))))))
  258.  
  259. (define depth-object (make-depth 20))
  260.                 
  261. (define drawing-frame
  262.   (xt:create-managed-widget
  263.    "frame" xm:frame panel))
  264.  
  265. (define drawing-area
  266.   (xt:create-managed-widget
  267.    "drawing-area" xm:drawing-area drawing-frame))
  268.  
  269. (xt:set-values
  270.  drawing-frame
  271.  xm:n-top-attachment xm:attach-form
  272.  xm:n-bottom-attachment xm:attach-form
  273.  xm:n-right-attachment xm:attach-form
  274.  xm:n-left-attachment xm:attach-widget
  275.  xm:n-left-widget controls)
  276.  
  277. (xt:realize-widget top-level)
  278.  
  279. (define xwindow (xt:window drawing-area))
  280. (define xdisplay (xt:display drawing-area))
  281. (define xgc1 (x:create-gc xdisplay '() x:gc-foreground 0 x:gc-background 1))
  282. (define    xgc2 (x:create-gc xdisplay '() x:gc-foreground 1 x:gc-background 0))
  283. (define display-colors (x:display-cells xdisplay 0))
  284.  
  285.  
  286. ;;; The cursor in the drawing area is a cross-hair.  If the user presses
  287. ;;; MB2 in the drawing area, we track motion events (until MB2 is released)
  288. ;;; and force the origin sliders to the point the cursor is on.
  289.  
  290. (x:define-cursor xdisplay (xt:window drawing-area) xc:crosshair)
  291.  
  292. (xt:add-event-handler
  293.  drawing-area x:button-press-mask 0
  294.  (lambda (widget event)
  295.    (let ((button (x:get-event-field event x:button-event:button)))
  296.      (if (= button 2)
  297.      (let* ((x (x:get-event-field event x:button-event:x))
  298.         (y (x:get-event-field event x:button-event:y))
  299.         (button-origin
  300.          (make-rectangular (+ (real-part window-origin)
  301.                       (* quantum x))
  302.                    (- (imag-part window-origin)
  303.                       (* quantum y))))
  304.          (tracker
  305.           (lambda (widget event)
  306.             (let* ((x (x:get-event-field event x:motion-event:x))
  307.                (y (x:get-event-field event x:motion-event:y))
  308.                (new-origin
  309.                 (make-rectangular (+ (real-part window-origin)
  310.                          (* quantum x))
  311.                           (- (imag-part window-origin)
  312.                          (* quantum y)))))
  313.               (origin-object 'set new-origin)))))
  314.        (origin-object 'set button-origin)
  315.        (xt:add-event-handler drawing-area x:pointer-motion-mask 0 tracker)
  316.        (xt:add-event-handler
  317.         drawing-area x:button-release-mask 0
  318.         (lambda (widget event)
  319.           (let ((button (x:get-event-field event x:button-event:button)))
  320.         (if (= button 2)
  321.             (xt:remove-event-handler
  322.              drawing-area x:pointer-motion-mask 0 tracker))))))))))
  323.           
  324. (xt:set-values panel xm:n-width 600 xm:n-height 400)
  325.  
  326. (define cmap (x:default-colormap xdisplay 0))
  327. (define private-colormap #f)
  328.  
  329. (define planes-n-colors
  330.   (x:alloc-color-cells xdisplay cmap #t 0 ncolors))
  331.  
  332. (if (not planes-n-colors)        ; if we couldn't allocate enuf cells
  333.     (begin
  334.       (set! cmap (x:create-colormap xdisplay (xt:window drawing-area) 0))
  335.       (set! planes-n-colors (x:alloc-color-cells xdisplay cmap #t 0 ncolors))
  336.       (set! private-colormap #t)))
  337.  
  338. (if (not planes-n-colors)
  339.     (error "Failed utterly to allocate required 16 colors"))
  340.  
  341. (define base-pixel (car (reverse (cadr planes-n-colors))))
  342.  
  343. (let ((i base-pixel))
  344.   (for-each
  345.    (lambda (item)
  346.      (let ((red (car item))
  347.        (green (cadr item))
  348.        (blue (caddr item)))
  349.        (x:store-color xdisplay cmap i red green blue)
  350.        (set! i (1+ i))))
  351.    '((    0     0     0)        ; colors - edit to taste (there
  352.      (60000     0 65000)        ; must be ncolors entries though)
  353.      (40000     0 60000)
  354.      (20000     0 55000)
  355.      (15000     0 50000)
  356.      (10000     0 45000)
  357.      ( 8000     0 40000)
  358.      ( 5000     0 35000)
  359.      ( 1000     0 30000)
  360.      (  500     0 25000)
  361.      (    0     0 20000)
  362.      (    0     0 15000)
  363.      (    0     0 10000)
  364.      (    0     0  8000)
  365.      (    0     0  6000)
  366.      (    0     0  4000))))
  367.  
  368. (if private-colormap
  369.     (xt:add-event-handler
  370.      drawing-area x:enter-window-mask 0
  371.      (lambda (widget event)
  372.        (x:install-colormap xdisplay cmap)
  373.        (xt:add-event-handler
  374.     drawing-area x:leave-window-mask 0
  375.     (lambda (widget event)
  376.       (x:install-colormap
  377.        xdisplay
  378.        (x:default-colormap xdisplay 0)))))))
  379.  
  380. ; The real (compute-intensive) work of computing the points to draw
  381. ; is performed in a work procedure called by Xt and registered with
  382. ; xt:add-work-proc (XtAddWorkProc).  The global variable "continuation"
  383. ; contains a continuation for the initiation  or resumption of this
  384. ; computation.  The work procedure calls compute-set (the first time)
  385. ; using call/cc and passing a continuation by which the Xt main loop
  386. ; can be resumed (so the program still handles user input).  compute-set
  387. ; computes for a while (currently 16 points) and then calls the
  388. ; continuation of the work proc with call/cc;   the work proc saves
  389. ; this continuation and the work proc resumes it each time it's
  390. ; called.  When compute-set finishes, it returns #t, which instructs
  391. ; the work proc to return #f, which instructs Xt to deregister it.
  392. ;
  393. ; There is also a global "paused" flag, which can be turned on by
  394. ; clicking a pause button -- useful if the machine's bogging down
  395. ; and you want to quit computing for a while.
  396.  
  397. (define (register-work-proc)
  398.   (xt:add-work-proc work-proc)
  399.   (set! work-proc-registered #t))
  400.  
  401. (define (work-proc)
  402.   (cond ((null? continuation)            ; computing not yet started
  403.      (set! continuation (call/cc compute-set))
  404.      #f)
  405.     ((or paused (eqv? #t continuation))    ; computing finished or paused
  406.      (set! work-proc-registered #f)
  407.      #t)
  408.     (else                    ; computing in progress
  409.      (continuation '())
  410.      #f)))
  411.  
  412. ; To speed things up, we just compute points and store them by color in a vector,
  413. ; drawing the points and emptying the vector at the end of each row.
  414. ;
  415. (define (compute-set contin)
  416.   (set! origin (origin-object 'get))
  417.   (set! depth (depth-object 'get))
  418.   (set! magnification (magnification-object 'get))
  419.   (set! quantum (/ 1 (* (min width height) magnification)))
  420.   (let* ((lastcolor '())
  421.      (real-width (* width quantum))
  422.      (real-height (* height quantum))
  423.      (x-increment (make-rectangular quantum 0))
  424.      (y-increment (make-rectangular 0 quantum))
  425.      (points (make-vector ncolors '()))
  426.      (complex-zero (make-rectangular 0 0)))
  427.     (set! window-origin (make-rectangular
  428.              (- (real-part origin) (/ real-width 2))
  429.              (+ (imag-part origin) (/ real-height 2))))
  430.     (origin-object 'rescale real-width real-height)
  431.     (do ((y 0 (1+ y))
  432.      (k0 window-origin (- k0 y-increment)))
  433.     ((=? y height) #t)
  434.       (do ((x 0 (1+ x))
  435.        (k k0 (+ k x-increment)))
  436.       ((=? x width) #t)
  437.     (let ((z complex-zero))
  438.       (do ((i 0 (1+ i)))
  439.           ((or (= i depth)
  440.            (>= (magnitude z) 4))
  441.            (let ((color
  442.               (modulo
  443.                (inexact->exact (truncate (magnitude z)))
  444.                ncolors))
  445.              (point (cons x y)))
  446.          (vector-set!
  447.           points color (cons point (vector-ref points color))))
  448.            #t)
  449.         (let ((term (+ z k)))
  450.           (set! z (* term term))))
  451.       (if (zero? (modulo x 16))        ; every 16 points, let XtMainLoop run
  452.           (call/cc contin))))
  453.       (do ((i 0 (1+ i)))            ; end of row, draw saved points
  454.       ((= i ncolors) #t)
  455.     (if (not (null? (vector-ref points i)))
  456.         (begin
  457.           (x:set-foreground xdisplay xgc2 (+ base-pixel i))
  458.           (if (xt:is-realized drawing-area)
  459.           (apply
  460.            x:draw-points
  461.            `(,xdisplay
  462.              ,(xt:window drawing-area)
  463.              ,xgc2 ,x:coord-mode-origin
  464.              ,@(vector-ref points i))))
  465.           (apply
  466.            x:draw-points
  467.            `(,xdisplay
  468.          ,pixmap ,xgc2 ,x:coord-mode-origin
  469.          ,@(vector-ref points i)))
  470.           (vector-set! points i '())))))))
  471.  
  472. ; The resize handler allocates a new pixmap of the correct size and
  473. ; restarts the computation.
  474. ;
  475. (define (resize-handler w)
  476.   (set! height (xt:get-value w xt:n-height xt:unsigned-short))
  477.   (set! width (xt:get-value w xt:n-width xt:unsigned-short))
  478.   (if (not (null? pixmap))
  479.       (x:free-pixmap xdisplay pixmap))
  480.   (set! pixmap
  481.     (x:create-pixmap
  482.      xdisplay '() width height
  483.      (x:display-depth xdisplay 0)))
  484.   (x:fill-rectangle xdisplay pixmap xgc1 0 0 width height)
  485.   (x:clear-area xdisplay xwindow 0 0 0 0 #t)
  486.   (xt:set-values restart-button xm:n-sensitive #f)
  487.   (set! continuation '())
  488.   (if (not work-proc-registered)
  489.       (register-work-proc)))
  490.  
  491. (resize-handler drawing-area)
  492.  
  493. ; The expose handler just copies from the pixmap onto the window
  494. ;
  495. (define (exposure-handler widget e)
  496.   (let ((x (x:get-event-field e x:expose-event:x))
  497.     (y (x:get-event-field e x:expose-event:y))
  498.     (w (x:get-event-field e x:expose-event:width))
  499.     (h (x:get-event-field e x:expose-event:height)))
  500.     (x:copy-area xdisplay pixmap (xt:window widget)
  501.          xgc1 x y w h x y)))
  502.  
  503. (xt:add-event-handler drawing-area x:exposure-mask 0 exposure-handler)
  504. (xt:add-callback drawing-area xm:n-resize-callback resize-handler)
  505.  
  506. (register-work-proc)
  507.  
  508. (if (not (defined? vs:top-level))
  509.     (xt:main-loop))
  510.