# walkmap/svg.clj

001 (ns walkmap.svg
002 "Utility functions for writing stereolithography (STL) files (and possibly,
003 later, other geometry files of interest to us) as scalable vector graphics
004 (SVG)."
005 (:require [clojure.string :as s]
006 [dali.io :as neatly-folded-clock]
007 [hiccup.core :refer [html]]
008 [taoensso.timbre :as l :refer [info error spy]]
009 [walkmap.ocean :refer [cull-ocean-facets]]
010 [walkmap.polygon :refer [polygon?]]
011 [walkmap.stl :refer [decode-binary-stl]]
012 [walkmap.vertex :refer [vertex?]]))
013
014 (def ^:dynamic *preferred-svg-render*
015 "Mainly for debugging dali; switch SVG renderer to use. Expected values:
016 `:dali`, `:hiccup`."
017 :dali)
018
019 (defn- facet->svg-poly
020 [facet]
021 [:polygon
022 {:points (s/join " " (map #(str (:x %) "," (:y %)) (:vertices facet)))}])
023
024 (defn- dali-facet->svg-poly
025 [facet]
026 (vec
027 (cons
028 :polygon
029 (map #(vec (list (:x %) (:y %))) (:vertices facet)))))
030
031 (defn dali-stl->svg
032 "Format this `stl` as SVG for the `dali` renderer on a page with these
033 bounds."
034 [stl minx maxx miny maxy]
035 [:dali/page
036 {:xmlns "http://www.w3.org/2000/svg"
037 :version "1.2"
038 :width (- maxx minx)
039 :height (- maxy miny)
040 :viewBox (s/join " " (map str [minx miny maxx maxy]))}
041 (vec
042 (cons
043 :g
044 (map
045 dali-facet->svg-poly
046 (:facets stl))))])
047
048 (defn hiccup-stl->svg
049 "Format this `stl` as SVG for the `hiccup` renderer on a page with these
050 bounds."
051 [stl minx maxx miny maxy]
052 [:svg
053 {:xmlns "http://www.w3.org/2000/svg"
054 :version "1.2"
055 :width (- maxx minx)
056 :height (- maxy miny)
057 :viewBox (s/join " " (map str [minx miny maxx maxy]))}
058 (vec
059 (cons
060 :g
061 (map
062 facet->svg-poly
063 (:facets stl))))])
064
065 (defn stl->svg
066 "Convert this in-memory `stl` structure, as read by `decode-binary-stl`, into
067 an in-memory hiccup representation of SVG structure, and return it."
068 [stl]
069 (let [minx (reduce
070 min
071 (map
072 #(reduce min (map :x (:vertices %)))
073 (:facets stl)))
074 maxx (reduce
075 max
076 (map
077 #(reduce max (map :x (:vertices %)))
078 (:facets stl)))
079 miny (reduce
080 min
081 (map
082 #(reduce min (map :y (:vertices %)))
083 (:facets stl)))
084 maxy (reduce
085 max
086 (map
087 #(reduce max (map :y (:vertices %)))
088 (:facets stl)))]
089 (l/info "Generating SVG for " *preferred-svg-render* " renderer")
090 (case *preferred-svg-render*
091 :hiccup (hiccup-stl->svg stl minx maxx miny maxy)
092 :dali (dali-stl->svg stl minx maxx miny maxy)
093 (throw (Exception. "Unexpected renderer value: " *preferred-svg-render*)))))
094
095 (defn binary-stl-file->svg
096 "Given only an `in-filename`, parse the indicated file, expected to be
097 binary STL, and return an equivalent SVG structure. Given both `in-filename`
098 and `out-filename`, as side-effect write the SVG to the indicated output file."
099 ([in-filename]
100 (stl->svg (cull-ocean-facets (decode-binary-stl in-filename))))
101 ([in-filename out-filename]
102 (let [s (binary-stl-file->svg in-filename)]
103 (l/info "Emitting SVG with " *preferred-svg-render* " renderer")
104 (case *preferred-svg-render*
105 :dali (neatly-folded-clock/render-svg s out-filename)
106 :hiccup (spit out-filename (html s))
107 (throw (Exception. "Unexpected renderer value: " *preferred-svg-render*)))
108 s)))