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 [check-kind-type check-kind-type-seq kind-type]]
007              [walkmap.vertex :refer [check-vertices 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  (defmacro check-polygon
024    "If `o` is not a polygon, throw an `IllegalArgumentException` with an
025    appropriate message; otherwise, returns `o`. Macro, so exception is thrown
026    from the calling function."
027    [o]
028    `(check-kind-type ~o polygon? :polygon))
029  
030  (defmacro check-polygons
031    "If `o` is not a sequence of polygons, throw an `IllegalArgumentException` with an
032    appropriate message; otherwise, returns `o`. Macro, so exception is thrown
033    from the calling function."
034    [o]
035    `(check-kind-type-seq ~o polygon? :polygon))
036  
037  (defn triangle?
038    "True if `o` satisfies the conditions for a triangle. A triangle shall be a
039    polygon with exactly three vertices."
040    [o]
041    (and
042      (coll? o)
043      (= (count (:vertices o)) 3)))
044  
045  (defmacro check-triangle
046    "If `o` is not a triangle, throw an `IllegalArgumentException` with an
047    appropriate message; otherwise, returns `o`. Macro, so exception is thrown
048    from the calling function."
049    [o]
050    `(check-kind-type ~o triangle? :triangle))
051  
052  (defn polygon
053    "Return a polygon constructed from these `vertices`."
054    [& vertices]
055    {:vertices (check-vertices vertices)
056     :walkmap.id/id (keyword (gensym "poly"))
057     :kind :polygon})
058  
059  (defn gradient
060    "Return a polygon like `triangle` but with a key `:gradient` whose value is a
061    unit vector representing the gradient across `triangle`."
062    [triangle]
063    (let [order (sort #(max (:z %1) (:z %2))
064                      (:vertices (check-triangle triangle)))
065          highest (first order)
066          lowest (last order)]
067       (assoc triangle :gradient (e/unit-vector (e/edge lowest highest)))))
068  
069  (defn triangle-centre
070    "Return a canonicalised `facet` (i.e. a triangular polygon) with an added
071    key `:centre` whose value represents the centre of this facet in 3
072    dimensions. This only works for triangles, so is here not in
073    `walkmap.polygon`. It is an error (although no exception is currently
074    thrown) if the object past is not a triangular polygon."
075    [facet]
076    (let [vs (:vertices (check-triangle facet))
077          v1 (first vs)
078          opposite (e/edge (nth vs 1) (nth vs 2))
079          oc (e/centre opposite)]
080        (assoc
081        facet
082        :centre
083        (vertex
084          (+ (:x v1) (* (- (:x oc) (:x v1)) 2/3))
085          (+ (:y v1) (* (- (:y oc) (:y v1)) 2/3))
086          (+ (:z v1) (* (- (:z oc) (:z v1)) 2/3))))))
087  
088  (defn centre
089    [poly]
090    (case (count (:vertices (check-polygon poly)))
091      3 (triangle-centre poly)
092      ;; else
093      (throw
094        (UnsupportedOperationException.
095          "The general case of centre for polygons is not yet implemented."))))
096  
097