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