001 (ns walkmap.polygon
002 "Essentially the specification for things we shall consider to be polygons."
003 (:require [clojure.string :as s]
004 [walkmap.edge :as e]
005 [walkmap.tag :as t]
006 [walkmap.utils :refer [kind-type]]
007 [walkmap.vertex :refer [vertex vertex?]]))
008
009 (defn polygon?
010 "True if `o` satisfies the conditions for a polygon. A polygon shall be a
011 map which has a value for the key `:vertices`, where that value is a sequence
012 of vertices."
013 [o]
014 (let
015 [v (:vertices o)]
016 (and
017 (coll? v)
018 (> (count v) 2)
019 (every? vertex? v)
020 (:walkmap.id/id o)
021 (or (nil? (:kind o)) (= (:kind o) :polygon)))))
022
023 (defn triangle?
024 "True if `o` satisfies the conditions for a triangle. A triangle shall be a
025 polygon with exactly three vertices."
026 [o]
027 (and
028 (coll? o)
029 (= (count (:vertices o)) 3)))
030
031 (defn polygon
032 "Return a polygon constructed from these `vertices`."
033 [vertices]
034 (when-not (every? vertex? vertices)
035 (throw (IllegalArgumentException.
036 (str
037 "Each item on vertices must be a vertex: "
038 (s/join " " (map kind-type (remove vertex? vertices)))))))
039 {:vertices vertices :walkmap.id/id (keyword (gensym "poly")) :kind :polygon})
040
041 (defn gradient
042 "Return a polygon like `triangle` but with a key `:gradient` whose value is a
043 unit vector representing the gradient across `triangle`."
044 [triangle]
045 (when-not (triangle? triangle)
046 (throw (IllegalArgumentException.
047 (s/join " " ["Must be a triangle:" (kind-type triangle)]))))
048 (let [order (sort #(max (:z %1) (:z %2)) (:vertices triangle))
049 highest (first order)
050 lowest (last order)]
051 (assoc triangle :gradient (e/unit-vector (e/edge lowest highest)))))
052
053 (defn triangle-centre
054 "Return a canonicalised `facet` (i.e. a triangular polygon) with an added
055 key `:centre` whose value represents the centre of this facet in 3
056 dimensions. This only works for triangles, so is here not in
057 `walkmap.polygon`. It is an error (although no exception is currently
058 thrown) if the object past is not a triangular polygon."
059 [facet]
060 (when-not (triangle? facet)
061 (throw (IllegalArgumentException.
062 (s/join " " ["Must be a triangle:" (kind-type facet)]))))
063 (let [vs (:vertices facet)
064 v1 (first vs)
065 opposite (e/edge (nth vs 1) (nth vs 2))
066 oc (e/centre opposite)]
067 (assoc
068 facet
069 :centre
070 (vertex
071 (+ (:x v1) (* (- (:x oc) (:x v1)) 2/3))
072 (+ (:y v1) (* (- (:y oc) (:y v1)) 2/3))
073 (+ (:z v1) (* (- (:z oc) (:z v1)) 2/3))))))
074
075 (defn centre
076 [poly]
077 (when-not (polygon? poly)
078 (throw (IllegalArgumentException.
079 (s/join " " ["Must be a polygon:" (kind-type poly)]))))
080 (case (count (:vertices poly))
081 3 (triangle-centre poly)
082 ;; else
083 (throw
084 (UnsupportedOperationException.
085 "The general case of centre for polygons is not yet implemented."))))
086
087