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