aboutsummaryrefslogtreecommitdiffhomepage
path: root/examples/14-canvas-paint.lisp
blob: 35c74ba4b4573db6b9a5e708ee549c68e9976831 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
;;;; 14-canvas-paint.lisp

(defpackage #:ww.examples/14
  (:use #:cl)
  (:export #:start))

(in-package #:ww.examples/14)

(defclass pixel-whomp (ww:application) ())

(defclass pwcanvas (ww:canvas)
  ((pen-down-pos :accessor pen-down-pos :initform nil)
   (mode :initform :draw :accessor mode)
   (pen-width :initform 1 :accessor pen-width)))

(defun scale-pos-to-canvas (can ex ey)
  (let
      ((scale (ww::scale-x can))
       (x (ww:x can))
       (y (ww:y can)))
    (values (/ (- ex x) scale)
            (/ (- ey y) scale))))

(defun set-pen-position (can ex ey)
  "x and y are as received from mouse events. adjust the pen position
   to account for scale, or set to nil of x and y are curren toff the
   canvas"
  (let
      ((pos
         (or (pen-down-pos can)
             (list 0 0))))
    (multiple-value-bind (nx ny) (scale-pos-to-canvas can ex ey) 
      (setf (car pos) nx
            (cadr pos) ny
            (pen-down-pos can) pos))
    pos))

(defun shift-mod-p (mods)
  (or (member :rshift mods)
      (member :lshift mods)))

(ww::defhandler keyboard-commands
    (ww::on-keydown (target scancode mods)
      (format t "~a~%"
              (list :code scancode :mods mods))
      (case scancode
        (:scancode-equals
         (when (shift-mod-p mods)
           (incf (pen-width target))))
        (:scancode-minus
         (setf (pen-width target)
               (max 0 (1- (pen-width target))))))))

(ww::defhandler change-mode-start
 (ww::on-keydown ()
   (case scancode
     ((:scancode-lshift :scancode-rshift)
      (setf (mode target) :drag)))))

(ww::defhandler change-mode-stop
    (ww::on-keyup ()
      (setf (mode target) :draw)))

(ww::defhandler zoom
    (ww::on-mousewheel (target h v)
      (format t "v: ~a~% " v)
      (ww:scale-by target
                   (if (plusp v) 1.1 0.9))))

(ww:defhandler canvas-mousebutton-down
    (ww:on-mousedown (target x y clicks button winx winy)
      (unless (pen-down-pos target)
        (set-pen-position target x y)
        (when (eq :draw (mode target))
          (format t "foo")
          (ww::with-canvas target
            (ww:with-pen (:width (pen-width target))
              (apply 'ww:apply-pen-at (mapcar #'floor (pen-down-pos target))))
            (ww:blit target))))))

(ww::defhandler canvas-mousebutton-up
    (ww::on-mouseup (target)
      (setf (pen-down-pos target) nil)))

(defun draw (canvas x y)
  (let ((old-pos (copy-seq (pen-down-pos canvas))))
    (destructuring-bind (nx ny) (set-pen-position canvas x y)  
      (ww:with-canvas canvas 
        (ww:with-pen (:position old-pos :width (pen-width canvas))
          (ww:stroke-to nx ny))))
    (ww:blit canvas)))

(defun drag (canvas x y)
  (multiple-value-bind (cx cy) (scale-pos-to-canvas canvas x y)
    (destructuring-bind (px py) (pen-down-pos canvas) 
      (let ((tx (* (ww:scale-x canvas)
                   (- cx px)))
            (ty (* (ww:scale-x canvas)
                   (- cy py))))
        (incf (ww:x canvas) tx)
        (incf (ww:y canvas) ty)))))

(ww::defhandler canvas-mouse-move
    (ww::on-mousemotion (target x y)
      (when (pen-down-pos target) 
        (ecase (mode target)
          (:draw (draw target x y))
          (:drag (drag target x y))))))

(defmethod initialize-instance :after ((ob pwcanvas) &key)
  (ww:add-handler ob #'keyboard-commands)
  (ww:add-handler ob #'change-mode-start)
  (ww:add-handler ob #'change-mode-stop)
  (ww:add-handler ob #'zoom)
  (ww:add-handler ob #'canvas-mousebutton-up)
  (ww:add-handler ob #'canvas-mousebutton-down)
  (ww:add-handler ob #'canvas-mouse-move))

(defmethod ww:boot ((app pixel-whomp))
  (let ((canvas
          (make-instance 'pwcanvas
                         :pixel-width 800
                         :pixel-height 800)))
    (ww:add-unit canvas)))

(defun start ()
  (ww:start
   (make-instance 'pixel-whomp)
   :x 2350))