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  ;; TODO: Think about reification/dereification. How can we cull a polygon, if
010  ;; some vertices still index it? I *think* that what's needed is that when
011  ;; we store something in the superstructure, we replace all its vertices (and
012  ;; other dependent structures, if any with their ids - as well as, obviously,
013  ;; adding/merging those vertices/dependent structures into the superstructure
014  ;; as first class objects in themselves. That means, for each identified thing,
015  ;; the superstructure only contains one copy of it.
016  ;;
017  ;; The question then is, when we want to do things with those objects, do we
018  ;; exteract a copy with its dependent structures fixed back up (reification),
019  ;; or do we indirect through the superstructure every time we want to access
020  ;; them? In a sense, the copy in the superstructure is the 'one true copy',
021  ;; but it may become very difficult then to have one true copy of the
022  ;; superstructure - unless we replace the superstructure altogether with a
023  ;; database, which may be the Right Thing To Do.
024  
025  (defn index-vertex
026    "Return a superstructure like `s` in which object `o` is indexed by vertex
027    `v`. It is an error (and an exception may be thrown) if
028  
029    1. `s` is not a map;
030    2. `o` is not a map;
031    3. `o` does not have a value for the key `:id`;
032    4. `v` is not a vertex."
033    [s o v]
034    (if-not (v/vertex? o)
035      (if (:id o)
036        (if (v/vertex? v)
037          (let [vi (or (:vertex-index s) {})
038                current (or (vi (:id v)) {})]
039            ;; deep-merge doesn't merge sets, only maps; so at this
040            ;; stage we need to build a map.
041            (assoc vi (:id v) (assoc current (:id o) (:id v))))
042          (throw (IllegalArgumentException. "Not a vertex: " v)))
043        (throw (IllegalArgumentException. (subs (str "No `:id` value: " o) 0 80))))
044      ;; it shouldn't actually be an error to try to index a vertex, but it
045      ;; also isn't useful to do so, so I'd be inclined to ignore it.
046      (:vertex-index s)))
047  
048  (defn index-vertices
049    "Return a superstructure like `s` in which object `o` is indexed by its
050    vertices. It is an error (and an exception may be thrown) if
051  
052    1. `s` is not a map;
053    2. `o` is not a map;
054    3. `o` does not have a value for the key `:id`."
055    [s o]
056    (assoc
057      s
058      :vertex-index
059      (reduce
060        u/deep-merge
061        (map
062          #(index-vertex s o %)
063          (u/vertices o)))))
064  
065  (defn add-to-superstructure
066    "Return a superstructure like `s` with object `o` added. If `o` is a collection,
067    return a superstructure like `s` with each element of `o` added. If only one
068    argument is supplied it will be assumed to represent `o` and a new
069    superstructure will be returned.
070  
071    It is an error (and an exception may be thrown) if
072  
073    1. `s` is not a map;
074    2. `o` is not a map, or a sequence of maps."
075    ([o]
076     (add-to-superstructure {} o))
077    ([s o]
078    (cond
079      (map? o) (let [o' (if (:id o) o (assoc o :id (keyword (gensym "obj"))))]
080                 (index-vertices (assoc s (:id o') o') o'))
081      (coll? o) (reduce u/deep-merge (map #(add-to-superstructure s %) o))
082      (nil? o) o
083      :else
084      (throw (IllegalArgumentException. (str "Don't know how to index " (or (type o) "nil")))))))
085