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!"))))))