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