1#lang racket/unit
2
3; Simple graphics routines for GRacket
4; Originally written by Johnathan Franklin
5;
6; modified by Gregory Cooper to support FrTime
7
8(require (for-syntax syntax/parse racket/base)
9         racket/class
10         (prefix-in gui: racket/gui/base)
11         frtime/core/frp
12         "graphics-sig.rkt")
13
14(import graphics:posn^)
15(export graphics:posn-less^)
16
17(define-syntax (rec stx)
18  (syntax-parse stx
19                [((~literal rec) var:identifier rhs:expr)
20                 #'(letrec ([var rhs])
21                     var)]))
22
23(define send/proc
24  (lambda (class method . args)
25    (send-generic class (make-generic gui:dc<%> method) . args)))
26
27(define send/proc2
28  (lambda (class method . args)
29    (send-generic class (make-generic sixlib-canvas% method) . args)))
30
31(define-struct viewport (label canvas))
32(define-struct sixmouse (x y left? middle? right?))
33(define-struct sixkey (value shift control meta alt))
34(define graphics-flag #f)
35(define global-viewport-list '())
36(define global-color-vector (make-vector 300 #f))
37(define global-pen-vector (make-vector 300 #f))
38(define global-brush-vector (make-vector 300 #f))
39(define default-font (make-object gui:font% 12 'roman 'normal 'normal))
40(define black-color (make-object gui:color% "BLACK"))
41
42(define sixlib-canvas%
43  (class gui:canvas%
44    ;; were public
45    (define viewport (void))
46    (define height 0)
47    (define width 0)
48    (define label 0)
49    (define current-pen 'uninitialized-pen)
50    (define current-brush 'uninitialized-brush)
51    (define bitmap 'uninitalized-bitmap)
52    (define dc 'uninitialized-dc)
53    (define buffer-dc 'uninitialized-buffer-dc)
54    (super-new)
55    (inherit get-parent
56             min-client-width min-client-height
57             stretchable-width stretchable-height)
58    (define current-mouse-pos (make-posn 0 0))
59    (define mouse-listener #f)
60    (define key-listener #f)
61    (private*
62     [reset-size
63      (lambda ()
64        (min-client-width width)
65        (min-client-height height)
66        (stretchable-width #f)
67        (stretchable-height #f)
68        (set! bitmap (make-object gui:bitmap% width height))
69        (unless (send bitmap ok?)
70          (error "cannot allocate viewport"))
71        (send buffer-dc set-bitmap bitmap)
72        (send buffer-dc set-brush (send dc get-brush))
73        (send buffer-dc set-pen (send dc get-pen))
74        (send buffer-dc set-smoothing 'aligned)
75        (let ([f (send dc get-font)])
76          (when f
77            (send buffer-dc set-font f)))
78        (send buffer-dc clear)
79        (send dc clear))])
80
81
82
83    (public*
84     [get-mouse-listener (lambda () mouse-listener)]
85     [get-key-listener (lambda () key-listener)]
86     [set-mouse-listener (lambda (ml) (set! mouse-listener ml))]
87     [set-key-listener (lambda (kl) (set! key-listener kl))]
88     [get-posn (lambda () current-mouse-pos)]
89     [get-viewport (lambda () viewport)]
90     [set-viewport (lambda (x) (set! viewport x))]
91     [get-sixlib-height (lambda () height)]
92     [get-sixlib-width (lambda () width)]
93     [get-current-pen (lambda () current-pen)]
94     [get-current-brush (lambda () current-brush)]
95     [get-bitmap (lambda () bitmap)]
96     [get-sixlib-dc (lambda () dc)]
97     [get-buffer-dc (lambda () buffer-dc)]
98     [remember-pen (lambda (pen) (set! current-pen pen))]
99     [remember-brush (lambda (brush) (set! current-brush brush))])
100
101    (override*
102     [on-paint
103      (lambda ()
104        (when (object? buffer-dc)
105            (define bm (send buffer-dc get-bitmap))
106            (when bm
107              (send dc draw-bitmap bm 0 0))))]
108
109     [on-event
110      (lambda (mouse-event)
111        (set! current-mouse-pos (make-posn (send mouse-event get-x)
112                                           (send mouse-event get-y)))
113        (send-event mouse-listener mouse-event))]
114     #|
115	  (let* ([x (send mouse-event get-x)]
116		 [y (send mouse-event get-y)]
117		 [left? (send mouse-event button-down? 'left)]
118		 [middle? (send mouse-event button-down? 'middle)]
119		 [right? (send mouse-event button-down? 'right)]
120		 [sixm (make-sixmouse x y left? middle? right?)])
121            (set! current-mouse-pos (make-posn x y))
122            (if mouse-listener
123                (send-event mouse-listener sixm))))]
124|#
125     [on-char
126      (lambda (key-event)
127        (when key-listener
128          (send-event
129           key-listener
130           (make-sixkey
131            (send key-event get-key-code)
132            (send key-event get-shift-down)
133            (send key-event get-control-down)
134            (send key-event get-meta-down)
135            (send key-event get-alt-down)))))])
136
137    (public*
138     [set-dc (lambda (new-dc) (set! dc new-dc))]
139     [set-buffer-dc (lambda (new-buffer-dc) (set! buffer-dc
140                                                  new-buffer-dc))]
141
142     [set-geometry
143      (lambda (new-width new-height)
144        (set! height new-height)
145        (set! width new-width)
146        (reset-size))]
147     [set-height (lambda (new-height)
148                   (set! height new-height)
149                   (reset-size))]
150     [set-width (lambda (new-width)
151                  (set! width new-width)
152                  (reset-size))])))
153
154(define open-frames-timer (make-object gui:timer%))
155
156(define sixlib-frame%
157  (class gui:frame%
158    (field [canvas #f])
159    (define/public (set-canvas x) (set! canvas x))
160    (define/augment (on-close)
161      (close-viewport (send canvas get-viewport))
162      (inner (void) on-close))
163    (super-instantiate ())))
164
165(define (query-mouse-posn viewport)
166  (send (viewport-canvas viewport) get-posn))
167
168(define repaint
169  (lambda (viewport)
170    (send (viewport-canvas viewport) on-paint)))
171
172(define viewport-mouse-events
173  (lambda (viewport)
174    (send (viewport-canvas viewport) get-mouse-listener)))
175
176(define viewport-key-events
177  (lambda (viewport)
178    (send (viewport-canvas viewport) get-key-listener)))
179
180(define viewport-dc
181  (lambda (viewport)
182    (send (viewport-canvas viewport) get-sixlib-dc)))
183
184(define viewport-buffer-dc
185  (lambda (viewport)
186    (send (viewport-canvas viewport) get-buffer-dc)))
187
188(define viewport-bitmap
189  (lambda (viewport)
190    (send (viewport-canvas viewport) get-bitmap)))
191
192(define viewport-frame
193  (lambda (viewport)
194    (send (send (viewport-canvas viewport) get-parent) get-parent)))
195
196(define viewport-height
197  (lambda (viewport)
198    (send (viewport-canvas viewport) get-sixlib-height)))
199
200(define viewport-width
201  (lambda (viewport)
202    (send (viewport-canvas viewport) get-sixlib-width)))
203
204(define clear-viewport
205  (lambda (viewport)
206    (let* ([vdc (viewport-dc viewport)]
207           [vbdc (viewport-buffer-dc viewport)])
208      (lambda ()
209        (send vdc clear)
210        (send vbdc clear)))))
211
212
213
214(define draw-viewport
215  (lambda (viewport)
216    (let* ([dc (viewport-dc viewport)]
217           [buffer-dc (viewport-buffer-dc viewport)]
218           [w (viewport-width viewport)]
219           [h (viewport-height viewport)])
220      (rec draw-viewport/color
221        (case-lambda
222          [(color)
223           (let ([new-pen (send gui:the-pen-list find-or-create-pen color 1 'solid)]
224                 [new-brush (send gui:the-brush-list find-or-create-brush color 'solid)]
225                 [old-pen (send dc get-pen)]
226                 [old-brush (send dc get-brush)])
227             (send dc set-pen new-pen)
228             (send dc set-brush new-brush)
229             (send buffer-dc set-pen new-pen)
230             (send buffer-dc set-brush new-brush)
231             (send dc draw-rectangle 0 0 w h)
232             (send buffer-dc draw-rectangle 0 0 w h)
233             (send dc set-pen old-pen)
234             (send buffer-dc set-pen old-pen)
235             (send dc set-brush old-brush)
236             (send buffer-dc set-brush old-brush))]
237          [() (draw-viewport/color (make-rgb 0 0 0))])))))
238
239(define flip-viewport
240  (lambda (viewport)
241    (let* ([dc (viewport-dc viewport)]
242           [dc2 (viewport-buffer-dc viewport)]
243           [w (viewport-width viewport)]
244           [h (viewport-height viewport)])
245      (lambda ()
246        (let ([pen (send dc get-pen)]
247              [pen2 (send dc2 get-pen)]
248              [brush (send dc get-brush)]
249              [brush2 (send dc2 get-brush)])
250          (send dc set-pen xor-pen)
251          (send dc2 set-pen xor-pen)
252          (send dc set-brush xor-brush)
253          (send dc2 set-brush xor-brush)
254          (send dc draw-rectangle 0 0 w h)
255          (send dc2 draw-rectangle 0 0 w h)
256          (send dc set-pen pen)
257          (send dc2 set-pen pen2)
258          (send dc set-brush brush)
259          (send dc2 set-brush brush2))))))
260
261(define close-viewport
262  (lambda (viewport)
263    (set! global-viewport-list
264          (let loop ([l global-viewport-list])
265            (cond
266              [(null? l) '()]
267              [(eq? (car l) viewport) (cdr l)]
268              [else (cons (car l) (loop (cdr l)))])))
269    (send (viewport-frame viewport) show #f)
270    (send (viewport-canvas viewport) show #f)
271    (when (null? global-viewport-list)
272      (send open-frames-timer stop))))
273
274(define open-graphics
275  (lambda ()
276    (set! graphics-flag #t)))
277
278(define close-graphics
279  (lambda ()
280    (map close-viewport global-viewport-list)
281    (set! graphics-flag #f)
282    (set! global-viewport-list '())
283    (send open-frames-timer stop)))
284
285(define graphics-open? (lambda () graphics-flag))
286
287(define make-rgb
288  (lambda (red green blue)
289    (when (or (< red 0.) (< blue 0.) (< green 0.)
290              (> red 1.) (> blue 1.) (> green 1.))
291      (error 'make-rgb
292             "all color indices should be in [0.0, 1.0]; provided ~s"
293             (list red green blue)))
294    (let* ([convert (lambda (num) (inexact->exact (round (* 255 num))))]
295           [nred (convert red)]
296           [ngreen (convert green)]
297           [nblue (convert blue)])
298      (make-object gui:color% nred ngreen nblue))))
299
300(define make-color make-rgb)
301
302(define (rgb-red rgb) (/ (send rgb red) 255))
303(define (rgb-blue rgb) (/ (send rgb blue) 255))
304(define (rgb-green rgb) (/ (send rgb green) 255))
305
306(define rgb? (lambda (object) (is-a? object gui:color%)))
307(define (color? x)
308  (or (rgb? x)
309      (not (not (send gui:the-color-database find-color x)))))
310
311(define change-color
312  (lambda (index color)
313    (vector-set! global-color-vector index color)
314    (vector-set! global-pen-vector index (get-pen color))
315    (vector-set! global-brush-vector index (get-brush color))))
316
317(define (get-color index)
318  (cond
319    [(is-a? index gui:color%) index]
320    [(string? index) (make-object gui:color% index)]
321    [else (vector-ref global-color-vector index)]))
322
323(define get-pen
324  (lambda (index)
325    (cond
326      [(is-a? index gui:pen%) index]
327      [(or (string? index) (is-a? index gui:color%))
328       (send gui:the-pen-list find-or-create-pen index 1 'solid)]
329      [else (vector-ref global-pen-vector index)])))
330
331(define get-brush
332  (lambda (index)
333    (cond
334      [(is-a? index gui:brush%) index]
335      [(or (string? index) (is-a? index gui:color%))
336       (send gui:the-brush-list find-or-create-brush index 'solid)]
337      [else (vector-ref global-brush-vector index)])))
338
339(define pen? (lambda (object) (is-a? object gui:pen%)))
340(define brush? (lambda (object) (is-a? object gui:brush%)))
341
342(define display-color-vector
343  (lambda ()
344    (do
345        ([index 0 (+ index 1)])
346      ((eq? index 100))
347      (display (list (/ (rgb-red (get-color index)) 255)
348                     (/ (rgb-green (get-color index)) 255)
349                     (/ (rgb-blue (get-color index)) 255))))))
350
351(define make-font
352  (lambda (name)
353    (cond
354      [(eq? name 'large-deco)
355       (make-object gui:font% 40 'decorative 'normal 'normal)]
356      [(eq? name 'small-roman)
357       (make-object gui:font% 12 'roman 'normal 'normal)]
358      [(eq? name 'medium-roman)
359       (make-object gui:font% 24 'roman 'normal 'normal)]
360      [(eq? name 'large-roman)
361       (make-object gui:font% 32 'roman 'normal 'normal)]
362      [else "no such font ~a; only 'large-deco, 'small-roman, 'medium-roman, and 'large-roman"
363            name])))
364
365(define custom-roman
366  (lambda (size)
367    (make-object gui:font%
368      size 'roman 'normal 'normal)))
369
370(define custom-deco
371  (lambda (size)
372    (make-object gui:font% size 'decorative 'normal 'normal)))
373
374(define set-viewport-pen
375  (lambda (viewport pen)
376    (send (viewport-canvas viewport) remember-pen pen)
377    (let ([pen (get-pen pen)])
378      (send (viewport-dc viewport) set-pen pen)
379      (send (viewport-buffer-dc viewport) set-pen pen))))
380
381(define set-viewport-brush
382  (lambda (viewport brush)
383    (send (viewport-canvas viewport) remember-brush brush)
384    (let ([brush (get-brush brush)])
385      (send (viewport-dc viewport) set-brush brush)
386      (send (viewport-buffer-dc viewport) set-brush brush))))
387
388(define set-text-foreground
389  (lambda (viewport color)
390    (let ([color (get-color color)])
391      (send (viewport-dc viewport) set-text-foreground color)
392      (send (viewport-buffer-dc viewport) set-text-foreground color))))
393
394(define set-text-background
395  (lambda (viewport color)
396    (let ([color (get-color color)])
397      (send (viewport-dc viewport) set-text-background color)
398      (send (viewport-buffer-dc viewport) set-text-background color))))
399
400(define set-viewport-font
401  (lambda (viewport font)
402    (send (viewport-dc viewport) set-font font)
403    (send (viewport-buffer-dc viewport) set-font font)))
404
405(define set-viewport-background
406  (lambda (viewport color)
407    (send (viewport-dc viewport) set-background color)
408    (send (viewport-buffer-dc viewport) set-background color)))
409
410(define set-viewport-logical-function
411  (lambda (viewport logical-function)
412    (send (viewport-dc viewport) set-logical-function logical-function)
413    (send (viewport-buffer-dc viewport) set-logical-function
414          logical-function)))
415
416(define white (make-rgb 1 1 1))
417(define black (make-rgb 0 0 0))
418(define red (make-rgb 1 0 0))
419(define green (make-rgb 0 1 0))
420(define blue (make-rgb 0 0 1))
421(define white-pen (get-pen white))
422(define black-pen (get-pen black))
423(define red-pen (get-pen red))
424(define blue-pen (get-pen blue))
425(define green-pen (get-pen green))
426(define white-brush (get-brush white))
427(define black-brush (get-brush black))
428(define red-brush (get-brush red))
429(define green-brush (get-brush green))
430(define blue-brush (get-brush blue))
431
432(define invisi-pen (send gui:the-pen-list find-or-create-pen "WHITE" 0 'transparent))
433(define invisi-brush (send gui:the-brush-list find-or-create-brush "WHITE" 'transparent))
434
435(define xor-pen (send gui:the-pen-list find-or-create-pen "BLACK" 1 'xor))
436(define xor-brush (send gui:the-brush-list find-or-create-brush "BLACK" 'xor))
437
438(define draw-it (lambda (draw flip clear) (draw)))
439(define flip-it (lambda (draw flip clear) (flip)))
440(define clear-it (lambda (draw flip clear) (clear)))
441
442(define make-draw-proc
443  (lambda (get-pen-name set-pen-name
444                        get-current-pen-name set-viewport-pen white-pen)
445    (lambda (viewport)
446      (let* ([vdc (viewport-dc viewport)]
447             [vbdc (viewport-buffer-dc viewport)])
448        (lambda (color go)
449          (let ([orig (and color
450                           (begin0
451                             (send/proc2 (viewport-canvas viewport)
452                                         get-current-pen-name)
453                             (set-viewport-pen viewport (get-color color))))])
454            (go (lambda (draw)
455                  (let ([pen (send vdc get-pen)]
456                        [brush (send vdc get-brush)])
457                    (send vdc set-brush xor-brush)
458                    (send vbdc set-brush xor-brush)
459                    (send vdc set-pen xor-pen)
460                    (send vbdc set-pen xor-pen)
461                    (draw)
462                    (send vdc set-brush brush)
463                    (send vbdc set-brush brush)
464                    (send vdc set-pen pen)
465                    (send vbdc set-pen pen)))
466                (lambda (draw)
467                  (let ([pen (send/proc vdc get-pen-name)])
468                    (send/proc vdc set-pen-name white-pen)
469                    (send/proc vbdc set-pen-name white-pen)
470                    (draw)
471                    (send/proc vdc set-pen-name pen)
472                    (send/proc vbdc set-pen-name pen))))
473            (when orig
474              (set-viewport-pen viewport orig))))))))
475
476(define make-do-line
477  (lambda (go)
478    (let ([f (make-draw-proc 'get-pen 'set-pen
479                             'get-current-pen set-viewport-pen white-pen)])
480      (lambda (viewport)
481        (let ([f (f viewport)])
482          (letrec ([the-function
483                    (case-lambda
484                      [(posn1 posn2) (the-function posn1 posn2 #f)]
485                      [(posn1 posn2 color)
486                       (f color
487                          (lambda (flip clear)
488                            (let* ([x1 (posn-x posn1)]
489                                   [y1 (posn-y posn1)]
490                                   [x2 (posn-x posn2)]
491                                   [y2 (posn-y posn2)]
492                                   [draw (lambda ()
493                                           (send (viewport-dc viewport)
494                                                 draw-line
495                                                 x1 y1 x2 y2)
496                                           (send (viewport-buffer-dc viewport)
497                                                 draw-line
498                                                 x1 y1 x2 y2))])
499                              (go draw
500                                  (lambda () (flip draw))
501                                  (lambda () (clear draw))))))])])
502            the-function))))))
503
504(define draw-line (make-do-line draw-it))
505(define (clear-line viewport)
506  (let ([f ((make-do-line clear-it) viewport)])
507    (rec clear-line-viewport
508      (lambda (p1 p2)
509        (f p1 p2)))))
510(define (flip-line viewport)
511  (let ([f ((make-do-line flip-it) viewport)])
512    (rec flip-line-viewport
513      (lambda (p1 p2)
514        (f p1 p2)))))
515
516(define (draw/clear/flip ivar)
517  (lambda (init-dc viewport p width height)
518    (let ([dc (viewport-dc viewport)]
519          [buffer-dc (viewport-buffer-dc viewport)])
520      (init-dc dc)
521      (init-dc buffer-dc)
522      (send/proc dc ivar (posn-x p) (posn-y p) width height)
523      (send/proc buffer-dc ivar (posn-x p) (posn-y p) width height))))
524
525(define draw/clear/flip-rectangle (draw/clear/flip 'draw-rectangle))
526(define draw/clear/flip-ellipse (draw/clear/flip 'draw-ellipse))
527
528(define (draw-arc viewport)
529  (check-viewport 'draw-arc viewport)
530  (rec draw-arc-viewport
531    (case-lambda
532      [(p width height start-radians end-radians)
533       (draw-arc-viewport p width height start-radians end-radians (make-rgb 0 0 0))]
534      [(p width height start-radians end-radians color)
535       (check 'draw-arc
536              posn? p "posn"
537              number? width "number"
538              number? height "number"
539              number? start-radians "number"
540              number? end-radians "number"
541              (orp color? number?) color "color or index")
542       (let ([dc (viewport-dc viewport)]
543             [buffer-dc (viewport-buffer-dc viewport)])
544         (send dc set-pen (send gui:the-pen-list find-or-create-pen (get-color color) 1 'solid))
545         (send dc set-brush (send gui:the-brush-list find-or-create-brush "BLACK" 'transparent))
546         (send buffer-dc set-pen (send gui:the-pen-list find-or-create-pen (get-color color) 1 'solid))
547         (send buffer-dc set-brush (send gui:the-brush-list find-or-create-brush "BLACK" 'transparent))
548         (send dc draw-arc (posn-x p) (posn-y p) width height start-radians end-radians)
549         (send buffer-dc draw-arc (posn-x p) (posn-y p) width height start-radians end-radians))])))
550
551(define (draw-solid-arc viewport)
552  (check-viewport 'draw-solid-arc viewport)
553  (rec draw-arc-viewport
554    (case-lambda
555      [(p width height start-radians end-radians)
556       (draw-arc-viewport p width height (make-rgb 0 0 0))]
557      [(p width height start-radians end-radians color)
558       (check 'draw-solid-arc
559              posn? p "posn"
560              number? width "number"
561              number? height "number"
562              number? start-radians "number"
563              number? end-radians "number"
564              (orp color? number?) color "color or index")
565       (let ([dc (viewport-dc viewport)]
566             [buffer-dc (viewport-buffer-dc viewport)])
567         (send dc set-pen (send gui:the-pen-list find-or-create-pen (get-color color) 1 'solid))
568         (send dc set-brush (send gui:the-brush-list find-or-create-brush (get-color color) 'solid))
569         (send buffer-dc set-pen (send gui:the-pen-list find-or-create-pen (get-color color) 1 'solid))
570         (send buffer-dc set-brush (send gui:the-brush-list find-or-create-brush (get-color color) 'solid))
571         (send dc draw-arc (posn-x p) (posn-y p) width height start-radians end-radians)
572         (send buffer-dc draw-arc (posn-x p) (posn-y p) width height start-radians end-radians))])))
573
574(define (draw-rectangle viewport)
575  (check-viewport 'draw-rectangle viewport)
576  (rec draw-rectangle-viewport
577    (case-lambda
578      [(p width height) (draw-rectangle-viewport p width height (make-rgb 0 0 0))]
579      [(p width height color)
580       (check 'draw-rectangle
581              posn? p "posn"
582              number? width "number"
583              number? height "number"
584              (orp color? number?) color "color or index")
585       (draw/clear/flip-rectangle
586        (lambda (dc)
587          (send dc set-pen (send gui:the-pen-list find-or-create-pen (get-color color) 1 'solid))
588          (send dc set-brush (send gui:the-brush-list find-or-create-brush "BLACK" 'transparent)))
589        viewport p width height)])))
590
591(define (draw-solid-rectangle viewport)
592  (check-viewport 'draw-solid-rectangle viewport)
593  (rec draw-solid-rectangle-viewport
594    (case-lambda
595      [(p width height) (draw-solid-rectangle-viewport p width height (make-rgb 0 0 0))]
596      [(p width height color)
597       (check 'draw-solid-rectangle
598              posn? p "posn"
599              number? width "number"
600              number? height "number"
601              (orp color? number?) color "color or index")
602       (draw/clear/flip-rectangle
603        (lambda (dc)
604          (send dc set-pen (send gui:the-pen-list find-or-create-pen (get-color color) 1 'solid))
605          (send dc set-brush (send gui:the-brush-list find-or-create-brush (get-color color) 'solid)))
606        viewport p width height)])))
607
608(define (flip-rectangle viewport)
609  (check-viewport 'flip-rectangle viewport)
610  (rec flip-rectangle-viewport
611    (case-lambda
612      [(p width height) (flip-rectangle-viewport p width height (make-rgb 0 0 0))]
613      [(p width height color)
614       (check 'flip-rectangle
615              posn? p "posn"
616              number? width "number"
617              number? height "number"
618              (orp color? number?) color "color or index")
619       (draw/clear/flip-rectangle
620        (lambda (dc)
621          (send dc set-pen (send gui:the-pen-list find-or-create-pen (get-color color) 1 'xor))
622          (send dc set-brush (send gui:the-brush-list find-or-create-brush "BLACK" 'transparent)))
623        viewport p width height)])))
624
625(define (flip-solid-rectangle viewport)
626  (check-viewport 'flip-solid-rectangle viewport)
627  (rec flip-solid-rectangle-viewport
628    (case-lambda
629      [(p width height) (flip-solid-rectangle-viewport p width height (make-rgb 0 0 0))]
630      [(p width height color)
631       (check 'flip-solid-rectangle
632              posn? p "posn"
633              number? width "number"
634              number? height "number"
635              (orp color? number?) color "color or index")
636       (draw/clear/flip-rectangle
637        (lambda (dc)
638          (send dc set-pen (send gui:the-pen-list find-or-create-pen "BLACK" 1 'transparent))
639          (send dc set-brush (send gui:the-brush-list find-or-create-brush (get-color color) 'xor)))
640        viewport p width height)])))
641
642(define (draw-ellipse viewport)
643  (check-viewport 'draw-ellipse viewport)
644  (rec draw-ellipse-viewport
645    (case-lambda
646      [(p width height) (draw-ellipse-viewport p width height (make-rgb 0 0 0))]
647      [(p width height color)
648       (check 'draw-ellipse
649              posn? p "posn"
650              number? width "number"
651              number? height "number"
652              (orp color? number?) color "color or index")
653       (draw/clear/flip-ellipse
654        (lambda (dc)
655          (send dc set-pen (send gui:the-pen-list find-or-create-pen (get-color color) 1 'solid))
656          (send dc set-brush (send gui:the-brush-list find-or-create-brush "BLACK" 'transparent)))
657        viewport p width height)])))
658
659(define (draw-solid-ellipse viewport)
660  (check-viewport 'draw-solid-ellipse viewport)
661  (rec draw-solid-ellipse-viewport
662    (case-lambda
663      [(p width height) (draw-solid-ellipse-viewport p width height (make-rgb 0 0 0))]
664      [(p width height color)
665       (check 'draw-solid-ellipse
666              posn? p "posn"
667              number? width "number"
668              number? height "number"
669              (orp color? number?) color "color or index")
670       (draw/clear/flip-ellipse
671        (lambda (dc)
672          (send dc set-pen (send gui:the-pen-list find-or-create-pen (get-color color) 1 'solid))
673          (send dc set-brush (send gui:the-brush-list find-or-create-brush (get-color color) 'solid)))
674        viewport p width height)])))
675
676(define (flip-ellipse viewport)
677  (check-viewport 'flip-ellipse viewport)
678  (rec flip-ellipse-viewport
679    (case-lambda
680      [(p width height) (flip-ellipse-viewport p width height (make-rgb 0 0 0))]
681      [(p width height color)
682       (check 'flip-ellipse
683              posn? p "posn"
684              number? width "number"
685              number? height "number"
686              (orp color? number?) color "color or index")
687       (draw/clear/flip-ellipse
688        (lambda (dc)
689          (send dc set-pen (send gui:the-pen-list find-or-create-pen (get-color color) 1 'xor))
690          (send dc set-brush (send gui:the-brush-list find-or-create-brush "BLACK" 'transparent)))
691        viewport p width height)])))
692
693(define (flip-solid-ellipse viewport)
694  (check-viewport 'flip-solid-rectangle viewport)
695  (rec flip-solid-ellipse-viewport
696    (case-lambda
697      [(p width height) (flip-solid-ellipse-viewport p width height (make-rgb 0 0 0))]
698      [(p width height color)
699       (check 'flip-solid-ellipse
700              posn? p "posn"
701              number? width "number"
702              number? height "number"
703              (orp color? number?) color "color or index")
704       (draw/clear/flip-ellipse
705        (lambda (dc)
706          (send dc set-pen (send gui:the-pen-list find-or-create-pen "BLACK" 1 'transparent))
707          (send dc set-brush (send gui:the-brush-list find-or-create-brush (get-color color) 'xor)))
708        viewport p width height)])))
709
710(define (clear-rectangle viewport)
711  (check-viewport 'clear-rectangle viewport)
712  (rec clear-rectangle-viewport
713    (lambda (p width height)
714      (check 'clear-rectangle
715             posn? p "posn"
716             number? width "number"
717             number? height "number")
718      (draw/clear/flip-rectangle
719       (lambda (dc)
720         (send dc set-pen (send gui:the-pen-list find-or-create-pen "WHITE" 1 'solid))
721         (send dc set-brush (send gui:the-brush-list find-or-create-brush "BLACK" 'transparent)))
722       viewport p width height))))
723
724(define (clear-solid-rectangle viewport)
725  (check-viewport 'clear-solid-rectangle viewport)
726  (rec clear-solid-rectangle-viewport
727    (lambda (p width height)
728      (check 'clear-solid-rectangle
729             posn? p "posn"
730             number? width "number"
731             number? height "number")
732      (draw/clear/flip-rectangle
733       (lambda (dc)
734         (send dc set-pen (send gui:the-pen-list find-or-create-pen "WHITE" 1 'solid))
735         (send dc set-brush (send gui:the-brush-list find-or-create-brush "WHITE" 'solid)))
736       viewport p width height))))
737
738(define (clear-ellipse viewport)
739  (check-viewport 'clear-ellipse viewport)
740  (rec clear-ellipse-viewport
741    (lambda (p width height)
742      (check 'clear-ellipse
743             posn? p "posn"
744             number? width "number"
745             number? height "number")
746      (draw/clear/flip-ellipse
747       (lambda (dc)
748         (send dc set-pen (send gui:the-pen-list find-or-create-pen "WHITE" 1 'solid))
749         (send dc set-brush (send gui:the-brush-list find-or-create-brush "BLACK" 'transparent)))
750       viewport p width height))))
751
752(define (clear-solid-ellipse viewport)
753  (check-viewport 'clear-solid-ellipse viewport)
754  (rec clear-solid-ellipse-viewport
755    (lambda (p width height)
756      (check 'clear-solid-ellipse
757             posn? p "posn"
758             number? width "number"
759             number? height "number")
760      (draw/clear/flip-ellipse
761       (lambda (dc)
762         (send dc set-pen (send gui:the-pen-list find-or-create-pen "WHITE" 1 'solid))
763         (send dc set-brush (send gui:the-brush-list find-or-create-brush "WHITE" 'solid)))
764       viewport p width height))))
765
766(define make-do-pointlist
767  (lambda (go name get-pen-name set-pen-name
768              get-current-pen-name set-viewport-pen white-pen
769              get-brush-name set-brush-name invisi-brush)
770    (let ([f (make-draw-proc get-pen-name set-pen-name
771                             get-current-pen-name set-viewport-pen white-pen)])
772      (lambda (viewport)
773        (let ([f (f viewport)]
774              [vdc (viewport-dc viewport)]
775              [vbdc (viewport-buffer-dc viewport)])
776          (letrec ([the-function
777                    (case-lambda
778                      [(posns offset) (the-function posns offset #f)]
779                      [(posns offset color)
780                       (f color
781                          (lambda (flip clear)
782                            (let* ([points (map (lambda (p)
783                                                  (make-object gui:point% (posn-x p) (posn-y p)))
784                                                posns)]
785                                   [x (posn-x offset)]
786                                   [y (posn-y offset)]
787                                   [orig (send/proc vdc get-brush-name)]
788                                   [draw (lambda ()
789                                           (send/proc vdc set-brush-name
790                                                      invisi-brush)
791                                           (send/proc vbdc set-brush-name
792                                                      invisi-brush)
793                                           (send/proc
794                                            (viewport-dc viewport) name
795                                            points x y)
796                                           (send/proc
797                                            (viewport-buffer-dc viewport) name
798                                            points x y)
799                                           (send/proc vdc set-brush-name orig)
800                                           (send/proc vbdc set-brush-name
801                                                      orig))])
802                              (go draw
803                                  (lambda () (flip draw))
804                                  (lambda () (clear draw))))))])])
805            the-function))))))
806
807(define make-do-polygon
808  (lambda (go)
809    (make-do-pointlist go 'draw-polygon 'get-pen 'set-pen
810                       'get-current-pen set-viewport-pen white-pen
811                       'get-brush 'set-brush invisi-brush)))
812
813(define make-do-solid-polygon
814  (lambda (go)
815    (make-do-pointlist go 'draw-polygon 'get-brush 'set-brush
816                       'get-current-brush set-viewport-brush white-brush
817                       'get-pen 'set-pen invisi-pen)))
818
819(define draw-polygon (make-do-polygon draw-it))
820(define (clear-polygon viewport)
821  (let ([f ((make-do-polygon clear-it) viewport)])
822    (rec clear-polygon-viewport
823      (lambda (posns offset)
824        (f posns offset)))))
825(define (flip-polygon viewport)
826  (let ([f ((make-do-polygon flip-it) viewport)])
827    (rec flip-polygon-viewport
828      (lambda (posns offset)
829        (f posns offset)))))
830
831(define draw-solid-polygon (make-do-solid-polygon draw-it))
832(define (clear-solid-polygon viewport)
833  (let ([f ((make-do-solid-polygon clear-it) viewport)])
834    (rec clear-solid-polygon-viewport
835      (lambda (posns offset)
836        (f posns offset)))))
837(define (flip-solid-polygon viewport)
838  (let ([f ((make-do-solid-polygon flip-it) viewport)])
839    (rec flip-solid-polygon-viewport
840      (lambda (posns offset)
841        (f posns offset)))))
842
843(define make-do-pixel
844  (lambda (go)
845    (let ([f (make-draw-proc 'get-pen 'set-pen
846                             'get-current-pen set-viewport-pen white-pen)])
847      (lambda (viewport)
848        (let ([f (f viewport)])
849          (letrec ([the-function
850                    (case-lambda
851                      [(posn) (the-function posn #f)]
852                      [(posn color)
853                       (f color
854                          (lambda (flip clear)
855                            (let* ([x (posn-x posn)]
856                                   [y (posn-y posn)]
857                                   [draw (lambda ()
858                                           (send
859                                            (viewport-dc viewport) draw-point
860                                            x y)
861                                           (send
862                                            (viewport-buffer-dc viewport)
863                                            draw-point
864                                            x y))])
865                              (go draw
866                                  (lambda () (flip draw))
867                                  (lambda () (clear draw))))))])])
868            the-function))))))
869
870(define draw-pixel (make-do-pixel draw-it))
871(define (clear-pixel viewport)
872  (let ([f ((make-do-pixel clear-it) viewport)])
873    (rec clear-pixel-viewport
874      (lambda (posns offset)
875        (f posns offset)))))
876(define (flip-pixel viewport)
877  (let ([f ((make-do-pixel flip-it) viewport)])
878    (rec flip-pixel-viewport
879      (lambda (posns offset)
880        (f posns offset)))))
881
882(define string-functions
883  (lambda (string-op)
884    (letrec ([outer-function
885              (case-lambda
886                [(viewport) (outer-function viewport default-font)]
887                [(viewport font)
888                 (letrec ([the-function
889                           (case-lambda
890                             [(posn text) (the-function posn text #f)]
891                             [(posn text color)
892                              (let*-values ([(dc) (viewport-dc viewport)]
893                                            [(x) (posn-x posn)]
894                                            [(w h d a) (send dc get-text-extent "X" font)]
895                                            [(y) (- (posn-y posn) (- h d))]
896                                            [(buffer) (viewport-buffer-dc viewport)]
897                                            [(string-create)
898                                             (lambda ()
899                                               (send dc draw-text text x y)
900                                               (send buffer draw-text text x y))])
901                                (cond
902                                  [(eq? string-op 'draw)
903                                   (when color
904                                     (set-text-foreground viewport color))
905                                   (set-viewport-font viewport font)
906                                   (send dc draw-text text x y)
907                                   (send buffer draw-text text x y)]
908                                  [(eq? string-op 'flip)
909                                   (when color
910                                     (set-text-foreground viewport color))
911                                   (set-viewport-font viewport font)
912                                   (string-create)]
913                                  [(eq? string-op 'clear)
914                                   (set-text-foreground viewport white)
915                                   (set-viewport-font viewport font)
916                                   (string-create)
917                                   (set-text-foreground viewport black)]))])])
918                   the-function)])])
919      outer-function)))
920
921(define draw-string (string-functions 'draw))
922(define (clear-string viewport)
923  (let ([f ((string-functions 'clear) viewport)])
924    (rec clear-string-viewport
925      (lambda (posns offset)
926        (f posns offset)))))
927(define (flip-string viewport)
928  (let ([f ((string-functions 'flip) viewport)])
929    (rec flip-string-viewport
930      (lambda (posns offset)
931        (f posns offset)))))
932
933(define get-string-size
934  (case-lambda
935    [(viewport) (get-string-size viewport default-font)]
936    [(viewport font)
937     (lambda (text)
938       (let-values ([(w h d a) (send (viewport-dc viewport) get-text-extent text font)])
939         (list w h)))]))
940
941(define get-color-pixel
942  (lambda (viewport)
943    (lambda (posn)
944      (let ([c (make-object gui:color%)]
945            [x (posn-x posn)]
946            [y (posn-y posn)])
947        (unless (send (viewport-buffer-dc viewport) get-pixel x y c)
948          (error 'get-color-pixel "specified point is out-of-range"))
949        c))))
950
951(define get-pixel
952  (lambda (viewport)
953    (lambda (posn)
954      (let ([c (make-object gui:color%)]
955            [x (posn-x posn)]
956            [y (posn-y posn)])
957        (unless (send (viewport-buffer-dc viewport) get-pixel x y c)
958          (error 'get-pixel "specified point is out-of-range"))
959        (if (or (< (send c blue) 255)
960                (< (send c red) 255)
961                (< (send c green) 255))
962            1
963            0)))))
964
965(define (test-pixel viewport)
966  (lambda (color)
967    (let ([c (make-object gui:color%)])
968      (send (viewport-buffer-dc viewport) try-color color c)
969      c)))
970
971(define draw-pixmap-posn
972  (lambda (filename [type 'unknown/mask])
973    (check 'draw-pixmap-posn
974           string? filename "filename"
975           (lambda (x) (memq x '(gif xbm xpm bmp pict unknown unknown/mask gif/mask))) type "file type symbol")
976    (let* ([bitmap (make-object gui:bitmap% filename type)])
977      (lambda (viewport)
978        (check 'draw-pixmap-posn
979               viewport? viewport "viewport")
980        (lambda (posn [color #f])
981          (check 'draw-pixmap-posn
982                 posn? posn "posn"
983                 (orp not color?) color (format "color or ~e" #f))
984          (when color
985            (set-viewport-pen viewport (get-color color)))
986          (let ([x (posn-x posn)]
987                [y (posn-y posn)])
988            (send (viewport-dc viewport) draw-bitmap bitmap x y 'solid black-color (send bitmap get-loaded-mask))
989            (send (viewport-buffer-dc viewport) draw-bitmap bitmap x y 'solid black-color (send bitmap get-loaded-mask))))))))
990
991(define draw-pixmap
992  (lambda (viewport)
993    (check 'draw-pixmap
994           viewport? viewport "viewport")
995    (lambda (filename p [color #f])
996      (check 'draw-pixmap
997             (andp string? file-exists?) filename "filename"
998             posn? p "posn"
999             (orp not color?) color (format "color or ~e" #f))
1000      (((draw-pixmap-posn filename 'unknown) viewport) p color))))
1001
1002(define copy-viewport
1003  (lambda (source target)
1004    (check 'copy-viewport
1005           viewport? source "viewport"
1006           viewport? target "viewport")
1007    (let* ([source-bitmap (viewport-bitmap source)]
1008           [target-dc (viewport-dc target)]
1009           [target-buffer-dc (viewport-buffer-dc target)])
1010      (send target-dc draw-bitmap source-bitmap 0 0)
1011      (send target-buffer-dc draw-bitmap source-bitmap 0 0))))
1012
1013(define save-pixmap
1014  (lambda (viewport)
1015    (check 'save-pixmap
1016           viewport? viewport "viewport")
1017    (lambda (filename [kind 'xpm])
1018      (check 'save-pixmap
1019             (andp string? (orp relative-path? absolute-path?)) filename "filename"
1020             (lambda (x) (memq x '(xpm xbm bmp pict))) kind "file type symbol")
1021      (let ([bm (viewport-bitmap viewport)])
1022        (send bm save-file filename kind)))))
1023
1024(define sixlib-eventspace #f)
1025
1026(define make-open-viewport
1027  (lambda (name show?)
1028    (unless sixlib-eventspace
1029      (set! sixlib-eventspace
1030            (parameterize ([uncaught-exception-handler
1031                            (lambda (x)
1032                              ((error-display-handler)
1033                               (format "internal error in graphics library: ~a"
1034                                       (if (exn? x)
1035                                           (exn-message x)
1036                                           (format "~e" x)))
1037                               x)
1038                              ((error-escape-handler)))])
1039              (gui:make-eventspace))))
1040    (letrec ([open-viewport
1041              (case-lambda
1042                [(label point)
1043                 (cond
1044                   [(posn? point) (open-viewport label (posn-x point) (posn-y point))]
1045                   [(and (list? point) (= (length point) 2))
1046                    (open-viewport label (car point) (cadr point))]
1047                   [else (error name "bad argument ~s" point)])]
1048                [(label width height)
1049                 (cond
1050                   [graphics-flag
1051                    (let*
1052                        ([frame
1053                          (parameterize ([gui:current-eventspace sixlib-eventspace])
1054                            (make-object sixlib-frame%
1055                              label #f width height))]
1056                         [panel (make-object gui:vertical-panel% frame)]
1057                         [canvas (make-object sixlib-canvas% panel)]
1058                         [_ (begin
1059                              (send canvas min-height height)
1060                              (send canvas min-width width))]
1061                         [dc (send canvas get-dc)]
1062                         [buffer-dc (make-object gui:bitmap-dc%)]
1063                         [viewport (make-viewport label canvas)]
1064                         [ml (event-receiver)]
1065                         [kl (event-receiver)])
1066                      (send panel set-alignment 'center 'center)
1067                      (send frame set-canvas canvas)
1068                      (send canvas set-viewport viewport)
1069                      (send canvas set-dc dc)
1070                      (send canvas set-buffer-dc buffer-dc)
1071                      (send canvas set-geometry width height)
1072                      (send canvas set-mouse-listener ml)
1073                      (send canvas set-key-listener kl)
1074                      (when show?
1075                        (send frame show #t)
1076                        (send canvas focus))
1077                      (set-text-foreground viewport black)
1078                      (set-text-background viewport white)
1079                      (set-viewport-background viewport white)
1080                      (set-viewport-pen viewport black-pen)
1081                      (set-viewport-brush viewport black-brush)
1082                      ((clear-viewport viewport))
1083                      (when (null? global-viewport-list)
1084                        (send open-frames-timer start 100000000))
1085                      (set! global-viewport-list (cons viewport global-viewport-list))
1086                      viewport)]
1087                   [else (error "graphics not open")])])])
1088      open-viewport)))
1089
1090(define open-viewport (make-open-viewport 'open-viewport #t))
1091(define open-pixmap (make-open-viewport 'open-pixmap #f))
1092
1093(define (default-display-is-color?) (gui:is-color-display?))
1094
1095(define position-display
1096  (lambda (viewport counter)
1097    (cond
1098      [(equal? counter 0) '()]
1099      [else (begin
1100              (display (query-mouse-posn viewport))
1101              (position-display viewport (- counter 1)))])))
1102
1103
1104(define create-cmap
1105  (lambda ()
1106    (do ([index 0 (+ 1 index)])
1107      ((> index 20))
1108      (let* ([r (* 0.05 index)]
1109             [b (- 1 r)]
1110             [g (- 1 r)])
1111        (change-color index (make-rgb r g b))))))
1112
1113(define viewport->snip
1114  (lambda (viewport)
1115    (let ([orig-bitmap (send (viewport-canvas viewport) get-bitmap)]
1116          [orig-dc (viewport-buffer-dc viewport)])
1117      (let* ([h (send orig-bitmap get-height)]
1118             [w (send orig-bitmap get-width)]
1119             [new-bitmap (make-object gui:bitmap% w h)]
1120             [tmp-mem-dc (make-object gui:bitmap-dc%)])
1121        (send tmp-mem-dc set-bitmap new-bitmap)
1122        (send tmp-mem-dc draw-bitmap (send orig-dc get-bitmap) 0 0)
1123        (send tmp-mem-dc set-bitmap #f)
1124        (let ([snip (make-object gui:image-snip%)])
1125          (send snip set-bitmap new-bitmap)
1126          snip)))))
1127
1128(create-cmap)
1129
1130
1131;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1132;;;                                                             ;;;
1133;;;                        ERROR CHECKING                       ;;;
1134;;;                                                             ;;;
1135;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1136
1137
1138;; check-viewport : symbol TST -> void
1139(define (check-viewport f-name obj)
1140  (unless (viewport? obj)
1141    (error f-name "expected viewport as first argument, got ~e" obj)))
1142
1143;; (define-type arg/pred/name-list (list* (TST -> bool) TST string arg/pred/name-list))
1144;; check : (symbol arg/pred/name-list *-> void)
1145(define (check f-name . in-args)
1146  (let loop ([args in-args]
1147             [n 0])
1148    (cond
1149      [(null? args) (void)]
1150      [else (let ([pred? (car args)]
1151                  [val (cadr args)]
1152                  [name (caddr args)])
1153              (unless (pred? val)
1154                (error f-name "expected ~a as arg ~a, got: ~e, all args: ~a"
1155                       name n val
1156                       (let loop ([args in-args])
1157                         (cond
1158                           [(null? args) ""]
1159                           [else (string-append (format "~e" (cadr args))
1160                                                " "
1161                                                (loop (cdddr args)))]))))
1162              (loop (cdddr args)
1163                    (+ n 1)))])))
1164
1165(define (orp . preds)
1166  (lambda (TST)
1167    (ormap (lambda (p) (p TST)) preds)))
1168
1169(define (andp . preds)
1170  (lambda (TST)
1171    (andmap (lambda (p) (p TST)) preds)))
1172