# walkmap/superstructure.clj

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