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