123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227 |
- ;;;; robot-ltk.lisp
- (in-package #:robot-ltk)
- (defparameter *originx* 5)
- (defparameter *originy* 5)
- (defparameter *cell-size* 32)
- (defparameter *active-robot-colour* :green)
- (defparameter *inactive-robot-colour* :red)
- (defparameter *painted-colour* :yellow)
- (defparameter *grid-colour* :grey)
- (defparameter *dot-radius* 2)
- (defparameter *led-size* 32)
- (defparameter *yes-led-on-colour* :green)
- (defparameter *no-led-on-colour* :red)
- (defparameter *window-title* "Робот")
- (defclass robot-map (frame)
- ((cells :accessor robot-map-cells)
- (robot :accessor robot-map-robot)))
- (defun draw-cells (canvas app)
- (let* ((world (robot-app-world app))
- (columns (landscape-width (landscape world)))
- (rows (landscape-height (landscape world))))
- (let ((cells (make-array (list rows columns))))
- (dotimes (i rows)
- (dotimes (j columns)
- (let ((r (make-rectangle canvas
- (+ *originx* (* j *cell-size*))
- (+ *originy* (* i *cell-size*))
- (+ *originx* (* (1+ j) *cell-size*))
- (+ *originy* (* (1+ i) *cell-size*)))))
- (configure r :outline :gray)
- (setf (aref cells i j) r))))
- (update-cells cells app)
- cells)))
- (defun draw-dotted-cells (canvas app)
- (loop for (x y) in (dotted-cells (landscape (robot-app-world app)))
- for x0 = (- (+ (* (+ x 1/2) *cell-size*) *originx*) *dot-radius*)
- for y0 = (- (+ (* (+ y 1/2) *cell-size*) *originy*) *dot-radius*)
- for x1 = (+ x0 (* 2 *dot-radius*))
- for y1 = (+ y0 (* 2 *dot-radius*))
- for handle = (create-oval canvas x0 y0 x1 y1)
- do (itemconfigure canvas handle :fill :black)))
- (defun update-cells (cells app)
- (loop for (x y) in (painted-cells (robot-app-world app))
- do (configure (aref cells y x) :fill *painted-colour*))
- cells)
- (defun draw-walls (canvas app)
- (let* ((world (robot-app-world app))
- (hwall (landscape-hwalls (landscape world)))
- (vwall (landscape-vwalls (landscape world))))
- (loop for (nx ny) in hwall
- for y = (+ *originy* (* ny *cell-size*))
- for x1 = (+ *originx* (* nx *cell-size*))
- for x2 = (+ x1 *cell-size*)
- for line = (create-line canvas (list x1 y x2 y))
- do (itemconfigure canvas line :fill :red)
- (itemconfigure canvas line :width :3))
- (loop for (nx ny) in vwall
- for x = (+ *originx* (* nx *cell-size*))
- for y1 = (+ *originy* (* ny *cell-size*))
- for y2 = (+ y1 *cell-size*)
- for line = (create-line canvas (list x y1 x y2))
- do (itemconfigure canvas line :fill :red)
- (itemconfigure canvas line :width :3))))
- (defun robot-oval-coords (world-x world-y)
- (let* ((r (/ *cell-size* 4))
- (x0 (+ (+ *originx* (* (+ world-x 1/2) *cell-size*)) (- r)))
- (y0 (+ (+ *originy* (* (+ world-y 1/2) *cell-size*)) (- r))))
- (list x0 y0 (+ x0 (* 2 r)) (+ y0 (* 2 r)))))
- (defun create-robot (canvas)
- (let ((robot-sprite (apply #'make-oval canvas (robot-oval-coords 0 0))))
- (configure robot-sprite :state :hidden)
- robot-sprite))
- (defun update-robot (robot-sprite app)
- (let ((robot (robot (robot-app-world app))))
- (when robot
- (setf (coords robot-sprite) (apply #'robot-oval-coords (robot-position (robot-app-world app))))
- (configure robot-sprite
- :state :normal
- :fill (if (robot-active-p robot)
- *active-robot-colour*
- *inactive-robot-colour*))))
- robot-sprite)
- (defun draw-robot (canvas app)
- (update-robot (create-robot canvas) app))
- (defun create-robot-map (app master)
- (let* ((landscape (landscape (robot-app-world app)))
- (map (make-instance 'robot-map :master master))
- (canvas (make-instance 'canvas
- :master map
- :height (+ (* (landscape-height landscape) *cell-size*)
- (* 2 *originy*))
- :width (+ (* (landscape-width landscape) *cell-size*)
- (* 2 *originx*)))))
- (configure canvas :background :white)
- (setf (robot-map-cells map) (draw-cells canvas app))
- (draw-walls canvas app)
- (draw-dotted-cells canvas app)
- (setf (robot-map-robot map) (draw-robot canvas app))
- (pack canvas)
- map))
- (defun robot-app-update-map (app)
- (with-atomic
- (let ((map (robot-app-component :map app)))
- (update-cells (robot-map-cells map) app)
- (update-robot (robot-map-robot map) app))))
- (defun create-five-buttons (master caption labels-commands)
- (loop with main-frame = (make-instance 'frame :master master)
- with frame = (make-instance 'frame :master main-frame)
- with label = (make-instance 'label :master main-frame :text caption)
- for (text command) in labels-commands
- for position in '((0 1) (1 0) (1 1) (1 2) (2 1))
- for button = (make-instance 'button
- :master frame
- :text text
- :command command
- :style "Robot.TButton")
- do (apply #'grid button position)
- finally (grid label 0 0) (grid frame 1 0) (return main-frame)))
- (defclass robot-led (frame)
- ((oval :accessor led-oval)
- (on-colour :reader on-colour :initarg :on-colour)))
- (defun create-led (master caption on-colour)
- (let* ((led (make-instance 'robot-led
- :master master
- :height *led-size*
- :width *led-size*
- :on-colour on-colour))
- (label (make-instance 'label
- :master led
- :text caption))
- (canvas (make-canvas led :width (+ *led-size* 2) :height (+ *led-size* 2)))
- (oval (make-oval canvas 1 1 (1+ *led-size*) (1+ *led-size*))))
- (grid label 0 0)
- (grid canvas 1 0)
- (setf (led-oval led) oval)
- (set-robot-led-mode :off led)
- led))
- (defun set-robot-led-mode (mode led)
- (with-atomic
- (configure (led-oval led) :fill (if (eql mode :on)
- (on-colour led)
- :white))))
- (defclass robot-data (frame)
- ((data-label :reader data-label)))
- (defun create-robot-data (master caption callback)
- (let* ((frame (make-instance 'robot-data :master master))
- (button (make-instance 'button :master frame :text caption :command callback :style "Robot.TButton"))
- (label (make-instance 'label :master frame)))
- (grid button 0 0 :padx 10)
- (grid label 0 1)
- (setf (slot-value frame 'data-label) label)
- frame))
- (defun set-robot-data (data component)
- (with-atomic
- (setf (text (data-label component)) data)))
- (defparameter *buttons* '((motion ("↑" :up) ("←" :left) ("К" :paint) ("→" :right) ("↓" :down))
- (query ("↑" :wall-up-p) ("←" :wall-left-p) ("К?" :paintedp) ("→" :wall-right-p) ("↓" :wall-down-p))
- (alt-query ("↑" :clear-up-p) ("←" :clear-left-p) ("С?" :blankp) ("→" :clear-right-p) ("↓" :clear-down-p))
- (temperature ("℃" :temperature))
- (radiation ("☢" :radiation))))
- (defun all-callbacks (app)
- (loop for (category . buttons) in *buttons*
- collect (cons category (loop for (text callback-name) in buttons
- do (robot-app-callback callback-name app)
- collect (list text (robot-app-callback callback-name app))))))
- (defun create-robot-app (app)
- (send-wish "ttk::style configure Robot.TButton -width 5")
- (wm-title *tk* *window-title*)
- (let* ((main-frame (make-instance 'frame :master *tk*))
- (callbacks (all-callbacks app)))
- (grid (setf (robot-app-component :map app) (create-robot-map app main-frame)) 0 0 :columnspan 2)
- (grid (create-five-buttons main-frame "Идти" (cdr (assoc 'motion callbacks))) 1 0 :columnspan 2 :pady "10")
- (grid (create-five-buttons main-frame "Занято?" (cdr (assoc 'query callbacks))) 2 0 :padx 5)
- (grid (create-five-buttons main-frame "Свободно?" (cdr (assoc 'alt-query callbacks))) 2 1 :padx 5)
- (grid (setf (robot-app-component :yes-led app) (create-led main-frame "Да" *yes-led-on-colour*)) 3 0 :sticky :e :padx 5)
- (grid (setf (robot-app-component :no-led app) (create-led main-frame "Нет" *no-led-on-colour*)) 3 1 :sticky :w :padx 5)
- (grid (setf (robot-app-component :temperature app) (apply #'create-robot-data main-frame (cadr (assoc 'temperature callbacks)))) 4 0)
- (grid (setf (robot-app-component :radiation app) (apply #'create-robot-data main-frame (cadr (assoc 'radiation callbacks)))) 4 1)
- (grid main-frame 0 0 :padx 12 :pady 3)))
- ;; TODO Use mailboxes
- (defun ltk-background (&optional thunk)
- (let ((box (make-mailbox)))
- (make-thread (lambda ()
- (setf *robot-app-running-p* t)
- (unwind-protect
- (with-ltk ()
- (mailbox-send-message box *wish*)
- (when thunk
- (funcall thunk))))
- (setf *robot-app-running-p* nil))
- :name "LTK")
- (mailbox-receive-message box)))
- (defun show-robot-app (app &key (background t))
- (let ((do-stuff (lambda ()
- (create-robot-app app))))
- (setf (robot-app-wish app) (if background
- (ltk-background do-stuff)
- (prog1
- *wish*
- (with-ltk ()
- (funcall do-stuff)))))))
|