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-vertex 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 rectangle
060    "Return a rectangle, with edges aligned east-west and north-south, whose
061    south-west corner is the vertex `vsw` and whose north-east corner is the
062    vertex `vne`."
063    [vsw vne]
064    ;; we can actually create any rectangle in the xy plane based on two opposite
065    ;; corners, but the maths are a bit to advanced for me today. TODO: do it!
066    (let [vnw (vertex (:x (check-vertex vsw))
067                      (:y (check-vertex vne))
068                      (/ (reduce + (map #(or (:z %) 0) [vsw vne])) 2))
069          vse (vertex (:x vne)
070                      (:y vsw)
071                      (/ (reduce + (map #(or (:z %) 0) [vsw vne])) 2))]
072      (t/tag (polygon vsw vnw vne vse) :rectangle)))
073  
074  ;; (rectangle (vertex 1 2 3) (vertex 7 9 4))
075  
076  (defn gradient
077    "Return a polygon like `triangle` but with a key `:gradient` whose value is a
078    unit vector representing the gradient across `triangle`."
079    [triangle]
080    (let [order (sort #(max (:z %1) (:z %2))
081                      (:vertices (check-triangle triangle)))
082          highest (first order)
083          lowest (last order)]
084       (assoc triangle :gradient (e/unit-vector (e/edge lowest highest)))))
085  
086  (defn triangle-centre
087    "Return a canonicalised `facet` (i.e. a triangular polygon) with an added
088    key `:centre` whose value represents the centre of this facet in 3
089    dimensions. This only works for triangles, so is here not in
090    `walkmap.polygon`. It is an error (although no exception is currently
091    thrown) if the object past is not a triangular polygon."
092    [facet]
093    (let [vs (:vertices (check-triangle facet))
094          v1 (first vs)
095          opposite (e/edge (nth vs 1) (nth vs 2))
096          oc (e/centre opposite)]
097        (assoc
098        facet
099        :centre
100        (vertex
101          (+ (:x v1) (* (- (:x oc) (:x v1)) 2/3))
102          (+ (:y v1) (* (- (:y oc) (:y v1)) 2/3))
103          (+ (:z v1) (* (- (:z oc) (:z v1)) 2/3))))))
104  
105  (defn centre
106    [poly]
107    (case (count (:vertices (check-polygon poly)))
108      3 (triangle-centre poly)
109      ;; else
110      (throw
111        (UnsupportedOperationException.
112          "The general case of centre for polygons is not yet implemented."))))
113  
114