Added: scaling and storing STL; beginnings of search

This commit is contained in:
Simon Brooke 2020-05-31 10:33:56 +01:00
parent 87177051da
commit 819aa0fbc4
No known key found for this signature in database
GPG key ID: A7A4F18D1D4DF987
4 changed files with 119 additions and 41 deletions

View file

@ -2,8 +2,9 @@
"Essentially the specification for things we shall consider to be polygons."
(:require [clojure.string :as s]
[walkmap.edge :as e]
[walkmap.tag :as t]
[walkmap.utils :refer [kind-type]]
[walkmap.vertex :refer [vertex?]]))
[walkmap.vertex :refer [vertex vertex?]]))
(defn polygon?
"True if `o` satisfies the conditions for a polygon. A polygon shall be a
@ -20,22 +21,26 @@
(or (nil? (:kind o)) (= (:kind o) :polygon)))))
(defn triangle?
"True if `o` satisfies the conditions for a triangle. A triangle shall be a
polygon with exactly three vertices."
[o]
(and
(coll? o)
(= (count (:vertices o)) 3)))
(defn polygon
"Return a polygon constructed from these `vertices`."
[vertices]
(when-not (every? vertex? vertices)
(throw (IllegalArgumentException.
(str
"Each item on path must be a vertex: "
"Each item on vertices must be a vertex: "
(s/join " " (map kind-type (remove vertex? vertices)))))))
{:vertices vertices :walkmap.id/id (keyword (gensym "poly")) :kind :polygon})
(defn gradient
"Return a unit vector representing the gradient across `triangle`."
"Return a polygon like `triangle` but with a key `:gradient` whose value is a
unit vector representing the gradient across `triangle`."
[triangle]
(when-not (triangle? triangle)
(throw (IllegalArgumentException.
@ -43,5 +48,40 @@
(let [order (sort #(max (:z %1) (:z %2)) (:vertices triangle))
highest (first order)
lowest (last order)]
(e/unit-vector (e/edge lowest highest))))
(assoc triangle :gradient (e/unit-vector (e/edge lowest highest)))))
(defn triangle-centre
"Return a canonicalised `facet` (i.e. a triangular polygon) with an added
key `:centre` whose value represents the centre of this facet in 3
dimensions. This only works for triangles, so is here not in
`walkmap.polygon`. It is an error (although no exception is currently
thrown) if the object past is not a triangular polygon."
[facet]
(when-not (triangle? facet)
(throw (IllegalArgumentException.
(s/join " " ["Must be a triangle:" (kind-type facet)]))))
(let [vs (:vertices facet)
v1 (first vs)
opposite (e/edge (nth vs 1) (nth vs 2))
oc (e/centre opposite)]
(assoc
facet
:centre
(vertex
(+ (:x v1) (* (- (:x oc) (:x v1)) 2/3))
(+ (:y v1) (* (- (:y oc) (:y v1)) 2/3))
(+ (:z v1) (* (- (:z oc) (:z v1)) 2/3))))))
(defn centre
[poly]
(when-not (polygon? poly)
(throw (IllegalArgumentException.
(s/join " " ["Must be a polygon:" (kind-type poly)]))))
(case (count (:vertices poly))
3 (triangle-centre poly)
;; else
(throw
(UnsupportedOperationException.
"The general case of centre for polygons is not yet implemented."))))

View file

@ -4,9 +4,10 @@
[clojure.string :as s]
[me.raynes.fs :as fs]
[org.clojars.smee.binary.core :as b]
[taoensso.timbre :as l :refer [info error spy]]
[taoensso.timbre :as l]
[walkmap.edge :as e]
[walkmap.polygon :refer [polygon?]]
[walkmap.polygon :refer [centre gradient polygon?]]
[walkmap.superstructure :refer [store]]
[walkmap.tag :refer [tag]]
[walkmap.utils :as u]
[walkmap.vertex :as v])
@ -54,25 +55,6 @@
:count :uint-le
:facets (b/repeated facet)))
(defn centre
"Return a canonicalised `facet` (i.e. a triangular polygon) with an added
key `:centre` whose value represents the centre of this facet in 3
dimensions. This only works for triangles, so is here not in
`walkmap.polygon`. It is an error (although no exception is currently
thrown) if the object past is not a triangular polygon."
[facet]
(let [vs (:vertices facet)
v1 (first vs)
opposite (e/edge (nth vs 1) (nth vs 2))
oc (e/centre opposite)]
(assoc
facet
:centre
(v/vertex
(+ (:x v1) (* (- (:x oc) (:x v1)) 2/3))
(+ (:y v1) (* (- (:y oc) (:y v1)) 2/3))
(+ (:z v1) (* (- (:z oc) (:z v1)) 2/3))))))
(defn canonicalise
"Objects read in from STL won't have all the keys/values we need them to have.
`o` may be a map (representing a facet or a vertex), or a sequence of such maps;
@ -82,6 +64,8 @@
`map-kind` is not a keyword."
([o] (canonicalise o :height))
([o map-kind]
(canonicalise o map-kind (v/vertex 1 1 1)))
([o map-kind scale-vertex]
(when-not
(keyword? map-kind)
(throw (IllegalArgumentException.
@ -93,25 +77,42 @@
:kind :stl
:walkmap.id/id (or (:walkmap.id/id o) (keyword (gensym "stl")))
:facets (canonicalise (:facets o) map-kind))
;; if it has :vertices it's a polygon, but it doesn't yet conform to `polygon?`
(:vertices o) (centre
;; if it has :vertices it's a polygon, but it may not yet conform to
;; `polygon?`
(:vertices o) (gradient
(centre
(tag
(assoc o
:walkmap.id/id (or (:walkmap.id/id o) (keyword (gensym "poly")))
:walkmap.id/id (or
(:walkmap.id/id o)
(keyword (gensym "poly")))
:kind :polygon
:vertices (canonicalise (:vertices o) map-kind))
:facet map-kind))
;; if it has a value for :x it's a vertex, but it doesn't yet conform to `vertex?`
(:x o) (v/canonicalise o)
:facet map-kind)))
;; if it has a value for :x it's a vertex, but it may not yet conform
;; to `vertex?`; it should also be scaled using the scale-vertex, if any.
(:x o) (let [c (v/canonicalise o)]
(if scale-vertex
(v/vertex* c scale-vertex)
c))
;; shouldn't happen
:else o)))
(defn decode-binary-stl
"Parse a binary STL file from this `filename` and return an STL structure
representing its contents. `map-kind`, if passed, must be a keyword
indicating the value represented by the `z` axis (defaults to `:height`).
or sequence of keywords indicating the semantic value represented by the `z`
axis (defaults to `:height`).
If `superstructure` is supplied and is a map, the generated STL structure
will be stored in that superstructure, which will be returned.
If `scale-vertex` is supplied, it must be a three dimensional vertex (i.e.
the `:z` key must have a numeric value) representing the amount by which
each of the vertices read from the STL will be scaled.
It is an error, and an exception will be thrown, if `map-kind` is not a
keyword.
keyword or sequence of keywords.
**NOTE** that we've no way of verifying that the input file is binary STL
data, if it is not this will run but will return garbage."
@ -122,8 +123,16 @@
(keyword? map-kind)
(throw (IllegalArgumentException.
(u/truncate (str "Must be a keyword: " (or map-kind "nil")) 80))))
(let [in (io/input-stream filename)]
(canonicalise (b/decode binary-stl in) map-kind))))
(decode-binary-stl filename map-kind nil))
([filename mapkind superstucture]
(decode-binary-stl filename mapkind superstucture (v/vertex 1 1 1)))
([filename map-kind superstructure scale-vertex]
(let [in (io/input-stream filename)
stl (canonicalise (b/decode binary-stl in) map-kind scale-vertex)]
(if
(map? superstructure)
(store stl superstructure)
stl))))
(defn- vect->str [prefix v]
(str prefix " " (:x v) " " (:y v) " " (:z v) "\n"))

View file

@ -4,7 +4,6 @@
[taoensso.timbre :as l]
[walkmap.path :as p]
[walkmap.polygon :as q]
[walkmap.stl :as s]
[walkmap.utils :as u]
[walkmap.vertex :as v]))
@ -166,3 +165,19 @@
(u/deep-merge s (in-store-find-objects o) (index-vertices s o))
(:walkmap.id/id o)
(in-store-replace-with-keys o))))
(defn search-vertices
"Search superstructure `s` for vertices within the box defined by vertices
`minv` and `maxv`. Every coordinate in `minv` must have a lower value than
the equivalent coordinate in `maxv`. If `d2?` is supplied and not false,
search only in the x,y projection."
([s minv maxv]
(search-vertices s minv maxv false))
([s minv maxv d2?]
(let [minv' (if d2? (assoc minv :z Double/NEGATIVE_INFINITY) minv)
maxv' (if d2? (assoc maxv :z Double/POSITIVE_INFINITY) maxv)]
(filter
#(v/within-box? % minv maxv)
(filter #(= (:kind %) :vertex) (vals s))))))

View file

@ -7,7 +7,7 @@
[clojure.string :as s]
[taoensso.timbre :as l]
[walkmap.geometry :refer [=ish]]
[walkmap.utils :refer [truncate]]))
[walkmap.utils :refer [kind-type truncate]]))
(defn vertex-key
"Making sure we get the same key everytime we key a vertex with the same
@ -133,3 +133,17 @@
(throw
(IllegalArgumentException.
(truncate (str "Not a vertex: " (or o "nil")) 80)))))))
(defn within-box?
"True if `target` is within the box defined by `minv` and `maxv`. All
arguments must be vertices; additionally, both `minv` and `maxv` must
have `:z` coordinates."
[target minv maxv]
(when-not (and (vertex? target) (vertex? minv) (vertex? maxv))
(throw (IllegalArgumentException.
(s/join " " ["Arguments to `within-box?` must be vertices:"
(map kind-type [target minv maxv])]))))
(every?
(map
#(< (% minv) (or (% target) 0) (% maxv))
[:x :y :z])))