nvg.lisp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. (cl:defpackage :ocm.nvg
  2. (:use :cl :cffi-c-ref :alexandria)
  3. (:export run))
  4. (cl:in-package :ocm.nvg)
  5. (defun aget (alist key)
  6. (cdr (assoc key alist :test #'equal)))
  7. (defun agets (alist &rest keys)
  8. (reduce #'aget keys :initial-value alist))
  9. (defun agetter (&rest keys)
  10. (lambda (alist)
  11. (apply 'agets alist keys)))
  12. (defun tags (obj)
  13. (let ((tags (ocm.osm::obj-tags obj)))
  14. (when (functionp tags)
  15. (setf tags (funcall tags)
  16. (ocm.osm::obj-tags obj) tags))
  17. tags))
  18. (defun merge-tags (obj parent)
  19. (append obj parent))
  20. (defvar *context* nil "NanoVG Context")
  21. (defvar *window-width* 640.0)
  22. (defvar *window-height* 480.0)
  23. (defvar *frame-width* 640.0)
  24. (defvar *frame-height* 480.0)
  25. (defvar *pixel-ratio* 1.0)
  26. (defvar *scale* 3f0)
  27. (defvar *pos-x* 0f0)
  28. (defvar *pos-y* 0f0)
  29. (defvar *objects* nil "List of OSM referenced objects to render")
  30. (defun fix-multipoly (rel)
  31. (labels ((id (obj) (ocm.osm::obj-id obj))
  32. (try-attach (way ways)
  33. (loop
  34. with way-nodes = (ocm.osm::way-refs way)
  35. with way-first = (car way-nodes)
  36. with way-last = (car (last way-nodes))
  37. for prev in ways
  38. for prev-nodes = (ocm.osm::way-refs prev)
  39. for prev-first = (car prev-nodes)
  40. for prev-last = (car (last prev-nodes))
  41. when (and way-first way-last prev-first prev-last)
  42. do (when-let (nodes (cond
  43. ((= (id way-first) (id prev-last))
  44. (append prev-nodes (cdr way-nodes)))
  45. ((= (id way-last) (id prev-first))
  46. (append way-nodes (cdr prev-nodes)))
  47. ((= (id way-first) (id prev-first))
  48. (append (reverse way-nodes) (cdr prev-nodes)))
  49. ((= (id way-last) (id prev-last))
  50. (append prev-nodes (cdr (reverse way-nodes))))
  51. ))
  52. (setf (ocm.osm::way-refs prev) nodes)
  53. (return prev)))))
  54. (loop with inner-ways and outer-ways
  55. for mem in (ocm.osm::rel-memids rel)
  56. for mem-role in (ocm.osm::rel-roles rel)
  57. for mem-type in (ocm.osm::rel-types rel)
  58. for parts = (if (equal "inner" mem-role) inner-ways outer-ways)
  59. when (ocm.osm::way-p mem)
  60. unless (try-attach mem parts)
  61. do (let ((new-way (ocm.osm::make-way :id (id mem)
  62. :refs (copy-list (ocm.osm::way-refs mem)))))
  63. (if (equal "inner" mem-role) (push new-way inner-ways) (push new-way outer-ways)))
  64. finally (return (ocm.osm::make-rel
  65. :id (id rel) :tags (ocm.osm::obj-tags rel)
  66. :memids (append outer-ways inner-ways)
  67. :roles (append (loop for w in outer-ways collect "outer")
  68. (loop for w in inner-ways collect "inner"))
  69. :types (append (loop for w in outer-ways collect :way)
  70. (loop for w in inner-ways collect :way)))))))
  71. (defun osm-fix (obj)
  72. (cond
  73. ((multipoly-p obj) (fix-multipoly obj))
  74. (:otherwise obj)))
  75. (defun load-test-objects ()
  76. (setf *objects* nil)
  77. (ocm.osm::with-related (obj "spb.osm.pbf" '(
  78. (nil "natural" "water")
  79. (nil "natural" "wood")
  80. (nil "natural" "scrub")
  81. (nil "highway" "primary")
  82. (nil "highway" "secondary")))
  83. (push (osm-fix obj) *objects*)))
  84. (defparameter +granularity+ (expt 10 9))
  85. ;; Web mercator
  86. (defun node-x (node)
  87. (float
  88. (* (expt 2 *zoom*)
  89. (/ (+ (/ (ocm.osm::node-lon node) +granularity+) 180) 360))))
  90. (defun node-y (node)
  91. (let* ((lat (/ (ocm.osm::node-lat node) +granularity+))
  92. (lat-rad (/ (* lat pi) 180)))
  93. (float
  94. (* (expt 2 (1- *zoom*))
  95. (- 1 (/ (log (+ (tan lat-rad) (/ 1 (cos lat-rad))))
  96. pi))))))
  97. (defun offset-x (node)
  98. (coerce (- (node-x node) (node-x *corner*)) 'single-float))
  99. (defun offset-y (node)
  100. (coerce (- (node-y node) (node-y *corner*)) 'single-float))
  101. (defun render-path (nodes)
  102. (%nvg:begin-path *context*)
  103. (let ((start (car nodes)))
  104. (%nvg:move-to *context* (offset-x start) (offset-y start))
  105. (loop for node in (cdr nodes)
  106. do (%nvg:line-to *context* (offset-x node) (offset-y node)))))
  107. (defparameter +highway-color+ '(150 100 0 255))
  108. (defparameter +highway-primary-color+ '(150 30 0 255))
  109. (defparameter +strokes+
  110. `(
  111. (((:way "lanes" "5")) :color ,+highway-primary-color+ :width 0.005)
  112. (((:way "lanes" "3")) :color ,+highway-primary-color+ :width 0.004)
  113. (((:way "highway" "primary")) :color ,+highway-primary-color+ :width 0.003)
  114. (((:way "highway")) :color ,+highway-color+ :width 0.001)))
  115. (defun apply-stroke (tags)
  116. (when-let (options (find-match +strokes+ tags :way))
  117. (destructuring-bind (&key color width) options
  118. (when color
  119. (c-with ((n-color %nvg:color))
  120. (%nvg:stroke-color *context* (apply '%nvg:rgba (n-color &) color))))
  121. (when width (%nvg:stroke-width *context* width)))))
  122. (defun render-line (nodes tags)
  123. (render-path nodes)
  124. (apply-stroke tags)
  125. (%nvg:stroke *context*))
  126. (defparameter +water-color+ '(0 0 250 255))
  127. (defparameter +wood-color+ '(0 250 0 255))
  128. (defparameter +fills+
  129. `(
  130. (((nil "natural" "water")) :color ,+water-color+)
  131. (((nil "natural" "scrub")
  132. (nil "natural" "wood")) :color ,+wood-color+)
  133. ))
  134. (defun find-match (query-value tags type)
  135. (loop for (query . value) in query-value
  136. when (ocm.osm::query-match2 query type tags)
  137. do (return value)))
  138. (defun apply-fill (tags)
  139. (when-let (fill (find-match +fills+ tags :way))
  140. (destructuring-bind (&key color paint) fill
  141. (when color
  142. (c-with ((n-color %nvg:color))
  143. (%nvg:fill-color *context* (apply '%nvg:rgba (n-color &) color))))
  144. (when paint))))
  145. (defun render-poly (nodes tags &optional hole)
  146. (render-path nodes)
  147. (%nvg:close-path *context*)
  148. (if hole
  149. (%nvg:path-winding *context* (cffi:foreign-bitfield-value '%nvg:solidity :hole))
  150. (apply-fill tags))
  151. (%nvg:fill *context*))
  152. (defparameter +closed-lines+
  153. '((:way "highway")))
  154. (defun closed-p (nodes)
  155. (let ((first (first nodes))
  156. (last (car (last nodes))))
  157. (and first last
  158. (= (if (ocm.osm::obj-p first) (ocm.osm::obj-id first) first)
  159. (if (ocm.osm::obj-p last) (ocm.osm::obj-id last) last)))))
  160. (defun area-p (nodes tags)
  161. (and (closed-p nodes)
  162. (not (ocm.osm::query-match2 +closed-lines+ :way tags))))
  163. (defun multipoly-p (rel)
  164. (and (ocm.osm::rel-p rel)
  165. (equal (aget (tags rel) "type")
  166. "multipolygon")))
  167. (defun render-way (nodes tags)
  168. (if (area-p nodes tags)
  169. (render-poly nodes tags)
  170. (render-line nodes tags)))
  171. (defun render-multipoly (rel)
  172. (loop for mem in (ocm.osm::rel-memids rel)
  173. for mem-role in (ocm.osm::rel-roles rel)
  174. for mem-type in (ocm.osm::rel-types rel)
  175. when (ocm.osm::way-p mem) ;; skip non-ways
  176. do (render-poly (ocm.osm::way-refs mem)
  177. (tags rel)
  178. (equal "inner" mem-role))))
  179. (defun render-objects (objects)
  180. (loop for obj in objects
  181. do (typecase obj
  182. (ocm.osm::way
  183. (render-way (ocm.osm::way-refs obj) (tags obj)))
  184. (ocm.osm::rel
  185. (when (multipoly-p obj)
  186. (render-multipoly obj))))))
  187. (defun render ()
  188. (%nvg:begin-frame *context* *window-width* *window-height* *pixel-ratio*)
  189. (%nvg:transform *context* *scale* 0f0 0f0 *scale* *pos-x* *pos-y*)
  190. (render-objects *objects*)
  191. (%nvg:end-frame *context*))
  192. (defun render-test ()
  193. (%nvg:begin-path *context*)
  194. (%nvg:rect *context* 100f0 100f0 120f0 30f0)
  195. (%nvg:circle *context* 120f0 120f0 5f0)
  196. (%nvg:path-winding *context* (cffi:foreign-bitfield-value '%nvg:solidity :hole))
  197. (c-with ((color %nvg:color))
  198. (%nvg:fill-color *context* (%nvg:rgba (color &)
  199. (round (* 127.5 (+ 1 (sin (%glfw:get-timer-value)))))
  200. 192 192 255)))
  201. (%nvg:fill *context*)
  202. )
  203. (defparameter +translate-step+ 1)
  204. (defparameter +scale-step+ 0.3)
  205. (defvar *pressed-keys* nil)
  206. (defun process-keys ()
  207. (let ((step (* +translate-step+ (expt 2 *zoom*))))
  208. (when (member %glfw:+key-left+ *pressed-keys*) (decf (ocm.osm::node-lon *corner*) step))
  209. (when (member %glfw:+key-right+ *pressed-keys*) (incf (ocm.osm::node-lon *corner*) step))
  210. (when (member %glfw:+key-up+ *pressed-keys*) (decf (ocm.osm::node-lat *corner*) step))
  211. (when (member %glfw:+key-down+ *pressed-keys*) (incf (ocm.osm::node-lat *corner*) step))
  212. (when (member %glfw:+key-minus+ *pressed-keys*) (decf *zoom* +scale-step+))
  213. (when (member %glfw:+key-equal+ *pressed-keys*) (incf *zoom* +scale-step+))))
  214. (glfw:define-key-callback on-keys (window key scancode action mod-keys)
  215. (declare (ignorable window key scancode action mod-keys))
  216. (format t "KEY: ~{~A ~}~%" (list key scancode action mod-keys))
  217. (when (eql action %glfw:+press+)
  218. (pushnew key *pressed-keys*)
  219. (cond
  220. ((or
  221. (eql key %glfw:+key-escape+)
  222. (eql key %glfw:+key-q+))
  223. (%glfw:set-window-should-close window %glfw:+true+))))
  224. (when (eql action %glfw:+release+)
  225. (setf *pressed-keys* (delete key *pressed-keys*)))
  226. (print (list *scale* *pos-x* *pos-y*)))
  227. (glfw:define-framebuffer-size-callback on-fb-size (window width height)
  228. (declare (ignorable window))
  229. (setf *frame-width* (float width)
  230. *frame-height* (float height)
  231. *pixel-ratio* (/ *frame-width* *window-width*))
  232. (gl:viewport 0 0 *frame-width* *frame-height*))
  233. (glfw:define-window-size-callback on-win-size (window width height)
  234. (declare (ignorable window))
  235. (setf *window-width* (float width)
  236. *window-height* (float height)
  237. *pixel-ratio* (/ *frame-width* *window-width*)))
  238. (glfw::define-error-callback on-error (err desc)
  239. (format *error-output* "GLFW error ~A: ~A~%" err desc))
  240. (defun create-window ()
  241. (if (uiop:featurep :bodge-gl2)
  242. (glfw:with-window-hints ((%glfw:+context-version-major+ 2)
  243. (%glfw:+context-version-minor+ 1)
  244. (%glfw:+depth-bits+ 24)
  245. (%glfw:+stencil-bits+ 8))
  246. (%glfw:create-window 640 480 "Hello NanoVG 2" nil nil))
  247. (glfw:with-window-hints ((%glfw:+context-version-major+ 3)
  248. (%glfw:+context-version-minor+ 3)
  249. (%glfw:+opengl-profile+ %glfw:+opengl-core-profile+)
  250. (%glfw:+opengl-forward-compat+ %glfw:+true+)
  251. (%glfw:+depth-bits+ 24)
  252. (%glfw:+stencil-bits+ 8))
  253. (%glfw:create-window 640 480 "Hello NanoVG 3" nil nil))))
  254. (defun main ()
  255. ;; Initializing window and OpenGL context
  256. (glfw:with-init ()
  257. (%glfw:set-error-callback (cffi:callback on-error))
  258. (c-let ((window %glfw:window :from (create-window))
  259. (width :int :alloc t)
  260. (height :int :alloc t))
  261. (when (cffi:null-pointer-p (window &))
  262. (%glfw:terminate)
  263. (error "Failed to create GLFW window"))
  264. (%glfw:make-context-current (window &))
  265. ;; Mangling GL function pointers
  266. (glad:init)
  267. ;; Set event callbacks
  268. (%glfw:set-key-callback (window &) (cffi:callback on-keys))
  269. (%glfw:set-framebuffer-size-callback (window &) (cffi:callback on-fb-size))
  270. (%glfw:set-window-size-callback (window &) (cffi:callback on-win-size))
  271. ;; Get initial values
  272. (%glfw:get-framebuffer-size (window &) (width &) (height &))
  273. (setf *frame-width* (float width)
  274. *frame-height* (float height))
  275. (%glfw:get-window-size (window &) (width &) (height &))
  276. (setf *window-width* (float width)
  277. *window-height* (float height)
  278. *pixel-ratio* (/ *frame-width* *window-width*))
  279. (gl:viewport 0 0 *frame-width* *frame-height*)
  280. ;; reset
  281. ;; (setf *scale* 540f0
  282. ;; *pos-x* -162910.0
  283. ;; *pos-y* 324540.0)
  284. (setf *zoom* 20
  285. *corner* (ocm.osm::make-node :LAT 59931756500 :LON 30293207600))
  286. (setf *scale* 1f0
  287. *pos-x* 0.0
  288. *pos-y* 0.0)
  289. ;; Creating NanoVG context
  290. (let ((*context* (nvg:make-context)))
  291. (unwind-protect
  292. (loop while (= (%glfw:window-should-close (window &)) 0) do
  293. (gl:clear-color 1f0 1f0 1f0 1f0)
  294. (gl:clear :color-buffer-bit :depth-buffer-bit :stencil-buffer-bit)
  295. ;; Step
  296. (process-keys)
  297. ;; Actually drawing into nanovg context and onto display
  298. (render)
  299. (%glfw:swap-buffers (window &))
  300. (%glfw:poll-events))
  301. ;; Cleaning up NanoVG context
  302. (nvg:destroy-context *context*))))))
  303. (defun run ()
  304. (main)
  305. ;; (trivial-main-thread:with-body-in-main-thread (:blocking t)
  306. ;; (float-features:with-float-traps-masked t
  307. ;; (main)))
  308. )