;;;-*-mode:lisp;package:hacks;lowercase:t;base:8-*- ;;; Magic number (defvar *crock-radius* 500) (defvar 2pi 6.2832s0) (defflavor crock-window (hour-hand minute-hand second-hand (center-x nil) (center-y nil)) (tv:process-mixin tv:window) (:default-init-plist :process '(crock-top-level) :font-map '(fonts:43vxms) :blinker-p nil :label nil)) (defun crock-top-level (window) (time:get-time) ;Make sure timebase initialized (funcall window ':top-level)) ;; Hour hand (defvar *hour-hand* '((6 0) (6 1) (6 2) (4 3) (5 4) (5 5) (6 6) (3 7) (6 10) (3 11) (6 12) (3 13) (6 14) (6 15) (6 16) (14 17) (12 20) (11 17) (6 16) (6 15) (3 14) (6 13) (3 12) (3 11) (3 10) (3 7) (3 6) (3 5) (3 4) (3 3) (3 2) (4 1))) ;; Minute hand (defvar *minute-hand* '((1 0 0) (1 0 1) (1 0 2) (2 0 3) (2 0 5) (1 0 6) (2 0 7) (1 1 10) (1 2 10) (1 2 11) (3 3 11) (3 4 12) (3 3 11) (1 2 11) (1 2 10) (1 1 10) (2 0 7) (11 0 6) (6 0 3) (100 0 2) (6 0 2) (6 0 3) (6 0 4) (6 0 6) (6 0 7) (6 0 10) (6 0 11) (6 0 12) (7 0 13) (22 0 14) (30 0 15) (14 0 14) (15 0 13) (6 0 12) (6 0 11) (6 0 10) (6 0 7) (6 0 6) (6 0 5) (6 0 4) (6 0 3) (6 0 2) (7 0 1))) (defmethod (crock-window :after :init) (ignore) (setq hour-hand (make-instance 'wide-hand ':curve *hour-hand*) minute-hand (make-instance 'wide-hand ':curve *minute-hand*) second-hand (make-instance 'thin-hand ':length *crock-radius*))) (defmethod (crock-window :after :refresh) (&optional ignore) (cond ((not tv:restored-bits-p) (funcall-self ':draw-face) (funcall hour-hand ':set-current-angle nil) (funcall minute-hand ':set-current-angle nil) (funcall second-hand ':set-current-angle nil)))) (defflavor hand ((current-angle nil)) () (:settable-instance-variables current-angle) (:required-methods :draw)) (defmethod (hand :draw-if-necessary) (window x0 y0 angle) (cond ((not (and current-angle (= angle current-angle))) (tv:prepare-sheet (window) (and current-angle (funcall-self ':draw window x0 y0 current-angle)) (setq current-angle angle) (funcall-self ':draw window x0 y0 current-angle))))) (defflavor thin-hand (length) (hand) (:initable-instance-variables length)) (defmethod (thin-hand :draw) (window x0 y0 angle) (setq angle (// (* angle 2pi) 360.)) (funcall window ':draw-line x0 y0 (fix (+ x0 (* length (cos angle)))) (fix (+ y0 (* length (sin angle)))) tv:alu-xor)) (defflavor wide-hand (curve) (hand) (:initable-instance-variables curve)) (defmethod (wide-hand :draw) (window x0 y0 angle) (draw-curve-at-angle window x0 y0 curve angle)) ;;; Angle is in degrees (defun draw-curve-at-angle (window x0 y0 curve angle &aux sin cos nseg) (setq angle (// (* angle 2pi) 360.) sin (sin angle) cos (cos angle)) (setq nseg (loop for elem in curve maximize (length (cdr elem)))) (tv:prepare-sheet (window) (loop for elem in curve with p1xs = (make-list nseg ':initial-value x0) and p1ys = (make-list nseg ':initial-value y0) and p2xs = (make-list nseg ':initial-value x0) and p2ys = (make-list nseg ':initial-value y0) for rx0 = (+ x0 (tv:sheet-inside-left window)) then rx1 and ry0 = (+ y0 (tv:sheet-inside-top window)) then ry1 as width = (pop elem) as rx1 = (+ rx0 (* width cos)) and ry1 = (+ ry0 (* width sin)) do (loop for width in elem and p1xs on p1xs and p1ys on p1ys and p2xs on p2xs and p2ys on p2ys as dx = (* width sin) and dy = (- (* width cos)) do (let ((p1x (car p1xs)) (p1y (car p1ys)) (p2x (car p2xs)) (p2y (car p2ys)) (p3x (fix (+ rx1 dx))) (p3y (fix (+ ry1 dy))) (p4x (fix (- rx1 dx))) (p4y (fix (- ry1 dy)))) (sys:%draw-triangle p1x p1y p2x p2y p3x p3y tv:alu-xor window) (sys:%draw-triangle p2x p2y p3x p3y p4x p4y tv:alu-xor window) (rplaca p1xs p3x) (rplaca p1ys p3y) (rplaca p2xs p4x) (rplaca p2ys p4y)))))) (defmethod (crock-window :draw-face) () (multiple-value-bind (w h) (funcall-self ':inside-size) (setq center-x (// w 2) center-y (// h 2))) (funcall-self ':draw-filled-in-circle center-x center-y *crock-radius* tv:alu-xor) (funcall-self ':draw-filled-in-circle center-x center-y (- *crock-radius* 4) tv:alu-xor) (loop for i from 1. to 12. do (put-string-in-circle self center-x center-y *crock-radius* (format nil "~D" i) (+ -90.0s0 (* i '#,(// 360. 12.)))))) (defun put-string-in-circle (window center-x center-y radius string angle &aux width height sin cos rad2) (setq width (// (compute-string-length string (tv:sheet-current-font window)) 2) height (// (font-char-height (tv:sheet-current-font window)) 2)) (setq angle (// (* angle 2pi) 360.) sin (sin angle) cos (cos angle) rad2 (* radius radius)) (loop with wid = (if (< cos -0.5s0) (+ width 10) width) and hei = (if (< sin -0.5s0) (+ height 4) height) for x = (+ center-x (* radius cos)) then (- x cos) and y = (+ center-y (* radius sin)) then (- y sin) when (< (+ (let ((dx (- (if ( x center-x) (- x wid) (+ x wid)) center-x))) (* dx dx)) (let ((dy (- (if ( y center-y) (- y hei) (+ y hei)) center-y))) (* dy dy))) rad2) do (funcall window ':set-cursorpos (- x (if (> cos 0.5s0) (+ width 10) width)) (- y (if (> sin 0.5s0) (+ height 4) height)) ':pixel) (funcall window ':string-out string) (return nil))) (defun compute-string-length (string font &aux cwt) (setq cwt (font-char-width-table font)) (loop with len = (string-length string) for i from 0 below len as char = (aref string i) sum (cond ((= i (1- len)) (fed:font-char-min-raster-width font char)) (cwt (aref cwt char)) (t (font-char-width font))))) (defmethod (crock-window :top-level) () (process-wait "go" #'car (locate-in-instance self 'center-x)) (loop doing (multiple-value-bind (sec min hou) (time:get-time) (setq hou (\ hou 12.)) (and (zerop hou) (setq hou 12.)) (funcall hour-hand ':draw-if-necessary self center-x center-y ;; Display accurate to 15 mins (+ -90.0s0 (// (* (+ (// (+ min 7.5s0) 15.) (* hou 4)) 360.) '#,(* 12. 4)))) (funcall minute-hand ':draw-if-necessary self center-x center-y ;; Display accurate to 1/2 minute (+ -90.0s0 (// (* (+ (// (+ sec 15.) 30.) (* min 2)) 360.) '#,(* 60. 2)))) (funcall second-hand ':draw-if-necessary self center-x center-y (+ -90.0s0 (* sec 6.))) (and (zerop sec) (zerop (\ min 30.)) (play-time (if (zerop min) hou 1)))) (process-sleep 30.))) ;Just less than a second (defun play-time (nbong) (play-string "aaaaddddssssbbbbbb--bbbbssssddddaaaaaa--ddddaaaassssbbbbbb--bbbbssssddddaaaaaaaaaa") (loop for i from 0 below nbong do (process-sleep 15.) (sys:%beep 30000 4000000))) (defvar *crock*) (defun crock () (or (boundp '*crock*) (let ((width (tv:sheet-inside-width tv:default-screen)) (height (tv:sheet-inside-height tv:default-screen))) (setq *crock* (tv:make-window 'crock-window ':width (- width 40) ':left 20 ':height (- height 200) ':top 100)))) (funcall *crock* ':expose)) (compile-flavor-methods crock-window thin-hand wide-hand) (defdemo "Crock" "Standard wall clock" (crock-demo)) (defun crock-demo () (crock) (funcall tv:selected-window ':tyi) (funcall *crock* ':bury)) ;;; Some help in drawing these things (comment (defun make-curve-list-from-spline (point-list &aux px py cx cy len) (setq len (length point-list) px (make-array len) py (make-array len)) (loop for (x y) in point-list as i from 0 do (aset x px i) (aset y py i)) (multiple-value (cx cy len) (tv:spline px py 20.)) (loop for i from 0 below len with x0 = 0 and y0 = 0 as x1 = (fix (aref cx i)) and y1 = (fix (aref cy i)) when ( y1 y0) collect (list (- x1 (prog1 x0 (setq x0 x1))) (prog1 y0 (setq y0 y1))))) )