;-*- Mode: Lisp; Package: (g :use (common-lisp)) -*-
; Source Code for G, a low-level, device-independent, graphics language
; for Macintosh Common Lisp. See http://envy.cs.umass.edu/People/sutton/G/g.html.
(defpackage :g
(:use :common-lisp :ccl))
(in-package :g)
;;;; OBJECTS
(export '(*g-device* g-view g-window g-device
g-get-parent g-set-parent g-get-children g-close-view))
(defvar *g-device*) ;[Doc]
(defclass g-view (view) ;[Doc]
((cs-left :initform 0.0s0 :accessor cs-left)
(cs-bottom :initform 0.0s0 :accessor cs-bottom)
(cs-right :initform 1.0s0 :accessor cs-right)
(cs-top :initform 1.0s0 :accessor cs-top)
(offsetx :accessor offsetx)
(offsety :accessor offsety)
(scalex :accessor scalex)
(scaley :accessor scaley)))
(defmethod initialize-instance ((view g-view) &key parent) ;[Doc]
(without-event-processing
(call-next-method)
(unless (or (typep view 'g-device)
(typep view 'window)
(eq parent :none))
(unless parent
(setq parent (or (front-window :class 'g-window) (front-window))))
(set-view-container view parent)
(set-view-size view (view-size parent)))
(g-update-normalization view)))
(defclass g-device (g-view) ;[Doc]
((children :initform nil :accessor children))
(:default-initargs
:view-size (make-point *screen-width* *screen-height*)))
(defclass g-window (g-view window) ;[Doc]
((parent :initarg :parent)
(last-color :initform nil))
(:default-initargs
:parent *g-device*
:color-p t))
(defmethod initialize-instance :after ;[Doc]
((window g-window) &key g-viewport gd-viewport g-viewport-r gd-viewport-r)
(without-event-processing
(when g-viewport (apply #'g-set-viewport window g-viewport))
(when gd-viewport (apply #'gd-set-viewport window gd-viewport))
(when g-viewport-r (apply #'g-set-viewport-r window g-viewport-r))
(when gd-viewport-r (apply #'gd-set-viewport-r window gd-viewport-r))
(let ((device (g-get-parent window)))
(push window (children device))))
(when (typep (target) 'listener)
(window-select (target))))
(defmethod g-close-view ((view view)) ;[Doc]
"Let the parent know this view is no longer in use"
(remove-subviews (g-get-parent view) view))
(defmethod g-close-view ((window g-window))
"Close the window and let the parent know this view is no longer in use"
(setf (children (g-get-parent window))
(remove window (children (g-get-parent window))))
(window-close window))
(defmethod g-close-view ((window window))
(window-close window))
(defmethod g-get-parent ((view simple-view)) ;[Doc]
(view-container view))
(defmethod g-get-parent ((window g-window))
(slot-value window 'parent))
(defmethod g-get-parent ((window window))
*g-device*)
(defmethod g-set-parent ((view view) new-parent)
(let ((parent (g-get-parent view)))
(when parent (remove-subviews parent view))
(set-view-container view new-parent)))
(defmethod g-set-parent ((window window) new-parent)
(with-slots (parent) window
(when parent
(setf (children parent)
(remove window (children parent))))
(setq parent new-parent)))
(defmethod g-get-children ((view g-view)) ;[Doc]
(subviews view))
(defmethod g-get-children ((device g-device))
(if (eq device *g-device*)
(union (children device) (windows))
(children device)))
;;; VIEWS and COLORS and COORDINATE SYSTEMS
(export '(g-set-viewport g-get-viewport gd-set-viewport gd-get-viewport
g-set-viewport-r g-get-viewport-r gd-set-viewport-r gd-get-viewport-r
g-set-viewport-size gd-set-viewport-size
g-set-coordinate-system g-get-coordinate-system gd-get-coordinate-system
g-set-coordinate-system-r g-get-coordinate-system-r gd-get-coordinate-system-r
g-set-coordinate-system-scale g-set-coordinate-system-scale-r
g-set-cs-r g-get-cs-r gd-get-cs-r
g-set-cs g-get-cs gd-get-cs
g-set-cs-scale g-get-cs-scale
g-accept-new-viewport-size
g-accept-new-viewport-position
g-coord-x g-coord-y gd-coord-x gd-coord-y
g-offset-x g-offset-y gd-offset-x gd-offset-y
g-color-rgb g-color-rgb-255
g-color-bw
g-color-pen
g-color-user-pick
g-color-black
g-color-white
g-color-pink
g-color-red
g-color-orange
g-color-yellow
g-color-green
g-color-dark-green
g-color-light-blue
g-color-blue
g-color-purple
g-color-brown
g-color-tan
g-color-light-gray
g-color-gray
g-color-cyan
g-color-magenta
g-color-dark-gray
g-color-flip
g-color-invisible
g-color-on
g-color-off
g-set-color
set-view-size
set-view-position
g-accept-new-viewport-position
g-update-normalization
gd-convert-x
gd-convert-y
g-convert-x
g-convert-y))
(defmethod set-view-size ((view g-view) h &optional v)
(declare (ignore h v))
(without-event-processing
(call-next-method)
(g-update-normalization view)
(g-accept-new-viewport-size view)))
(defmethod set-view-position ((view g-view) x-pos &optional y-pos)
(declare (ignore x-pos y-pos))
(without-event-processing
(call-next-method)
(g-accept-new-viewport-position view)))
(defmethod g-accept-new-viewport-size ((view g-view)) ;[Doc]
)
(defmethod g-accept-new-viewport-position ((view g-view))
)
(defclass screen-coordinates (view) ())
(defmethod g-accept-new-viewport-position :before ((view screen-coordinates))
(set-view-scroll-position
view (local-to-global view (view-scroll-position view)))
(loop for child in (g-get-children view)
do (g-accept-new-viewport-position child)))
(defun gd-get-viewport (view) ;[Doc]
(let ((point1 (view-position view))
(point2 (view-size view)))
(setq point2 (add-points point1 point2))
(values (point-h point1)
(point-v point1)
(1- (point-h point2))
(1- (point-v point2)))))
(defun g-get-viewport (view) ;[Doc]
(let ((parent (g-get-parent view)))
(multiple-value-bind (dx1 dy1 dx2 dy2) (gd-get-viewport view)
(let ((x1 (g-coord-x parent dx1))
(y1 (g-coord-y parent dy1))
(x2 (g-coord-x parent dx2))
(y2 (g-coord-y parent dy2)))
(values (min x1 x2)
(min y1 y2)
(max x1 x2)
(max y1 y2))))))
(defun gd-get-viewport-r (view) ;[Doc]
(multiple-value-bind (x1 y1 x2 y2) (gd-get-viewport view)
(values x1 y1 (- x2 x1) (- y2 y1))))
(defun g-get-viewport-r (view) ;[Doc]
(multiple-value-bind (x1 y1 x2 y2) (g-get-viewport view)
(values (min x1 x2)
(min y1 y2)
(abs (- x2 x1))
(abs (- y2 y1)))))
(defun gd-set-viewport (view dx1 dy1 dx2 dy2) ;[Doc]
(set-view-position view (min dx1 dx2) (min dy1 dy2))
(set-view-size view
(1+ (abs (- dx1 dx2)))
(1+ (abs (- dy1 dy2)))))
(defun g-set-viewport (view vpx1 vpy1 vpx2 vpy2) ;[Doc]
(let ((parent (g-get-parent view)))
(gd-set-viewport view
(gd-coord-x parent vpx1)
(gd-coord-y parent vpy1)
(and vpx2 (gd-coord-x parent vpx2))
(and vpy2 (gd-coord-y parent vpy2)))))
;[Doc]
(defun gd-set-viewport-r (view dx dy &optional delta-x delta-y)
(when (null dx) (setq dx (nth-value 0 (gd-get-viewport view))))
(when (null dy) (setq dy (nth-value 1 (gd-get-viewport view))))
(when (null delta-x) (setq delta-x (nth-value 2 (gd-get-viewport-r view))))
(when (null delta-y) (setq delta-y (nth-value 3 (gd-get-viewport-r view))))
(gd-set-viewport view dx dy (+ dx delta-x) (+ dy delta-y)))
(defun g-set-viewport-r (view x y &optional delta-x delta-y) ;[Doc]
(when (null x) (setq x (nth-value 0 (g-get-viewport view))))
(when (null y) (setq y (nth-value 1 (g-get-viewport view))))
(when (null delta-x) (setq delta-x (nth-value 2 (g-get-viewport-r view))))
(when (null delta-y) (setq delta-y (nth-value 3 (g-get-viewport-r view))))
(g-set-viewport view x y (+ x delta-x) (+ y delta-y)))
(defun g-get-cs (view)
(g-get-coordinate-system view))
(defun g-get-coordinate-system (view) ;[Doc]
(with-slots (cs-left cs-right cs-bottom cs-top) view
(values (min cs-left cs-right)
(min cs-top cs-bottom)
(max cs-right cs-left)
(max cs-top cs-bottom)
(if (<= cs-left cs-right)
(if (<= cs-bottom cs-top)
:lower-left
:upper-left)
(if (<= cs-bottom cs-top)
:lower-right
:upper-right)))))
(defun gd-get-cs (view)
(gd-get-coordinate-system view))
(defun gd-get-coordinate-system (view) ;[Doc] ;
(let ((min-point (view-scroll-position view))
(max-point (view-size view)))
(setq max-point (add-points min-point max-point))
(values (point-h min-point)
(point-v min-point)
(1- (point-h max-point))
(1- (point-v max-point))
:upper-left)))
(defun g-get-cs-r (view)
(g-get-coordinate-system-r view))
;[Doc]
(defun g-get-coordinate-system-r (view)
(multiple-value-bind (x1 y1 x2 y2 corner) (g-get-coordinate-system view)
(values x1 y1 (- x2 x1) (- y2 y1) corner)))
(defun gd-get-cs-r (view)
(gd-get-coordinate-system-r view))
;[Doc]
(defun gd-get-coordinate-system-r (view)
(multiple-value-bind (x1 y1 x2 y2 corner) (gd-get-coordinate-system view)
(values x1 y1 (- x2 x1) (- y2 y1) corner)))
;[Doc]
(defun g-get-cs-scale (view)
(g-get-coordinate-system-scale view))
;[Doc]
(defun g-get-coordinate-system-scale (view)
(multiple-value-bind (x1 y1 x2 y2 corner) (g-get-coordinate-system view)
(declare (ignore x2 y2))
(with-slots (scalex scaley) view
(values x1 y1 scalex scaley corner))))
(defun g-set-cs (view x1 y1 x2 y2 &optional (corner :lower-left))
(g-set-coordinate-system view x1 y1 x2 y2 corner))
;[Doc]
(defun g-set-coordinate-system (view x1 y1 x2 y2 &optional (corner :lower-left))
(setq x1 (coerce x1 'float)
y1 (coerce y1 'float)
x2 (coerce x2 'float)
y2 (coerce y2 'float))
(cond ((= x1 x2)
(print "Attempt to set left and right of G coordinate system to same values.")
(setq x2 (+ x1 1)))
((= y1 y2)
(print "Attempt to set top and bottom of G coordinate system to same values.")
(setq y2 (+ y1 1)))
(t (with-slots (cs-left cs-right cs-top cs-bottom) view
(setf cs-left (if (member corner '(:lower-left :upper-left)) x1 x2))
(setf cs-bottom (if (member corner '(:lower-left :lower-right)) y1 y2))
(setf cs-right (if (member corner '(:lower-left :upper-left)) x2 x1))
(setf cs-top (if (member corner '(:lower-left :lower-right)) y2 y1)))))
(g-update-normalization view))
(defun g-set-cs-r (view x y delta-x delta-y &optional (corner :lower-left))
(g-set-coordinate-system-r view x y delta-x delta-y corner))
;[Doc]
(defun g-set-coordinate-system-r (view x y delta-x delta-y &optional (corner :lower-left))
(g-set-coordinate-system view x y (+ x delta-x) (+ y delta-y) corner))
;[Doc]
(defun g-set-cs-scale (view x y x-scale &optional y-scale (corner :lower-left))
(g-set-coordinate-system-scale view x y x-scale y-scale corner))
;[Doc]
(defun g-set-coordinate-system-scale (view x y x-scale &optional y-scale (corner :lower-left))
(when (not (numberp x-scale))
(setq x-scale (ecase x-scale
(:inches 72)
(:centimeters 28.35)
(:pixels 1)
(:points 1))))
(when (not (numberp y-scale))
(setq y-scale (ecase y-scale
('nil x-scale)
(:inches 72)
(:centimeters 28.35)
(:pixels 1)
(:points 1))))
(multiple-value-bind (dx1 dy1 dx2 dy2) (gd-get-viewport view)
(let ((x2 (+ x (/ (abs (- dx1 dx2)) (float x-scale))))
(y2 (+ y (/ (abs (- dy1 dy2)) (float y-scale)))))
(g-set-coordinate-system view x y x2 y2 corner))))
;(defun gd-within-coordinate-system-p (view dx dy)
; (multiple-value-bind (min-x min-y max-x max-y) (gd-get-coordinate-system view)
; (and (<= min-x dx max-x)
; (<= min-y dy max-y))))
(defun g-update-normalization (view)
"Updates state variables of normalized coordinate system"
(with-slots (scalex scaley offsetx offsety cs-left cs-bottom cs-right cs-top) view
(multiple-value-bind (x1 y1 x2 y2) (gd-get-coordinate-system view)
(if (= cs-right cs-left)
(error "Attempt to establish invalid (zero area) G coordinate system")
(setf scalex (/ (- x2 x1) (- cs-right cs-left))))
(setf offsetx (- x1 (* cs-left scalex)))
(if (= cs-bottom cs-top)
(error "Attempt to establish invalid (zero area) G coordinate system")
(setf scaley (/ (- y2 y1) (- cs-bottom cs-top))))
(setf offsety (- y1 (* cs-top scaley))))))
(defun gd-coord-x (view x) ;[Doc]
(with-slots (offsetx scalex) view
(round (+ offsetx
(* x scalex)))))
(defun gd-coord-y (view y) ;[Doc]
(with-slots (offsety scaley) view
(round (+ offsety
(* y scaley)))))
(defun gd-coords (view x y)
(values (gd-coord-x view x) (gd-coord-y view y)))
;[Doc]
(defun gd-offset-x (view x-offset)
"Returns the length in device coords (pixels) of the x-distance in normal coords"
(with-slots (scalex) view
(round (* x-offset scalex))))
;[Doc]
(defun gd-offset-y (view y-offset)
"Returns the length in device coords (pixels) of the y-distance in normal coords"
(with-slots (scaley) view
(round (* y-offset scaley))))
(defun gd-offset (view x-offset y-offset)
(values (gd-offset-x view x-offset) (gd-offset-y view y-offset)))
;;; Converting from device to normal coordinates
(defun g-coord-x (view dx) ;[Doc]
(with-slots (offsetx scalex) view
(/ (- dx offsetx) scalex)))
(defun g-coord-y (view dy) ;[Doc]
(with-slots (offsety scaley) view
(/ (- dy offsety) scaley)))
(defun g-coords (view dx dy)
(values (g-coord-x view dx) (g-coord-y view dy)))
(defun g-offset-x (view dx-offset) ;[Doc]
(with-slots (scalex) view
(round (/ dx-offset scalex))))
(defun g-offset-y (view dy-offset) ;[Doc]
(with-slots (scaley) view
(round (/ dy-offset scaley))))
(defun g-offset (view dx-offset dy-offset)
(values (g-offset-x view dx-offset) (g-offset-y view dy-offset)))
;;; Converting coordinates between views:
;[Doc]
(defun gd-convert-x (from-view to-view dx)
(point-h (global-to-local to-view (local-to-global from-view (make-point dx 0)))))
;[Doc]
(defun gd-convert-y (from-view to-view dy)
(point-v (global-to-local to-view (local-to-global from-view (make-point 0 dy)))))
;[Doc]
(defun g-convert-x (from-view to-view x)
(g-coord-x to-view (gd-convert-x from-view to-view (gd-coord-x from-view x))))
;[Doc]
(defun g-convert-y (from-view to-view y)
(g-coord-y to-view (gd-convert-y from-view to-view (gd-coord-y from-view y))))
;; Color Routines
(defvar *pens* (make-hash-table :test #'equal))
(defvar *colors* (make-hash-table :test #'equal))
(defun translate-pattern (keyword)
(ecase keyword
(:black-pattern *black-pattern*)
(:white-pattern *white-pattern*)
(:gray-pattern *gray-pattern*)
(:light-gray-pattern *light-gray-pattern*)
(:dark-gray-pattern *dark-gray-pattern*)))
;[Doc]
(defun g-color-pen (view color pattern &optional mode x-size y-size)
"Returns a new color with specified pen characteristics"
(declare (ignore view))
(let (pen)
(setq pen (if (atom color)
(list (or pattern :black-pattern)
(or mode :patCopy)
(make-point (or x-size 1) (or y-size 1)))
(list (or pattern (second color))
(or mode (third color))
(make-point (or x-size (point-h (fourth color)))
(or y-size (point-v (fourth color)))))))
(setq pen (or (gethash pen *pens*)
(setf (gethash pen *pens*) pen)))
(setq color (cons (if (atom color)
color (first color))
pen))
(or (gethash color *colors*)
(setf (gethash color *colors*) color))))
(defun g-color-pen-flip (view color)
(g-color-pen view color :black-pattern :patXor))
(defun g-color-pen-invisible (view color)
(g-color-pen view color :black-pattern :NotPatOr))
(defun g-color-size (view color x-size &optional y-size)
(g-color-pen view color nil nil x-size y-size))
;[Doc]
(defun g-color-black (view) (declare (ignore view)) *black-color*)
(defun g-color-white (view) (declare (ignore view)) *white-color*)
(defun g-color-pink (view) (declare (ignore view)) *pink-color*)
(defun g-color-red (view) (declare (ignore view)) *red-color*)
(defun g-color-orange (view) (declare (ignore view)) *orange-color*)
(defun g-color-yellow (view) (declare (ignore view)) *yellow-color*)
(defun g-color-green (view) (declare (ignore view)) *green-color*)
(defun g-color-dark-green (view) (declare (ignore view)) *dark-green-color*)
(defun g-color-light-blue (view) (declare (ignore view)) *light-blue-color*)
(defun g-color-blue (view) (declare (ignore view)) *blue-color*)
(defun g-color-purple (view) (declare (ignore view)) *purple-color*)
(defun g-color-brown (view) (declare (ignore view)) *brown-color*)
(defun g-color-tan (view) (declare (ignore view)) *tan-color*)
(defun g-color-light-gray (view) (declare (ignore view)) *light-gray-color*)
(defun g-color-gray (view) (declare (ignore view)) *gray-color*)
(defun g-color-dark-gray (view) (declare (ignore view)) *dark-gray-color*)
(defun g-color-on (view) (declare (ignore view)) *black-color*)
(defun g-color-off (view) (declare (ignore view)) *white-color*)
(defun g-color-cyan (view) (g-color-rgb view 0 1 1))
(defun g-color-magenta (view) (g-color-rgb view 1 0 1))
(defun g-color-flip (view)
(g-color-pen-flip view *black-color*))
(defun g-color-invisible (view)
(g-color-pen-invisible view *black-color*))
(defun g-color-rgb (view red green blue) ;[Doc]
(declare (ignore view))
(setq red (min 1.0 (max 0.0 red)))
(setq green (min 1.0 (max 0.0 green)))
(setq blue (min 1.0 (max 0.0 blue)))
(make-color (floor (* 65535 red))
(floor (* 65535 green))
(floor (* 65535 blue))))
(defun g-color-rgb-255 (view red green blue) ;[Doc]
(declare (ignore view))
(setq red (min 255 (max 0 red)))
(setq green (min 255 (max 0 green)))
(setq blue (min 255 (max 0 blue)))
(make-color (floor (* 257 red))
(floor (* 257 green))
(floor (* 257 blue))))
(defvar black *black-color*)
(defvar white *white-color*)
(defvar pink *pink-color*)
(defvar red *red-color*)
(defvar orange *orange-color*)
(defvar yellow *yellow-color*)
(defvar green *green-color*)
(defvar dark-green *dark-green-color*)
(defvar light-blue *light-blue-color*)
(defvar blue *blue-color*)
(defvar purple *purple-color*)
(defvar brown *brown-color*)
(defvar tan *tan-color*)
(defvar light-gray *light-gray-color*)
(defvar gray *gray-color*)
(defvar dark-gray *dark-gray-color*)
(defvar on *black-color*)
(defvar off *white-color*)
(defvar cyan (g-color-cyan t))
(defvar magenta (g-color-magenta t))
(defvar flip (g-color-flip t))
(defvar invisible (g-color-invisible t))
(defun g-color-bw (view intensity) ;[Doc]
; (when (= 4 (ccl::screen-bits (ccl::window-screen (view-window view))))
; (setq intensity (/ (round (* 15 intensity)) 15)))
(setq intensity (- 1 intensity))
(g-color-rgb view intensity intensity intensity))
(defun g-color-user-pick (view &rest args) ;[Doc]
(declare (ignore view))
(apply #'user-pick-color args))
(defun g-set-color (view color) ;[Doc]
(set-color-if-needed (view-window view) color))
(defun set-color-if-needed (window color)
"Sets the fore-color and pen of the window, if needed, to color"
(if (not (typep window 'g-window))
(if (atom color) ; not a g-window
(set-fore-color window color)
(progn (set-fore-color window (first color))
(pen-normal window)
(when (neq (second color) :black-pattern)
(set-pen-pattern window (translate-pattern (second color))))
(when (neq (third color) :patCopy)
(set-pen-mode window (third color)))
(when (neq (fourth color) #@(1 1))
(set-pen-size window (fourth color)))))
(with-slots (last-color) window ; is a g-window
(unless (eq color last-color)
(if (atom color)
(if (atom last-color) ; color is atomic
(set-fore-color window color)
(progn (when (neq color (first last-color))
(set-fore-color window color))
(pen-normal window)))
(if (atom last-color) ; color is list
(progn (when (neq (first color) last-color) ; last-color atomic
(set-fore-color window (first color)))
(when (neq (second color) :black-pattern)
(set-pen-pattern window (translate-pattern (second color))))
(when (neq (third color) :patCopy)
(set-pen-mode window (third color)))
(when (neq (fourth color) #@(1 1))
(set-pen-size window (fourth color))))
(progn (when (neq (first color) (first last-color)) ; both lists
(set-fore-color window (first color)))
(when (neq (second color) (second last-color))
(set-pen-pattern window (translate-pattern (second color))))
(when (neq (third color) (third last-color))
(set-pen-mode window (third color)))
(when (neq (fourth color) (fourth last-color))
(set-pen-size window (fourth color))))))
(setq last-color color)))))
;;; GD-GRAPHICS
(export 'gd-draw-point)
(export 'gd-draw-line)
(export 'gd-draw-line-r)
(export 'gd-outline-rect)
(export 'gd-outline-rect-r)
(export 'gd-fill-rect)
(export 'gd-fill-rect-r)
(export 'gd-draw-circle)
(export 'gd-draw-disk)
(export 'gd-draw-arc)
(export 'gd-draw-text)
(export 'gd-draw-text-centered)
(export 'gd-text-width)
(export 'gd-text-height)
(export 'gd-get-cursor-position)
(defun gd-draw-point (view dx dy &optional color-code) ;[Doc]
(without-interrupts
; (unless (eq *current-view* view) (focus-view view))
(with-focused-view view
(when color-code (set-color-if-needed (view-window view) color-code))
(#_MoveTo :long (make-point dx dy))
(#_Line :long #@(0 0)))))
;[Doc]
(defun gd-draw-line (view dx1 dy1 dx2 dy2 &optional color-code)
(without-interrupts
; (unless (eq *current-view* view) (focus-view view))
(with-focused-view view
(when color-code (set-color-if-needed (view-window view) color-code))
(#_MoveTo :long (make-point dx1 dy1))
(#_LineTo :long (make-point dx2 dy2)))))
;[Doc]
(defun gd-draw-line-r (view dx dy delta-x delta-y &optional color-code)
(gd-draw-line view dx dy (+ dx delta-x) (+ dy delta-y) color-code))
;[Doc]
(defun gd-outline-rect (view dx1 dy1 dx2 dy2 &optional color-code)
(gd-outline-rect-r view dx1 dy1 (- dx2 dx1) (- dy2 dy1) color-code))
;[Doc]
(defun gd-outline-rect-r (view dx dy delta-x delta-y &optional color-code)
(incf delta-x dx)
(incf delta-y dy)
(rlet ((rect :rect))
(points-to-rect (make-point dx dy)
(make-point delta-x delta-y)
rect)
(incf (rref rect :rect.bottom))
(incf (rref rect :rect.right))
(without-interrupts
; (unless (eq *current-view* view) (focus-view view))
(with-focused-view view
(when color-code (set-color-if-needed (view-window view) color-code))
(#_FrameRect rect)))))
;[Doc]
(defun gd-fill-rect (view dx1 dy1 dx2 dy2 &optional color-code)
(gd-fill-rect-r view dx1 dy1 (- dx2 dx1) (- dy2 dy1) color-code))
;[Doc]
(defun gd-fill-rect-r (view dx dy delta-x delta-y color-code)
(incf delta-x dx)
(incf delta-y dy)
(rlet ((rect :rect))
(points-to-rect (make-point dx dy)
(make-point delta-x delta-y)
rect)
(incf (rref rect :rect.bottom))
(incf (rref rect :rect.right))
(without-interrupts
; (unless (eq *current-view* view) (focus-view view))
(with-focused-view view
(when color-code (set-color-if-needed (view-window view) color-code))
(#_PaintRect rect)))))
;[Doc]
(defun gd-draw-circle (view dx dy dradius &optional color-code)
(without-interrupts
; (unless (eq *current-view* view) (focus-view view))
(with-focused-view view
(when color-code (set-color-if-needed (view-window view) color-code))
(ccl::with-rectangle-arg
(r (- dx dradius) (- dy dradius) (+ dx dradius) (+ dy dradius))
(#_FrameOval r)))))
;[Doc]
(defun gd-draw-arc (view dx dy dradius start-angle angle &optional color-code)
(without-interrupts
; (unless (eq *current-view* view) (focus-view view))
(with-focused-view view
(when color-code (set-color-if-needed (view-window view) color-code))
(ccl::with-rectangle-arg
(r (- dx dradius) (- dy dradius) (+ dx dradius) (+ dy dradius))
(#_FrameArc r (- 90 start-angle) (- angle))))))
;[Doc]
(defun gd-draw-disk (view dx dy dradius &optional color-code)
(without-interrupts
; (unless (eq *current-view* view) (focus-view view))
(with-focused-view view
(when color-code (set-color-if-needed (view-window view) color-code))
(ccl::with-rectangle-arg
(r (- dx dradius) (- dy dradius) (+ dx dradius) (+ dy dradius))
(#_PaintOval r)))))
;[Doc]
(defun gd-draw-text (view text font dx dy &optional color-code)
(without-interrupts
; (unless (eq *current-view* view) (focus-view view))
(with-focused-view view
(when color-code (set-color-if-needed (view-window view) color-code))
(#_MoveTo :long (make-point dx dy))
(let ((old-font (view-font view)))
(unwind-protect
(progn (set-view-font view font)
(stream-write-string view text 0 (length text)))
(set-view-font view old-font))))))
;[Doc]
(defun gd-draw-text-centered (view string font dx dy &optional color-code)
(let ((half-length (round (* .5 (gd-text-width view string font))))
(half-height (round (* .5 (gd-text-height view string font)))))
(gd-draw-text view string font (- dx half-length) (- dy half-height) color-code)))
(defun gd-text-width (view-ignore string character-style) ;[Doc]
(declare (ignore view-ignore))
(string-width string character-style))
;[Doc]
(defun gd-text-height (view-ignore text-ignore character-style)
(declare (ignore view-ignore text-ignore))
(font-info character-style))
; gd-read-cursor (calls view-mouse-position and converts coords)
;[Doc]
(defun gd-get-cursor-position (view)
"Returns the current cursor position in appropriate coordinates"
(let* ((point (view-mouse-position view))
(dx (point-h point))
(dy (point-v point)))
(values dx dy)))
;;; G-GRAPHICS
(export 'g-clear)
(export 'g-make-visible)
(export 'g-draw-point)
(export 'g-draw-line)
(export 'g-draw-line-r)
(export 'g-outline-rect)
(export 'g-outline-rect-r)
(export 'g-fill-rect)
(export 'g-fill-rect-r)
(export 'g-draw-circle)
(export 'g-draw-disk)
(export 'g-draw-arc)
(export 'g-draw-text)
(export 'g-draw-text-centered)
(export 'g-text-width)
(export 'g-text-height)
(export 'g-get-cursor-position)
;[Doc]
(defun g-clear (view &optional (color (g-color-off view)))
(multiple-value-bind (min-x min-y max-x max-y) (gd-get-coordinate-system view)
(gd-fill-rect view min-x min-y max-x max-y color)))
(defun g-make-visible (view) ;[Doc]
(if (typep view 'window)
(window-select view)
(g-make-visible (view-container view))))
(defun g-draw-point (view x y &optional color) ;[Doc]
(let ((dx (gd-coord-x view x))
(dy (gd-coord-y view y)))
(gd-draw-point view dx dy color)))
(defun g-draw-line (view x1 y1 x2 y2 &optional color) ;[Doc]
(let ((dx1 (gd-coord-x view x1))
(dy1 (gd-coord-y view y1))
(dx2 (gd-coord-x view x2))
(dy2 (gd-coord-y view y2)))
(gd-draw-line view dx1 dy1 dx2 dy2 color)))
;[Doc]
(defun g-draw-line-r (view x y delta-x delta-y &optional color)
(g-draw-line view x y (+ x delta-x) (+ y delta-y) color))
(defun g-outline-rect (view x1 y1 x2 y2 &optional color) ;[Doc]
(let ((dx1 (gd-coord-x view x1))
(dy1 (gd-coord-y view y1))
(dx2 (gd-coord-x view x2))
(dy2 (gd-coord-y view y2)))
(gd-outline-rect view dx1 dy1 dx2 dy2 color)))
;[Doc]
(defun g-outline-rect-r (view x y delta-x delta-y &optional color)
(let ((dx1 (gd-coord-x view x))
(dy1 (gd-coord-y view y))
(dx2 (gd-coord-x view (+ x delta-x)))
(dy2 (gd-coord-y view (+ y delta-y))))
(gd-outline-rect view dx1 dy1 dx2 dy2 color)))
(defun g-fill-rect (view x1 y1 x2 y2 &optional color) ;[Doc]
(let ((dx1 (gd-coord-x view x1))
(dy1 (gd-coord-y view y1))
(dx2 (gd-coord-x view x2))
(dy2 (gd-coord-y view y2)))
(gd-fill-rect view dx1 dy1 dx2 dy2 color)))
;[Doc]
(defun g-fill-rect-r (view x y delta-x delta-y &optional color)
(let ((dx1 (gd-coord-x view x))
(dy1 (gd-coord-y view y))
(dx2 (gd-coord-x view (+ x delta-x)))
(dy2 (gd-coord-y view (+ y delta-y))))
(gd-fill-rect view dx1 dy1 dx2 dy2 color)))
(defun g-draw-circle (view x y radius &optional color) ;[Doc]
(let ((dx (gd-coord-x view x))
(dy (gd-coord-y view y))
(dradius (gd-offset-x view radius)))
(gd-draw-circle view dx dy dradius color)))
(defun g-draw-disk (view x y radius &optional color) ;[Doc]
(let ((dx (gd-coord-x view x))
(dy (gd-coord-y view y))
(dradius (gd-offset-x view radius)))
(gd-draw-disk view dx dy dradius color)))
;[Doc]
(defun g-draw-arc (view x y radius start-angle angle &optional color)
(let ((dx (gd-coord-x view x))
(dy (gd-coord-y view y))
(dradius (gd-offset-x view radius)))
(gd-draw-arc view dx dy dradius start-angle angle color)))
(defun g-draw-text (view string font x y &optional color) ;[Doc]
(let ((dx (gd-coord-x view x))
(dy (gd-coord-y view y)))
(gd-draw-text view string font dx dy color)))
;[Doc]
(defun g-draw-text-centered (view string font x y &optional color)
(let ((half-length (* .5 (g-text-width view string font)))
(half-height (* .5 (g-text-height view string font))))
(g-draw-text view string font (- x half-length) (- y half-height) color)))
;[Doc]
(defun g-text-width (view string character-style)
(g-offset-x view (gd-text-width view string character-style)))
(defun g-text-height (view text character-style) ;[Doc]
(g-offset-y view (gd-text-height view text character-style)))
;[Doc]
(defun g-get-cursor-position (view)
"Returns the current cursor position in appropriate coordinates"
(multiple-value-bind (dx dy) (gd-get-cursor-position view)
(when dx
(g-coords view dx dy))))
;;; EVENTS
; View-click-event-handler for a gus-window calls g-click-event-handler and
; gd-click-event-handler for each gus context.
; Window-mouse-up-event-handler for a gus-window calls
; g-mouse-up-event-handler for each gus-context.
(export '(gd-click-event-handler g-click-event-handler
g-mouse-up-event-handler gd-cursor g-cursor
g-draw-view view-draw-contents
view-click-event-handler
*grow-cursor* *cross-hair-cursor*))
(defmethod view-draw-contents ((view g-view))
(g-draw-view view))
(defmethod g-draw-view ((view g-view)) ;[Doc]
(loop for child in (g-get-children view)
do (view-draw-contents child)))
; To respond to a mouse click on a view you specialize a method
; (gd-click-event-handler g-view dx dy)
; or (g-click-event-handler g-view x y)
; which will be called each time there is a mouse click in that view.
(defmethod view-click-event-handler :after ((view g-view) point-of-click)
(let ((dx (point-h point-of-click))
(dy (point-v point-of-click)))
(gd-click-event-handler view dx dy)
(g-click-event-handler view
(g-coord-x view dx)
(g-coord-y view dy))))
(defmethod gd-click-event-handler ((view g-view) dx dy) ;[Doc]
(declare (ignore dx dy)))
(defmethod g-click-event-handler ((view g-view) x y) ;[Doc]
(declare (ignore x y)))
(defmethod window-mouse-up-event-handler ((view g-view))
(g-mouse-up-event-handler view))
(defmethod g-mouse-up-event-handler ((view g-view)) )
(defmethod view-cursor ((view g-view) point-of-click)
(let ((dx (point-h point-of-click))
(dy (point-v point-of-click)))
(or (g-cursor view
(g-coord-x view dx)
(g-coord-y view dy))
(gd-cursor view dx dy)
*arrow-cursor*)))
(defmethod gd-cursor ((view g-view) dx dy) ;[Doc]
(declare (ignore dx dy)))
(defmethod g-cursor ((view g-view) x y) ;[Doc]
(declare (ignore x y)))
;;;;;;;;;;;;;;;;;;;;
;;
;; cursor hacking
;;
(defun make-cursor (data-string mask-string hotspot)
(when (or (> (length (string data-string)) 64)
(> (length (string mask-string)) 64))
(error "data-string & mask-string must be < 64 chars long"))
(rlet ((data :bits16)
(mask :bits16))
(with-pstrs ((data-str data-string)
(mask-str mask-string))
(#_StuffHex :ptr data :ptr data-str)
(#_StuffHex :ptr mask :ptr mask-str))
(make-record :cursor
:data data
:mask mask
:hotspot hotspot)))
(defun make-grow-cursor ()
(make-cursor "00003FC02040204027F82448244824483FC80408040807F80000000000000000"
"00003FC03FC03FC03FF83FF83FF83FF83FF807F807F807F80000000000000000"
#@(2 3)))
(defvar *grow-cursor* (make-grow-cursor))
;the cross-hair-cursor
(defun make-cross-hair-cursor ()
(make-cursor "04000400040004000400FFE00400040004000400040004000000000000000000"
"0000000000000000000000000000000000000000000000000000000000000000"
#@(5 5)))
(defvar *cross-hair-cursor* (make-cross-hair-cursor))
;;; ADDITIONAL
(export '(gd-draw-arrow gd-draw-arrowhead g-draw-arrow g-draw-arrowhead
gd-draw-arrow-r gd-draw-arrowhead-r g-draw-arrow-r g-draw-arrowhead-r))
;[Doc]
(defun gd-draw-arrow (view dx1 dy1 dx2 dy2 &optional color)
"Draws an arrow starting at dx1,dy1 and ending at dx2,dy2 of color"
(gd-draw-arrowhead view dx1 dy1 dx2 dy2 1.0 0.25 color))
;[Doc]
(defun gd-draw-arrow-r (view dx dy delta-x delta-y &optional color)
"Draws an arrow starting at dx,dy and ending at dx+delta-x,dy+delta-y of color"
(gd-draw-arrowhead view dx dy (+ dx delta-x) (+ dy delta-y) 1.0 0.25 color))
(defvar angle-tangent 0.7)
; Angle-tangent is the tangent of the angle between the base main
; part of the arrow and one of the two parts of the arrowhead. I don't
; know what happens if you make this parameter negative. The default
; value is 0.7.
;[Doc]
(defun gd-draw-arrowhead (view dx1 dy1 dx2 dy2 body-size head-size &optional color)
"Draws an arrowhead dx2,dy2 from dx1,dy1 of color and sizes"
(let ((delta-x (* (- dx2 dx1) head-size))
(delta-y (* (- dy2 dy1) head-size)))
(unless (= 0 body-size) ;draw arrow body
(gd-draw-line-r view dx2 dy2
(round (* body-size (- dx1 dx2)))
(round (* body-size (- dy1 dy2))) color))
(gd-draw-line-r view dx2 dy2
(round (- (* (- delta-y) Angle-tangent) delta-x))
(round (- (* delta-x Angle-tangent) delta-y))
color)
(gd-draw-line-r view dx2 dy2
(round (- (* delta-y Angle-tangent) delta-x))
(round (- (* (- delta-x) Angle-tangent) delta-y))
color)))
;[Doc]
(defun gd-draw-arrowhead-r (view dx dy delta-x delta-y body-size head-size &optional color)
"Draws an arrowhead starting at dx,dy and ending at dx+delta-x,dy+delta-y of color and sizes"
(gd-draw-arrowhead view dx dy (+ dx delta-x) (+ dy delta-y) body-size head-size color))
(defun g-draw-arrow (view x1 y1 x2 y2 &optional color) ;[Doc]
(let ((dx1 (gd-coord-x view x1))
(dy1 (gd-coord-y view y1))
(dx2 (gd-coord-x view x2))
(dy2 (gd-coord-y view y2)))
(gd-draw-arrowhead view dx1 dy1 dx2 dy2 1.0 0.25 color)))
;[Doc]
(defun g-draw-arrow-r (view x y delta-x delta-y &optional color)
(g-draw-arrow view x y (+ x delta-x) (+ y delta-y) color))
;[Doc]
(defun g-draw-arrowhead (view x1 y1 x2 y2 body-size head-size &optional color)
(let ((dx1 (gd-coord-x view x1))
(dy1 (gd-coord-y view y1))
(dx2 (gd-coord-x view x2))
(dy2 (gd-coord-y view y2)))
(gd-draw-arrowhead view dx1 dy1 dx2 dy2 body-size head-size color)))
;[Doc]
(defun g-draw-arrowhead-r (view x y delta-x delta-y body-size head-size &optional color)
(g-draw-arrowhead view x y (+ x delta-x) (+ y delta-y) body-size head-size color))
(export 'maintain-g-viewports-of-children)
(defclass maintain-g-viewports-of-children (g-view) () )
(defmethod g-accept-new-viewport-size
((view maintain-g-viewports-of-children))
(let* ((children (g-get-children view))
(g-viewports (loop for child in children
collect (multiple-value-list
(g-get-viewport child)))))
(call-next-method view)
(loop for child in children
for g-viewport in g-viewports
do (apply #'g-set-viewport child g-viewport))))
;;; FINAL INITIALIZATION
(unless (boundp '*g-device*)
(setq *g-device* (make-instance 'g-device)))
;;; More things, on g
(export '(with-g-coordinate-system
with-g-cs
with-g-coordinate-system-r
with-g-cs-r
with-g-coordinate-system-scale
with-g-cs-scale))
(defmacro with-g-coordinate-system ((view x1 y1 x2 y2 &optional corner) &body body)
`(multiple-value-bind (oldx1 oldy1 oldx2 oldy2 old-corner) (g-get-cs ,view)
(unwind-protect
(progn (g-set-coordinate-system ,view ,x1 ,y1 ,x2 ,y2 ,corner)
. ,body)
(g-set-coordinate-system ,view oldx1 oldy1 oldx2 oldy2 old-corner))))
(defmacro with-g-cs ((view x1 y1 x2 y2 &optional corner) &body body)
`(with-g-coordinate-system (,view ,x1 ,y1 ,x2 ,y2 ,corner) . ,body))
(defmacro with-g-coordinate-system-r ((view x y delta-x delta-y &optional corner) &body body)
`(multiple-value-bind (oldx1 oldy1 oldx2 oldy2 old-corner) (g-get-cs ,view)
(unwind-protect
(progn (g-set-coordinate-system-r ,view ,x ,y ,delta-x ,delta-y ,corner)
. ,body)
(g-set-coordinate-system ,view oldx1 oldy1 oldx2 oldy2 old-corner))))
(defmacro with-g-cs-r ((view x1 y1 x2 y2 &optional corner) &body body)
`(with-g-coordinate-system-r (,view ,x1 ,y1 ,x2 ,y2 ,corner) . ,body))
(defmacro with-g-coordinate-system-scale ((view x y xscale &optional yscale corner) &body body)
`(multiple-value-bind (oldx1 oldy1 oldx2 oldy2 old-corner) (g-get-cs ,view)
(unwind-protect
(progn (g-set-coordinate-system-scale ,view ,x ,y ,xscale ,yscale ,corner)
. ,body)
(g-set-coordinate-system ,view oldx1 oldy1 oldx2 oldy2 old-corner))))
(defmacro with-g-cs-scale ((view x y xscale &optional yscale corner) &body body)
`(with-g-coordinate-system-scale (,view ,x ,y ,xscale ,yscale ,corner) . ,body))
#|
(export 'basic-g-colors) ; mix this with views to make color-slots
(export '(black white pink red orange yellow green dark-green light-blue blue purple
brown tan light-gray gray dark-gray flip invisible on off))
(defclass basic-g-colors (view)
(black white pink red orange yellow green dark-green light-blue blue purple
brown tan light-gray gray dark-gray flip invisible on off))
(defmethod initialize-instance ((view basic-g-colors) &key)
(without-event-processing
(call-next-method)
(with-slots (black white pink red orange yellow green dark-green light-blue blue purple
brown tan light-gray gray dark-gray flip invisible on off) view
(setq black (g-color-black view))
(setq white (g-color-white view))
(setq pink (g-color-pink view))
(setq red (g-color-red view))
(setq orange (g-color-orange view))
(setq yellow (g-color-yellow view))
(setq green (g-color-green view))
(setq dark-green (g-color-dark-green view))
(setq light-blue (g-color-light-blue view))
(setq blue (g-color-blue view))
(setq purple (g-color-purple view))
(setq brown (g-color-brown view))
(setq tan (g-color-tan view))
(setq light-gray (g-color-light-gray view))
(setq gray (g-color-gray view))
(setq dark-gray (g-color-dark-gray view))
(setq flip (g-color-flip view))
(setq invisible (g-color-invisible view))
(setq on (g-color-on view))
(setq off (g-color-off view)))))
|#
(export '(gd-within-viewport-p g-within-viewport-p))
(defun gd-within-viewport-p (view dx dy)
"Is dx,dy within viewport of view, in the coordinate system of the parent of view?"
(multiple-value-bind (x1 y1 x2 y2) (gd-get-viewport view)
(and (< x1 dx x2)
(< y1 dy y2))))
(defun g-within-viewport-p (view x y)
"Is x,y within viewport of view, in the coordinate system of the parent of view?"
(multiple-value-bind (x1 y1 x2 y2) (g-get-viewport view)
(and (< x1 x x2)
(< y1 y y2))))
;