001 (ns walkmap.vertex
002 "Essentially the specification for things we shall consider to be vertices.
003
004 Note that there's no `distance` function here; to find the distance between
005 two vertices, create an edge from them and use `walkmap.edge/length`.")
006
007 (defn vertex-key
008 "Making sure we get the same key everytime we key a vertex with the same
009 coordinates. `o` must have numeric values for `:x`, `:y`, and optionally
010 `:z`."
011 [o]
012 (cond
013 (and (:x o) (:y o) (:z o)) (keyword (str "vert{" (:x o) "|" (:y o) "|" (:z o) "}"))
014 (and (:x o) (:y o)) (keyword (str "vert{" (:x o) "|" (:y o) "}"))
015 :else (throw (IllegalArgumentException. "Not a vertex."))))
016
017 (defn vertex?
018 "True if `o` satisfies the conditions for a vertex. That is, essentially,
019 that it must rerpresent a two- or three- dimensional vector. A vertex is
020 shall be a map having at least the keys `:x` and `:y`, where the value of
021 those keys is a number. If the key `:z` is also present, its value must also
022 be a number.
023
024 The name `vector?` was not used as that would clash with a function of that
025 name in `clojure.core` whose semantics are entirely different."
026 [o]
027 (and
028 (map? o)
029 (:id o)
030 (number? (:x o))
031 (number? (:y o))
032 (or (nil? (:z o)) (number? (:z o)))
033 (or (nil? (:kind o)) (= (:kind o) :vertex))))
034
035 (defn vertex
036 "Make a vertex with this `x`, `y` and (if provided) `z` values. Returns a map
037 with those values, plus a unique `:id` value, and `:kind` set to `:vertex`.
038 It's not necessary to use this function to create a vertex, but the `:id`
039 must be present and must be unique."
040 ([x y]
041 (let [v {:x x :y y :kind :vertex}]
042 (assoc v :id (vertex-key v))))
043 ([x y z]
044 (assoc (vertex x y) :z z)))
045
046 (defn canonicalise
047 "If `o` is a map with numeric values for `:x`, `:y` and optionally `:z`,
048 upgrade it to something we will recognise as a vertex."
049 [o]
050 (if
051 (and
052 (map? o)
053 (number? (:x o))
054 (number? (:y o))
055 (or (nil? (:z o)) (number? (:z o))))
056 (assoc o :kind :vertex :id (vertex-key o))
057 (throw (IllegalArgumentException. "Not a proto-vertex: must have numeric `:x` and `:y`."))))
058
059 (def ensure3d
060 "Given a vertex `o`, if `o` has a `:z` value, just return `o`; otherwise
061 return a vertex like `o` but having thie `dflt` value as the value of its
062 `:z` key, or zero as the value of its `:z` key if `dflt` is not specified.
063
064 If `o` is not a vertex, throws an exception."
065 (memoize
066 (fn
067 ([o]
068 (ensure3d o 0.0))
069 ([o dflt]
070 (cond
071 (not (vertex? o)) (throw (IllegalArgumentException. "Not a vertex!"))
072 (:z o) o
073 :else (assoc o :z dflt))))))
074
075 (def ensure2d
076 "If `o` is a vertex, set its `:z` value to zero; else throw an exception."
077 (memoize
078 (fn [o]
079 (if
080 (vertex? o)
081 (assoc o :z 0.0)
082 (throw (IllegalArgumentException. "Not a vertex!"))))))