001 (ns walkmap.superstructure
002 "single indexing structure for walkmap objects"
003 (:require [walkmap.path :as p]
004 [walkmap.polygon :as q]
005 [walkmap.stl :as s]
006 [walkmap.utils :as u]
007 [walkmap.vertex :as v]))
008
009 (defn index-vertex
010 "Return a superstructure like `s` in which object `o` is indexed by vertex
011 `v`. It is an error (and an exception may be thrown) if
012
013 1. `s` is not a map;
014 2. `o` is not a map;
015 3. `o` does not have a value for the key `:id`;
016 4. `v` is not a vertex."
017 ;; two copies of the same vertex are not identical enough to one another
018 ;; to be used as keys in a map. So our vertices need to have ids, and we need
019 ;; to key the vertex-index by vertex ids.
020 ;; TODO: BUT WE CANNOT USE GENSYMED ids, because two vertices with the same
021 ;; vertices must have the same id!
022 [s o v]
023 (if-not (v/vertex? o)
024 (if (:id o)
025 (if (v/vertex? v)
026 (let [vi (or (:vertex-index s) {})
027 current (or (vi (:id v)) {})]
028 ;; deep-merge doesn't merge sets, only maps; so at this
029 ;; stage we need to build a map.
030 (assoc vi (:id v) (assoc current (:id o) (:id v))))
031 (throw (IllegalArgumentException. "Not a vertex: " v)))
032 (throw (IllegalArgumentException. (subs (str "No `:id` value: " o) 0 80))))
033 ;; it shouldn't actually be an error to try to index a vertex, but it
034 ;; also isn't useful to do so, so I'd be inclined to ignore it.
035 (:vertex-index s)))
036
037 (defn index-vertices
038 "Return a superstructure like `s` in which object `o` is indexed by its
039 vertices. It is an error (and an exception may be thrown) if
040
041 1. `s` is not a map;
042 2. `o` is not a map;
043 3. `o` does not have a value for the key `:id`."
044 [s o]
045 (assoc
046 s
047 :vertex-index
048 (reduce
049 u/deep-merge
050 (map
051 #(index-vertex s o %)
052 (u/vertices o)))))
053
054 (defn add-to-superstructure
055 "Return a superstructure like `s` with object `o` added. If `o` is a collection,
056 return a superstructure like `s` with each element of `o` added. If only one
057 argument is supplied it will be assumed to represent `o` and a new
058 superstructure will be returned.
059
060 It is an error (and an exception may be thrown) if
061
062 1. `s` is not a map;
063 2. `o` is not a map, or a sequence of maps."
064 ([o]
065 (add-to-superstructure {} o))
066 ([s o]
067 (cond
068 (map? o) (let [o' (if (:id o) o (assoc o :id (keyword (gensym "obj"))))]
069 (index-vertices (assoc s (:id o') o') o'))
070 (coll? o) (reduce u/deep-merge (map #(add-to-superstructure s %) o))
071 (nil? o) o
072 :else
073 (throw (IllegalArgumentException. (str "Don't know how to index " (or (type o) "nil")))))))
074