001 (ns walkmap.edge
002 "Essentially the specification for things we shall consider to be an edge.
003 An edge is a line segment having just a start and an end, with no intervening
004 nodes."
005 (:require [clojure.math.numeric-tower :as m]
006 [walkmap.path :refer [path? polygon->path]]
007 [walkmap.polygon :refer [polygon?]]
008 [walkmap.vertex :refer [ensure3d vertex?]]))
009
010 (defn edge?
011 "True if `o` satisfies the conditions for a path. A path shall be a map
012 having the keys `:start` and `:end`, such that the values of each of those
013 keys shall be a vertex."
014 [o]
015 (and
016 (map? o)
017 (vertex? (:start o))
018 (vertex? (:end o))))
019
020 (defn path->edges
021 "if `o` is a path, a polygon, or a sequence of vertices, return a sequence of
022 edges representing that path, polygon or sequence."
023 [o]
024 (cond
025 (seq? o)
026 (when
027 (and
028 (vertex? (first o))
029 (vertex? (first (rest o))))
030 (cons
031 {:start (first o)
032 :end (first (rest o))}
033 (path->edges (rest o))))
034 (path? o)
035 (path->edges (:nodes o))
036 (polygon? o)
037 (path->edges (polygon->path o))))
038
039 (defn length
040 "Return the length of the edge `e`."
041 [e]
042 (let [start (ensure3d (:start e))
043 end (ensure3d (:end e))]
044 (m/sqrt
045 (reduce
046 +
047 (map
048 #(m/expt (- (% end) (% start)) 2)
049 [:x :y :z])))))
050
051 (defn unit-vector
052 "Return an vertex parallel to `e` starting from the coordinate origin. Two
053 edges which are parallel will have the same unit vector."
054 [e]
055 (let [e' {:start (ensure3d (:start e)) :end (ensure3d (:end e))}
056 l (length e')]
057 (reduce
058 merge
059 {}
060 (map
061 (fn [k]
062 {k (/ (- (k (:end e')) (k (:start e'))) l)})
063 [:x :y :z]))))
064
065 (defn parallel?
066 "True if all `edges` passed are parallel with one another."
067 ;; TODO: this bears being wary about, dealing with floating point arithmetic.
068 ;; Keep an eye out for spurious errors.
069 [& edges]
070 (let [uvs (map unit-vector edges)]
071 (every?
072 #(= % (first uvs))
073 (rest uvs))))
074
075 (defn collinear?
076 "True if edges `e1` and `e2` are collinear with one another."
077 [e1 e2]
078 (parallel?
079 e1
080 e2
081 {:start (:start e1) :end (:start e2)}))
082