canvas-shapes.lisp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. ;; This software is Copyright (c) 2003-2010 Peter Herth <herth@peter-herth.de>
  2. ;; Portions Copyright (c) 2005-2010 Thomas F. Burdick
  3. ;; Portions Copyright (c) 2006-2010 Cadence Design Systems
  4. ;; Portions Copyright (c) 2010 Daniel Herring
  5. ;; Portions Copyright (c) 2018,2019 cage
  6. ;; The authors grant you the rights to distribute
  7. ;; and use this software as governed by the terms
  8. ;; of the Lisp Lesser GNU Public License
  9. ;; (http://opensource.franz.com/preamble.html),
  10. ;; known as the LLGPL.
  11. ;; This program is distributed in the hope that it will be useful,
  12. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. ;; GNU General Public License for more details.
  15. (in-package :nodgui.shapes)
  16. (named-readtables:in-readtable nodgui.syntax:nodgui-syntax)
  17. (defclass canvas-handler-holder ()
  18. ((handle
  19. :initform nil
  20. :accessor handle
  21. :initarg :handle
  22. :documentation "The TK handle (the ID) of this object."))
  23. (:documentation "A class that holds a canvas."))
  24. (defclass shape (canvas-holder)
  25. ((coordinates
  26. :initform '()
  27. :initarg :coordinates
  28. :accessor coordinates
  29. :documentation "The coordinates of the vertices (or control
  30. points) belonging to this shape")
  31. (fill-color
  32. :initform "#ffffff"
  33. :initarg :fill-color
  34. :accessor fill-color
  35. :documentation "The inner color of this shape")
  36. (outline-color
  37. :initform "#FFFFFF"
  38. :initarg :outline-color
  39. :accessor outline-color
  40. :documentation "The outline color of this shape")
  41. (outline-width
  42. :initform 1
  43. :initarg :outline-width
  44. :accessor outline-width
  45. :documentation "The width of the outline that surrounds this shape"))
  46. (:documentation "A generic shape to be drawn on a canvas"))
  47. (defgeneric shape-move (object dx dy)
  48. (:documentation "Move this shape by the amoount specified in dx and dx"))
  49. (defgeneric shape-move-to (object x y)
  50. (:documentation "Move this shape so that its (minimum x, minimum y)
  51. point coincides with (x, y)"))
  52. (defgeneric shape-delete (object)
  53. (:documentation "Remove this shape trom canvas"))
  54. (defmethod shape-delete ((object shape))
  55. (with-accessors ((canvas canvas)
  56. (handle handle)) object
  57. (and handle
  58. (item-delete canvas handle))))
  59. (defmethod shape-move ((object shape) dx dy)
  60. (with-accessors ((canvas canvas)
  61. (handle handle)) object
  62. (and handle
  63. (item-move canvas handle dx dy))))
  64. (defmethod shape-move-to ((object shape) x y)
  65. (with-accessors ((canvas canvas)
  66. (handle handle)) object
  67. (and handle
  68. (item-move-to canvas handle x y))))
  69. (defun create-polygon (canvas coords
  70. &key
  71. (fill-color "#0000ff") (outline-color "#ff0000")
  72. (outline-width 1))
  73. "Create a polygon.
  74. This is the low level procedure that deal with TK.
  75. canvas: the canvas where draw the polygon
  76. coords: the vertices of this polygon
  77. fill-color: the color that fills this polygon
  78. outline-color: the color of the contour that surrond this polygon
  79. outline-width: the width in pixel of the outline of this polygon"
  80. (with-read-data ()
  81. (let ((*suppress-newline-for-tcl-statements* t))
  82. (format-wish (tclize `(senddata [,(widget-path canvas) " "
  83. create polygon
  84. ,(process-coords coords) " "
  85. ,(empty-string-if-nil fill-color
  86. `(-fill {+ ,fill-color }))
  87. ,(empty-string-if-nil outline-color
  88. `(-outline {+ ,outline-color }))
  89. ,(empty-string-if-nil outline-width
  90. `(-width {+ ,outline-width }))
  91. ]))))))
  92. (defclass polygon (shape canvas-handler-holder)
  93. ()
  94. (:documentation "A filled polygon"))
  95. (defmethod initialize-instance :after ((object polygon) &key &allow-other-keys)
  96. (with-accessors ((handle handle)
  97. (fill-color fill-color)
  98. (coordinates coordinates)
  99. (canvas canvas)
  100. (outline-color outline-color)
  101. (outline-width outline-width)) object
  102. (setf handle (create-polygon canvas
  103. coordinates
  104. :fill-color fill-color
  105. :outline-color outline-color
  106. :outline-width outline-width))))
  107. (defun make-polygon (canvas coords
  108. &key
  109. (fill-color "#ffffff") (outline-color "#BEBEBE")
  110. (outline-width 1))
  111. "Make a polygon.
  112. canvas: the canvas where draw the polygon
  113. coords: the vertices of this polygon
  114. fill-color: the color that fills this polygon
  115. outline-color: the color of the contour that surrond this polygon
  116. outline-width: the width in pixel of the outline of this polygon"
  117. (make-instance 'canvas-polygon
  118. :canvas canvas
  119. :coords coords
  120. :fill-color fill-color
  121. :outline-color outline-color
  122. :outline-width outline-width))
  123. (define-constant +star-bbox-fix-scale+ 0.97 :test #'=)
  124. (defclass star (polygon)
  125. ()
  126. (:documentation "A star-shaped polygon"))
  127. (defun make-star (canvas ext-radius inner-radius-ratio inner-color outer-color corners
  128. &key
  129. (draw-left-half nil) (draw-right-half nil)
  130. (outline-width 1))
  131. "draw a star shaped polygon.
  132. canvas: the canvas where draw this star to
  133. ext-radius: the external radius of the circle that inscribe this star
  134. inner-radius-ratio: the ratio between concave and convex point length of this star
  135. inner-color: the color of this star
  136. outer-color: the color of the outline of this star
  137. corners: the number of spikes for this star
  138. draw-left-half draw left half side of the star only
  139. draw-right-half draw right half side of the star only
  140. outline-width: the width in pixel of the outline of this polygon.
  141. Return an instance of 'star'
  142. "
  143. (assert (> corners 0))
  144. (assert (not (and draw-left-half draw-right-half)))
  145. (flet ((make-points (start num)
  146. (let ((inc (->f (/ nodgui.constants:+2pi+ num)))
  147. (dir (vec2-normalize start)))
  148. (loop
  149. repeat num
  150. for angle from 0.0 downto -1000.0 by inc collect
  151. (let* ((rotated (vec2-rotate dir angle))
  152. (scaled (vec2* rotated (vec2-length start))))
  153. (vector (round (vec2-x scaled))
  154. (round (vec2-y scaled)))))))
  155. (slice (seq num)
  156. (subseq seq 0 num)))
  157. (let* ((corners-num (if (or draw-left-half
  158. draw-right-half)
  159. (ceiling (/ corners 2))
  160. corners))
  161. (starting-angle (if (oddp corners)
  162. (->f (- (/ nodgui.constants:+2pi+
  163. (* 2 corners))))
  164. (->f (- (/ nodgui.constants:+2pi+
  165. corners)))))
  166. (ext-start (vec2-rotate (vec2 0.0 (->f ext-radius))
  167. starting-angle))
  168. (ext-points (make-points ext-start corners))
  169. (inner-start (vec2-rotate (vec2* ext-start
  170. inner-radius-ratio)
  171. (->f (/ nodgui.constants:+2pi+
  172. (* 2 corners)))))
  173. (inner-points (make-points inner-start corners))
  174. (points (alexandria:flatten (mapcar #'list
  175. (slice inner-points corners-num )
  176. (slice ext-points corners-num)))))
  177. (when (and (or draw-left-half
  178. draw-right-half)
  179. (evenp corners))
  180. (push (alexandria:last-elt ext-points) points))
  181. (when draw-left-half
  182. (setf points
  183. (mapcar (lambda (a) (vec2 (- (vec2-x a)) (vec2-y a)))
  184. points)))
  185. (make-instance 'star
  186. :outline-width outline-width
  187. :canvas canvas
  188. :coordinates points
  189. :fill-color inner-color
  190. :outline-color outer-color))))
  191. (defclass two-color-star (shape)
  192. ((bbox-fix
  193. :initform +star-bbox-fix-scale+
  194. :initarg :bbox-fix
  195. :accessor bbox-fix)
  196. (left-side
  197. :initform nil
  198. :initarg :left-side
  199. :accessor left-side)
  200. (right-side
  201. :initform nil
  202. :initarg :right-side
  203. :accessor right-side))
  204. (:documentation "A star-shaped polygon with two differents colors
  205. for left and right side"))
  206. (defun make-two-color-star (canvas
  207. ext-radius
  208. inner-radius-ratio
  209. inner-color-left
  210. outer-color-left
  211. inner-color-right
  212. outer-color-right
  213. corners
  214. &key (outline-width 1))
  215. "draw a star shaped polygon.
  216. canvas: the canvas where draw this star to
  217. ext-radius: the external radius of the circle that inscribe this star
  218. inner-radius-ratio: the ratio between concave and convex point length of this star
  219. inner-color-left: the color of the left side of this star
  220. outer-color-left: the color of the outline of the left side of this star
  221. inner-color-right: the color of the right side of this star
  222. outer-color-right: the color of the outline of the right side of this star
  223. corners: the number of spikes for this star
  224. outline-width: the width in pixel of the outline of this polygon.
  225. return an istance of two-color-star.
  226. "
  227. (let ((star-left (make-star canvas ext-radius inner-radius-ratio
  228. inner-color-left outer-color-left
  229. corners
  230. :outline-width outline-width
  231. :draw-right-half nil
  232. :draw-left-half t))
  233. (star-right (make-star canvas ext-radius inner-radius-ratio
  234. inner-color-right outer-color-right
  235. corners
  236. :outline-width outline-width
  237. :draw-right-half t
  238. :draw-left-half nil)))
  239. (make-instance 'two-color-star
  240. :canvas canvas
  241. :left-side star-left
  242. :right-side star-right)))
  243. (defmethod shape-move ((object two-color-star) dx dy)
  244. (with-accessors ((left-side left-side)
  245. (right-side right-side)) object
  246. (shape-move left-side dx dy)
  247. (shape-move right-side dx dy)))
  248. (defmethod shape-move-to ((object two-color-star) x y)
  249. (with-accessors ((left-side left-side)
  250. (right-side right-side)) object
  251. (with-accessors ((left-side-handle handle)
  252. (canvas canvas)) left-side
  253. (let* ((*bbox-scale-fix* (bbox-fix object))
  254. (aabb (canvas-item-bbox canvas left-side-handle))
  255. (max-x (bbox-max-x aabb))
  256. (min-x (bbox-min-x aabb))
  257. (outline-width (floor (safe-parse-number (item-cget canvas
  258. left-side-handle
  259. :width)
  260. :fix-fn (lambda (e)
  261. (declare (ignore e))
  262. 0))))
  263. (w (- max-x min-x outline-width)))
  264. (shape-move-to left-side x y)
  265. (shape-move-to right-side (+ w x) y)))))
  266. (defmethod shape-delete ((object two-color-star))
  267. (with-accessors ((left-side left-side)
  268. (right-side right-side)) object
  269. (shape-delete left-side)
  270. (shape-delete right-side)))
  271. (defun two-color-star-handle (object accessor)
  272. (handle (funcall accessor object)))
  273. (defgeneric left-side-handle (object))
  274. (defgeneric right-side-handle (object))
  275. (defmethod left-side-handle ((object two-color-star))
  276. (two-color-star-handle object #'left-side))
  277. (defmethod right-side-handle ((object two-color-star))
  278. (two-color-star-handle object #'right-side))