;;; -*- Package: TV; Base: 10. -*- ;;; New CHOOSE-VARIABLES-VALUES type to support graphic sizes. (defprop :positive-number (prin1 read-positive-number nil nil nil "Click left to input a new number from the keyboard.") choose-variable-values-keyword) (defun read-positive-number (stream) (let ((val (read stream))) (unless (and (numberp val) (> val 0)) (ferror nil "a positive number is required")) val)) (defconst *ellipse* 10000000. "Square of the largest ellipse radius to be drawn.") ;;; This method will draw an ellipse centered at (x-center, y-center) with "radius's" ;;; of x-radius and y-radius. The points are computed via an adaptation of ;;; Brensenham's algorithim to conic sections. The equation for an ellipse is ;;; ax^2 + by^2 - c^2 = 0. The algorithim works by treating this as an error term. ;;; The key to updating the error term is to note that a unit step in y will change ;;; the error by 2by + b. (similarly for x) Decrementing y will change the error ;;; term by 2by - b. ;;; The coordinate of greatest movement is stepped by unit increments. The other ;;; coordinate is stepped conditionally depending on the size of the error term. ;;; The ellipse is draw in two passes. First we step the y and conditionaly step x. ;;; When the derivative passes through 1, we switch over to stepping x and condionally ;;; stepping y. Each time we step, we draw four points. ;;; The conditional step is determined by testing whether the step will make the ;;; size of the error smaller. ;;; Very special kludgey macro for :draw-ellipse. Borrowed from :draw-circle. (defmacro ellipse-draw-clipped-point (x-val y-val) `(or (< ,x-val il) ( ,x-val ir) (< ,y-val it) ( ,y-val ib) (aset (boole alu -1 (aref tv:screen-array ,x-val ,y-val)) tv:screen-array ,x-val ,y-val))) (defmethod (graphics-mixin :draw-ellipse) #+LMI (x-center y-center x-radius y-radius &optional (alu tv:char-aluf)) #+Symbolics (y-center x-center y-radius x-radius &optional (alu tv:char-aluf)) (setq x-radius (fixr x-radius) y-radius (fixr y-radius)) (let ((il (tv:sheet-inside-left)) (it (tv:sheet-inside-top)) (ir (tv:sheet-inside-right)) (ib (tv:sheet-inside-bottom)) (a (fixr (// *ellipse* (* y-radius y-radius)))) (b (fixr (// *ellipse* (* x-radius x-radius))))) (setq y-center (fixr (+ y-center il)) x-center (fixr (+ x-center it))) (prepare-sheet (self) (loop for error = 0. then (+ error bx bx b) for x upfrom 0 for ay = (* a y) for bx = (* b x) for y = y-radius then (cond (( error ay) (setq error (- error ay ay (- a))) (setq ay (- ay a)) (1- y)) (t y)) while (> ay bx) for yr = (+ y-center y) for xb = (+ x-center x) for yl = (- y-center y) for xt = (- x-center x) do (ellipse-draw-clipped-point yr xb) do (ellipse-draw-clipped-point yl xb) do (ellipse-draw-clipped-point yr xt) do (ellipse-draw-clipped-point yl xt)) (loop for error = 0. then (+ error ay ay a) for y upfrom 0 for ay = (* a y) for bx = (* b x) for x = x-radius then (cond (( error bx) (setq error (- error bx bx (- b))) (setq bx (- bx b)) (1- x)) (t x)) while (> bx ay) for yr = (+ y-center y) for xb = (+ x-center x) for yl = (- y-center y) for xt = (- x-center x) do (ellipse-draw-clipped-point yr xb) do (ellipse-draw-clipped-point yl xb) do (ellipse-draw-clipped-point yr xt) do (ellipse-draw-clipped-point yl xt))))) ;;; **************** ;;; Circular-blinker stuff ;;; **************** (defflavor circular-blinker ((radius 5.)) (blinker) :gettable-instance-variables :settable-instance-variables :initable-instance-variables) (defmethod (circular-blinker :size) () (let ((two (* 2. radius))) (values two two))) ;;; This always runs inside a sheet-prepared form. (defmethod (circular-blinker :blink) () (let ((rad (float radius))) (do ((x 0.0s0) (y rad) (fy nil nfy) (nfy) (fx) (nfx) ( (- (* 0.9s0 (// rad)))) (owidth) (nwidth) (flag nil)) (nil) (setq nwidth (fix (* x 2)) nfy (fix (- y-pos y)) nfx (fix (- x-pos x))) (if (eq nfy fy) ;if same line (and (> nwidth owidth) ;and this line wider (setq owidth nwidth fx nfx)) ;remember to draw it ;; different lines, draw last one (and fy (draw-rectangle-inside-clipped owidth 1 (1+ fx) (1+ fy) tv:alu-xor sheet)) (setq owidth nwidth fx nfx fy nfy) ;remember new values (if ( owidth 0) (and flag (return t)) (setq flag t))) (setq y (+ y (*  x)) x (- x (*  y)))))) ;;; **************** ;;; Wide-curve-blinker stuff ;;; **************** ;;; This is used to highlight arcs. (defflavor wide-curve-blinker ((x-array (make-array 10 ':leader-list '(0))) (y-array (make-array 10 ':leader-list '(0))) (arc nil)) (tv:blinker) :gettable-instance-variables :settable-instance-variables :initable-instance-variables) ;;; The body of this is "borrowed" from the graphics-mixin, draw-wide-curve ;;; method. ;;; (defmethod (wide-curve-blinker :blink) (&aux curve-width end (alu tv:alu-xor)) (setq end (array-active-length x-array)) (setq curve-width (// graph:*line-highlight-width* 2.0s0)) (do ((i 0 (1+ i)) (x0) (y0) (x1) (y1) (px1) (py1) (px2) (py2) (px3) (py3) (px4) (py4)) (( i end)) (setq x0 x1) (or (setq x1 (aref x-array i)) (return nil)) (setq y0 y1) (or (setq y1 (aref y-array i)) (return nil)) (or (= i 0) (let ((dx (- x1 x0)) (dy (- y1 y0)) len) (setq len (sqrt (+ (* dx dx) (* dy dy)))) (and (zerop len) (= i 1) (setq len 1)) (cond ((not (zerop len)) (psetq dx (// (* curve-width dy) len) dy (// (* curve-width dx) len)) (if (= i 1) (setq px1 (fix (- x0 dx)) py1 (fix (+ y0 dy)) px2 (fix (+ x0 dx)) py2 (fix (- y0 dy))) (setq px1 px3 py1 py3 px2 px4 py2 py4)) (setq px3 (fix (- x1 dx)) py3 (fix (+ y1 dy)) px4 (fix (+ x1 dx)) py4 (fix (- y1 dy))) (sys:%draw-triangle (+ (tv:sheet-inside-left tv:sheet) px1) (+ (tv:sheet-inside-top tv:sheet) py1) (+ (tv:sheet-inside-left tv:sheet) px2) (+ (tv:sheet-inside-top tv:sheet) py2) (+ (tv:sheet-inside-left tv:sheet) px4) (+ (tv:sheet-inside-top tv:sheet) py4) alu tv:sheet) (sys:%draw-triangle (+ (tv:sheet-inside-left tv:sheet) px1) (+ (tv:sheet-inside-top tv:sheet) py1) (+ (tv:sheet-inside-left tv:sheet) px3) (+ (tv:sheet-inside-top tv:sheet) py3) (+ (tv:sheet-inside-left tv:sheet) px4) (+ (tv:sheet-inside-top tv:sheet) py4) alu tv:sheet))))))) ;;; This is a bit of a kludge, but we wanted to do the translation ;;; here, to avoid consing elsewhere. (defmethod (wide-curve-blinker :set-points) (graph-window x-and-y-values) (send self ':set-visibility nil) (fillarray x-array nil) (fillarray y-array nil) (setf (fill-pointer x-array) 0) (setf (fill-pointer y-array) 0) (loop for (x y . rest) on x-and-y-values by 'cddr do (array-push-extend x-array (send graph-window ':translate-x x)) do (array-push-extend y-array (send graph-window ':translate-y y)))) (defmethod (wide-curve-blinker :size) () (loop for x being the array-elements of x-array for y being the array-elements of y-array maximize x into maxx maximize y into maxy minimize x into minx minimize y into miny finally (values (- maxx minx) (- maxy miny))))