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)))