diff --git a/src/cc/journeyman/walkmap/edge.clj b/src/cc/journeyman/walkmap/edge.clj index 89eeafc..9d105cd 100644 --- a/src/cc/journeyman/walkmap/edge.clj +++ b/src/cc/journeyman/walkmap/edge.clj @@ -3,7 +3,6 @@ An edge is a line segment having just a start and an end, with no intervening nodes." (:require [clojure.math.numeric-tower :as m] - [cc.journeyman.walkmap.utils :as u] [cc.journeyman.walkmap.vertex :refer [canonicalise check-vertex ensure2d ensure3d vertex vertex= vertex?]])) (defn edge @@ -149,7 +148,7 @@ not an edge." ([e1 e2] (intersection2d e1 e2 :x :y :z)) - ([e1 e2 c1 c2 c3] + ([e1 e2 c1 c2 _c3] (if (and (edge? e1) (edge? e2)) (when (overlaps2d? e1 e2) ;; relatively cheap check diff --git a/src/cc/journeyman/walkmap/microworld.clj b/src/cc/journeyman/walkmap/microworld.clj index 95d1f69..8a3d383 100644 --- a/src/cc/journeyman/walkmap/microworld.clj +++ b/src/cc/journeyman/walkmap/microworld.clj @@ -2,36 +2,36 @@ "An interface between walkmap and microworld, to allow use of microworld functionality to model things like rainfall, soil fertility, settlement and so on." - (:require [clojure.edn :as edn :only [read]] - [clojure.java.io :as io] - [clojure.string :as s] - [taoensso.timbre :as l] - [cc.journeyman.walkmap.edge :as e] - [cc.journeyman.walkmap.polygon :as p :only [rectangle]] + (:require [cc.journeyman.walkmap.polygon :refer [rectangle]] [cc.journeyman.walkmap.superstructure :refer [store]] - [cc.journeyman.walkmap.tag :as t :only [tag]] - [cc.journeyman.walkmap.vertex :as v :only [check-vertex vertex vertex?]] - [cc.journeyman.walkmap.utils :as u :only [truncate]])) + [cc.journeyman.walkmap.tag :refer [tag]] + [cc.journeyman.walkmap.vertex :refer [check-vertex vertex]] + [cc.journeyman.walkmap.utils :refer [truncate]] + [clojure.edn :as edn :only [read]] + [clojure.java.io :refer [reader]] + [taoensso.timbre :refer [error]]) + (:import [clojure.lang Keyword Map] + [java.io PushbackReader])) (defn cell->polygon "From this MicroWorld `cell`, construct a walkmap polygon (specifically, - a rectangle. If `scale-vector` passed and is a vertex, scale all the vertices + a rectangle). If `scale-vector` passed and is a vertex, scale all the vertices in the cell by that vector." ([cell] - (cell->polygon cell (v/vertex 1 1 1))) + (cell->polygon cell (vertex 1 1 1))) ([cell scale-vector] - (t/tag + (tag (assoc (merge cell - (let [w (* (:x cell) (:x (v/check-vertex scale-vector))) + (let [w (* (:x cell) (:x (check-vertex scale-vector))) s (* (:y cell) (:y scale-vector)) e (+ w (:x scale-vector)) n (+ s (:y scale-vector)) z (* (:altitude cell) (:z scale-vector))] - (p/rectangle - (v/vertex s w z) - (v/vertex n e z)))) + (rectangle + (vertex s w z) + (vertex n e z)))) :walkmap.id/id (keyword (gensym "mw-cell"))) (:state cell)))) @@ -42,23 +42,23 @@ compute-heavy that it's much more sensible to do it in batch mode. So the better plan is to be able to pull the output from MicroWorld - as an EDN structure - into a walkmap superstructure." - ([filename] + ([^String filename] (load-microworld-edn filename :mw)) - ([filename map-kind] + ([^String filename ^Keyword map-kind] (when-not (keyword? map-kind) (throw (IllegalArgumentException. - (u/truncate - (str "Must be a keyword: " (or map-kind "nil")) 80)))) + (truncate + (format "Must be a keyword: %s." (or map-kind "nil")) 80)))) (load-microworld-edn filename map-kind nil)) - ([filename mapkind superstucture] - (load-microworld-edn filename mapkind superstucture (v/vertex 1 1 1))) - ([filename map-kind superstructure scale-vertex] + ([^String filename ^Keyword mapkind ^Map superstucture] + (load-microworld-edn filename mapkind superstucture (vertex 1 1 1))) + ([^String filename ^Keyword _map-kind ^Map superstructure ^Map _scale-vertex] (let [mw (try - (with-open [r (io/reader filename)] - (edn/read (java.io.PushbackReader. r))) + (with-open [r (reader filename)] + (edn/read (PushbackReader. r))) (catch RuntimeException e - (l/error "Error parsing edn file '%s': %s\n" + (error "Error parsing edn file '%s': %s\n" filename (.getMessage e)))) polys (reduce concat diff --git a/src/cc/journeyman/walkmap/path.clj b/src/cc/journeyman/walkmap/path.clj index b367736..34c70b1 100644 --- a/src/cc/journeyman/walkmap/path.clj +++ b/src/cc/journeyman/walkmap/path.clj @@ -2,11 +2,9 @@ "Essentially the specification for things we shall consider to be path. **Note that** for these purposes `path` means any continuous linear feature, where such features specifically include watercourses." - (:require [clojure.string :as s] - [cc.journeyman.walkmap.edge :as e] - [cc.journeyman.walkmap.polygon :refer [check-polygon polygon?]] - [cc.journeyman.walkmap.tag :refer [tag tags]] - [cc.journeyman.walkmap.utils :refer [check-kind-type check-kind-type-seq kind-type]] + (:require [cc.journeyman.walkmap.edge :as e] + [cc.journeyman.walkmap.polygon :refer [polygon?]] + [cc.journeyman.walkmap.utils :refer [check-kind-type check-kind-type-seq]] [cc.journeyman.walkmap.vertex :refer [check-vertices vertex?]])) (defn path? diff --git a/src/cc/journeyman/walkmap/polygon.clj b/src/cc/journeyman/walkmap/polygon.clj index 6107799..87ac2d3 100644 --- a/src/cc/journeyman/walkmap/polygon.clj +++ b/src/cc/journeyman/walkmap/polygon.clj @@ -139,7 +139,7 @@ projection of this rectangle on that plane?" [vertex rectangle] (let [xo (sort-by :x (:vertices rectangle)) - yo (sort-by :x (:vertices rectangle))] + yo (sort-by :y (:vertices rectangle))] (and (< (:x (first xo)) (:x vertex) (:x (last xo))) (< (:y (first yo)) (:y vertex) (:y (last yo)))))) diff --git a/src/cc/journeyman/walkmap/read_svg.clj b/src/cc/journeyman/walkmap/read_svg.clj index c061831..5092828 100644 --- a/src/cc/journeyman/walkmap/read_svg.clj +++ b/src/cc/journeyman/walkmap/read_svg.clj @@ -1,16 +1,11 @@ (ns cc.journeyman.walkmap.read-svg "Utility functions for scalable vector graphics (SVG) into walkmap structures." - (:require [clojure.data.zip :as dz] - [clojure.data.zip.xml :as zx] - [clojure.java.io :as io] + (:require [clojure.java.io :as io] [clojure.string :as s] [clojure.xml :as x] - [clojure.zip :as z] - [taoensso.timbre :as l] [cc.journeyman.walkmap.path :refer [path]] [cc.journeyman.walkmap.tag :refer [tag]] - [cc.journeyman.walkmap.utils :refer [kind-type truncate]] [cc.journeyman.walkmap.vertex :refer [vertex vertex?]])) (defn upper-case? @@ -91,7 +86,7 @@ ;; quite small XML files. So I've implemented my own solution. ([file-name] (read-svg file-name nil)) - ([file-name map-kind] + ([file-name _map-kind] (let [xml (x/parse (io/file file-name)) paths (progeny xml #(= (:tag %) :path))] (remove nil? (map path-elt->path paths))))) diff --git a/src/cc/journeyman/walkmap/routing.clj b/src/cc/journeyman/walkmap/routing.clj index c7608c8..d6dc4aa 100644 --- a/src/cc/journeyman/walkmap/routing.clj +++ b/src/cc/journeyman/walkmap/routing.clj @@ -1,16 +1,15 @@ (ns cc.journeyman.walkmap.routing "Finding optimal routes to traverse a map." - (:require [clojure.math.numeric-tower :as m :only [expt]] + (:require [clojure.math.numeric-tower :refer [expt]] [clojure.set :refer [intersection]] - [clojure.string :as cs :only [join]] - [search.core :refer [a*]] [cc.journeyman.walkmap.edge :as e] [cc.journeyman.walkmap.path :as p] [cc.journeyman.walkmap.polygon :as q] [cc.journeyman.walkmap.superstructure :as s] - [cc.journeyman.walkmap.tag :as t] - [cc.journeyman.walkmap.utils :as u] - [cc.journeyman.walkmap.vertex :as v])) + [cc.journeyman.walkmap.tag :refer [tags]] + [cc.journeyman.walkmap.vertex :as v] + [search.core :refer [a*]] + [taoensso.timbre :refer [debug]])) ;; Breadth first search is a good algorithm for terrain in which all steps have ;; equal, but in our world (like the real world), they don't. @@ -37,25 +36,30 @@ of traversals. Dynamic, because we will want to tune this." -2) +(def ^:dynamic *untraversable* + "The set of all tags which may indicate that a polygon should not be + traversed." + #{:no-traversal}) + (defn traversable? "True if this `object` is a polygon which can be considered as part of the walkmap." [object] (and - (q/polygon? object) - (:centre object) - (not (t/tagged? object :no-traversal)))) + (q/polygon? object) + (:centre object) + (empty? (intersection (tags object) *untraversable*)))) (declare traversal-cost) (defn vertices-traversal-cost [vertices s] (reduce - + - (map - #(traversal-cost %1 %2 s) - (v/check-vertices vertices) - (rest vertices)))) + + + (map + #(traversal-cost %1 %2 s) + (v/check-vertices vertices) + (rest vertices)))) (defn path-traversal-cost [path s] @@ -64,7 +68,8 @@ (defn barriers-crossed "Search superstructure `s` and return a sequence of barriers, if any, which obstruct traversal from vertex `from` to vertex `to`." - [from to s] + [from to _s] + (debug (format "barriers-crossed called with vertices\n\tfrom: %s\n\tto: %s" from to)) ;; TODO: implement '()) @@ -74,7 +79,8 @@ of superstructure `s`. If there's a bridge, ferry or other crossing mechanism in `s` at the intersection of the vertex and the barrier, then the penalty should be substantially less than it would otherwise be." - [barrier from to s] + [barrier from to _s] + (debug (format "crossing-penalty called with arguments\n\tbarrier: %s\n\tfrom: %s\n\tto: %s" barrier from to)) ;; TODO: implement 0) @@ -83,7 +89,7 @@ [edge] (let [g (:z (e/unit-vector edge))] (if (pos? g) - (m/expt (inc g) *gradient-exponent*) + (expt (inc g) *gradient-exponent*) 1))) ;; (gradient-cost (e/edge (v/vertex 0 0 0) (v/vertex 0 1 0))) @@ -98,10 +104,10 @@ [from to s] (let [f (fn [v] (set (s/touching v p/path? s)))] (first - (sort-by + (sort-by ;;; I... chose the path more travelled by. - #(or (:traversals %) 0) - (filter traversable? (intersection (f from) (f to))))))) + #(or (:traversals %) 0) + (filter traversable? (intersection (f from) (f to))))))) (defn road-bonus "Calculate the road bonus of the edge represented by the vertices `from`, @@ -112,7 +118,7 @@ [from to s] (let [best (best-road from to s)] (when (:traversals best) - (m/expt (:traversals best) *traversals-exponent*)))) + (expt (:traversals best) *traversals-exponent*)))) (defn traversal-cost "Return the traversal cost of the edge represented by the vertices `from`, @@ -125,14 +131,14 @@ (let [edge (e/edge from to) distance (e/length edge)] (/ - (+ - (* distance - (gradient-cost edge)) - (reduce + - (map - #(crossing-penalty [% from to s]) - (barriers-crossed from to s)))) - (or (road-bonus from to s) 1))))) + (+ + (* distance + (gradient-cost edge)) + (reduce + + (map + #(crossing-penalty % from to s) + (barriers-crossed from to s)))) + (or (road-bonus from to s) 1))))) ;; (def p '({:x 1.40625, :y 0, :kind :vertex, :walkmap.id/id :vert_1-40625_0} ;; {:x 1.40625, :y -10.703125, :kind :vertex, :walkmap.id/id :vert_1-40625_-10-703125} @@ -154,11 +160,11 @@ ([frontier candidates] (extend-frontier frontier candidates nil)) ([frontier candidates rejects] - (if + (if (empty? frontier) - candidates - (let [fs (set (concat frontier rejects))] - (concat frontier (remove fs candidates)))))) + candidates + (let [fs (set (concat frontier rejects))] + (concat frontier (remove fs candidates)))))) ;; (extend-frontier '(1 2 3 4 5) '(7 3 6 2 9 8) '(6 8)) ;; (extend-frontier '(1 2 3 4 5) '(7 3 6 2 9 8)) @@ -176,11 +182,10 @@ [target search-radius s] (let [r (s/nearest s target traversable? search-radius)] (when-not r (throw - (Exception. - (cs/join " " ["Unable to find walkable facet within" - search-radius - "of" - target])))) + (Exception. + (format "Unable to find walkable facet within %s of %s" + search-radius + target)))) r)) (defn route diff --git a/src/cc/journeyman/walkmap/stl.clj b/src/cc/journeyman/walkmap/stl.clj index 83972fd..3ec5c81 100644 --- a/src/cc/journeyman/walkmap/stl.clj +++ b/src/cc/journeyman/walkmap/stl.clj @@ -1,19 +1,18 @@ (ns cc.journeyman.walkmap.stl "Utility functions dealing with stereolithography (STL) files. Not a stable API yet!" - (:require [clojure.java.io :as io :refer [file output-stream input-stream]] - [clojure.string :as s] - [me.raynes.fs :as fs] - [org.clojars.smee.binary.core :as b] - [taoensso.timbre :as l] - [cc.journeyman.walkmap.edge :as e] - [cc.journeyman.walkmap.ocean :as o] - [cc.journeyman.walkmap.polygon :refer [centre gradient polygon?]] - [cc.journeyman.walkmap.superstructure :refer [store]] - [cc.journeyman.walkmap.tag :refer [tag]] - [cc.journeyman.walkmap.utils :as u] - [cc.journeyman.walkmap.vertex :as v]) - (:import org.clojars.smee.binary.core.BinaryIO - java.io.DataInput)) + (:require + [cc.journeyman.walkmap.ocean :refer [ocean?]] + [cc.journeyman.walkmap.polygon :refer [centre gradient polygon?]] + [cc.journeyman.walkmap.superstructure :refer [store]] + [cc.journeyman.walkmap.tag :refer [tag]] + [cc.journeyman.walkmap.utils :refer [truncate]] + [cc.journeyman.walkmap.vertex :as v] + [clojure.lang.io :refer [input-stream]] + [clojure.string :as s] + [me.raynes.fs :refer [base-name split-ext]] + [org.clojars.smee.binary.core :as b] + [taoensso.timbre :refer [debug]]) + (:import [clojure.lang Keyword])) (defn stl? "True if `o` is recogniseable as an STL structure. An STL structure must @@ -64,13 +63,13 @@ (defaults to `:height`). It is an error, and an exception will be thrown, if `map-kind` is not a keyword." ([o] (canonicalise o :height)) - ([o map-kind] + ([o ^Keyword map-kind] (canonicalise o map-kind (v/vertex 1 1 1))) - ([o map-kind scale-vertex] + ([o ^Keyword map-kind scale-vertex] (when-not (keyword? map-kind) (throw (IllegalArgumentException. - (u/truncate (str "Must be a keyword: " (or map-kind "nil")) 80)))) + (truncate (str "Must be a keyword: " (or map-kind "nil")) 80)))) (cond (and (coll? o) (not (map? o))) (map #(canonicalise % map-kind) o) ;; if it has :facets it's an STL structure, but it doesn't yet conform to `stl?` @@ -92,13 +91,13 @@ (:vertices o) map-kind)) :facet map-kind)))] - (if (o/ocean? f) + (if (ocean? f) (tag f :ocean :no-traversal) f)) ;; if it has a value for :x it's a vertex, but it may not yet conform ;; to `vertex?`; it should also be scaled using the scale-vertex, if any. (:x o) (let [c (v/canonicalise o)] - (if scale-vertex + (if (v/vertex? scale-vertex) (v/vertex* c scale-vertex) c)) ;; shouldn't happen @@ -122,18 +121,14 @@ **NOTE** that we've no way of verifying that the input file is binary STL data, if it is not this will run but will return garbage." - ([filename] + ([^String filename] (decode-binary-stl filename :height)) - ([filename map-kind] - (when-not - (keyword? map-kind) - (throw (IllegalArgumentException. - (u/truncate (str "Must be a keyword: " (or map-kind "nil")) 80)))) + ([^String filename ^Keyword map-kind] (decode-binary-stl filename map-kind nil)) - ([filename mapkind superstucture] + ([^String filename ^Keyword mapkind superstucture] (decode-binary-stl filename mapkind superstucture (v/vertex 1 1 1))) - ([filename map-kind superstructure scale-vertex] - (let [in (io/input-stream filename) + ([^String filename ^Keyword map-kind superstructure scale-vertex] + (let [in (input-stream filename) stl (canonicalise (b/decode binary-stl in) map-kind scale-vertex)] (if (map? superstructure) @@ -175,12 +170,12 @@ "Write an `stl` structure as read by `decode-binary-stl` to this `filename` as ASCII encoded STL." ([filename stl] - (let [b (fs/base-name filename true)] + (let [b (base-name filename true)] (write-ascii-stl filename stl (subs b 0 (or (s/index-of b ".") (count b)))))) ([filename stl solidname] - (l/debug "Solid name is " solidname) + (debug "Solid name is " solidname) (spit filename (stl->ascii stl solidname)))) @@ -190,7 +185,7 @@ `out-filename`, if specified; otherwise, to a file with the same basename as `in-filename` but the extension `.ascii.stl`." ([in-filename] - (let [[_ ext] (fs/split-ext in-filename)] + (let [[_ ext] (split-ext in-filename)] (binary-stl-to-ascii in-filename (str diff --git a/src/cc/journeyman/walkmap/superstructure.clj b/src/cc/journeyman/walkmap/superstructure.clj index 7c6900a..ded4ae4 100644 --- a/src/cc/journeyman/walkmap/superstructure.clj +++ b/src/cc/journeyman/walkmap/superstructure.clj @@ -3,15 +3,16 @@ (:require [clojure.walk :refer [postwalk]] [taoensso.timbre :as l] [cc.journeyman.walkmap.edge :refer [edge length]] - [cc.journeyman.walkmap.path :as p] - [cc.journeyman.walkmap.polygon :as q] + ;; [cc.journeyman.walkmap.path :as p] + ;; [cc.journeyman.walkmap.polygon :as q] [cc.journeyman.walkmap.utils :as u] - [cc.journeyman.walkmap.vertex :as v])) + [cc.journeyman.walkmap.vertex :as v]) + (:import [clojure.lang IFn Map])) ;; TODO: Think about reification/dereification. How can we cull a polygon, if ;; some vertices still index it? I *think* that what's needed is that when ;; we store something in the superstructure, we replace all its vertices (and -;; other dependent structures, if any with their ids - as well as, obviously, +;; other dependent structures, if any) with their ids - as well as, obviously, ;; adding/merging those vertices/dependent structures into the superstructure ;; as first class objects in themselves. That means, for each identified thing, ;; the superstructure only contains one copy of it. @@ -31,14 +32,14 @@ [o] (when (map? o) (reduce - concat - (remove - nil? - (map - #(cond - (v/vertex? %) (list %) - (and (coll? %) (every? v/vertex? %)) %) - (vals o)))))) + concat + (remove + nil? + (map + #(cond + (v/vertex? %) (list %) + (and (coll? %) (every? v/vertex? %)) %) + (vals o)))))) ;; (cond ;; (v/vertex? o) (list o) ;; (q/polygon? o) (:vertices o) @@ -53,7 +54,7 @@ 2. `o` is not a map; 3. `o` does not have a value for the key `:walkmap.id/id`; 4. `v` is not a vertex." - [s o v] + [^Map s ^Map o ^Map v] (if-not (v/vertex? o) (if (:walkmap.id/id o) (if (v/vertex? v) @@ -77,14 +78,14 @@ 3. `o` does not have a value for the key `:walkmap.id/id`." [s o] (u/deep-merge - s - {::vertex-index - (reduce - u/deep-merge - {} - (map - #(index-vertex s o %) - (:vertices o)))})) + s + {::vertex-index + (reduce + u/deep-merge + {} + (map + #(index-vertex s o %) + (:vertices o)))})) (defn in-retrieve "Internal guts of `retrieve`, q.v. `x` can be anything; `s` must be a @@ -99,17 +100,17 @@ x) ;; if it's a map, for every key which is not `:walkmap.id/id`, recurse. (map? x) (let [v (reduce - (fn [m k] - (assoc m k (in-retrieve (x k) s))) - {} - (keys (dissoc x :walkmap.id/id))) + (fn [m k] + (assoc m k (in-retrieve (x k) s))) + {} + (keys (dissoc x :walkmap.id/id))) id (:walkmap.id/id x)] ;; if it has an id, bind it to that id in the returned value. (if id (assoc - v - :walkmap.id/id - (:walkmap.id/id x)) + v + :walkmap.id/id + (:walkmap.id/id x)) v)) (set? x) x ;; TODO: should I search in sets for objects when storing? (coll? x) (map #(in-retrieve % s) x) @@ -132,9 +133,9 @@ (set? o) s ;; TODO: should I search in sets for objects when storing? (map? o) (if (:walkmap.id/id o) (assoc - (in-store-find-objects (vals o) s) - (:walkmap.id/id o) - o) + (in-store-find-objects (vals o) s) + (:walkmap.id/id o) + o) (in-store-find-objects (vals o) s)) (coll? o) (reduce merge s (map #(in-store-find-objects % s) o)) :else s))) @@ -145,9 +146,9 @@ `in-store`, q.v. Use at your own peril." [o] (assoc - (postwalk #(or (:walkmap.id/id %) %) (dissoc o :walkmap.id/id)) - :walkmap.id/id - (:walkmap.id/id o))) + (postwalk #(or (:walkmap.id/id %) %) (dissoc o :walkmap.id/id)) + :walkmap.id/id + (:walkmap.id/id o))) ;; (in-store-replace-with-keys (p/path (v/vertex 0 0 0) (v/vertex 0 1 2) (v/vertex 3 3 3))) ;; (in-store-find-objects (p/path (v/vertex 0 0 0) (v/vertex 0 1 2) (v/vertex 3 3 3))) @@ -161,22 +162,22 @@ 1. `s` is not a map; 2. `o` is not a recognisable walkmap object" - ([o] + ([^Map o] (store o {})) - ([o s] + ([^Map o ^Map s] (when-not (:walkmap.id/id o) (throw - (IllegalArgumentException. - (str "Not a walkmap object: no value for `:walkmap.id/id`: " - (u/kind-type o))))) + (IllegalArgumentException. + (str "Not a walkmap object: no value for `:walkmap.id/id`: " + (u/kind-type o))))) (when-not (map? s) (throw - (IllegalArgumentException. - (str "Superstructure must be a map: " (u/kind-type s))))) + (IllegalArgumentException. + (str "Superstructure must be a map: " (u/kind-type s))))) (assoc - (u/deep-merge s (in-store-find-objects o) (index-vertices s o)) - (:walkmap.id/id o) - (in-store-replace-with-keys o)))) + (u/deep-merge s (in-store-find-objects o) (index-vertices s o)) + (:walkmap.id/id o) + (in-store-replace-with-keys o)))) (defn search-vertices "Search superstructure `s` for vertices within the box defined by vertices @@ -188,14 +189,14 @@ have properties which will be denormalised by `store`, and therefore do not have to restored with `retrieve`. If properties are added to vertices whose values are objects, then this will have to be rewritten." - ([s minv maxv] + ([^Map s ^Map minv ^Map maxv] (search-vertices s minv maxv false)) ([s minv maxv d2?] (let [minv' (if d2? (assoc minv :z Double/NEGATIVE_INFINITY) minv) maxv' (if d2? (assoc maxv :z Double/POSITIVE_INFINITY) maxv)] (filter - #(v/within-box? % minv maxv) - (filter #(= (:kind %) :vertex) (vals s)))))) + #(v/within-box? % minv' maxv') + (filter #(= (:kind %) :vertex) (vals s)))))) (defn nearest "Search superstructure `s` for the nearest object matching `filter-fn` to @@ -205,68 +206,72 @@ WARNING: currently only returns objects which have a defined `:centre` (but most of the significant objects we have do)." - [s target filter-fn radius] - (let [minv (v/vertex + ([^Map s ^Map target ^Number radius] + (nearest s target :centre radius)) + ([^Map s ^Map target ^IFn filter-fn ^Number radius] + (let [minv (v/vertex (- (:x (v/check-vertex target)) radius) (- (:y target) radius) (- (or (:z target) 0) radius)) - maxv (v/vertex + maxv (v/vertex (+ (:x target) 0.5) (+ (:y target) 0.5) (+ (or (:z target) 0) 0.5))] ;; filter those objects with the filter function, then sort that list ;; by the edge distance from the target to the `:centre` of the object ;; and take the first - (first + (first (sort-by - #(length (edge target (:centre %))) + #(length (edge target (:centre %))) + (filter + filter-fn (filter - :centre - (map #(retrieve % s) + :centre + (map #(retrieve % s) ;; for each vertex id in vids, get the objects associated with that id ;; in the vertex index as a single flat list - (reduce - concat - (remove - nil? - (map - #(-> s ::vertex-index % keys) + (reduce + concat + (remove + nil? + (map + #(-> s ::vertex-index % keys) ;; get all the vertex ids within radius of the target - (set - (map - :walkmap.id/id - (search-vertices s minv maxv)))))))))))) + (set + (map + :walkmap.id/id + (search-vertices s minv maxv)))))))))))))) (defn touching "Return a sequence of all objects in superstructure `s` which are indexed as touching the vertex `v`." - ([vertex s] + ([^Map vertex ^Map s] (map - #(retrieve % s) - (set (-> s :vertex-index (:walkmap.id/id (v/check-vertex vertex)) keys)))) - ([vertex filter-fn s] + #(retrieve % s) + (set (-> s :vertex-index (:walkmap.id/id (v/check-vertex vertex)) keys)))) + ([^Map vertex ^IFn filter-fn ^Map s] (filter - filter-fn - (touching vertex s)))) + filter-fn + (touching vertex s)))) (defn neighbours "Return a sequence of all those objects in superstructure `s` which share at least one vertex with `target`, and which are matched by `filter-fn` if supplied." - ([target s] - (neighbours identity s)) - ([target filter-fn s] + ([^Map target ^Map s] + (neighbours target identity s)) + ([^Map target ^IFn filter-fn ^Map s] (remove - #(= target %) - (reduce - concat - (remove - nil? - (map #(touching % filter-fn s) (vertices target))))))) + #(= target %) + (reduce + concat + (remove + nil? + (map #(touching % filter-fn s) (vertices target))))))) (defn neighbour-ids "Return a sequence of the ids all those objects in superstructure `s` which share at least one vertex with `target`, and which are matched by `filter-fn` if supplied." - ([target s] + ([^Map target ^Map s] (neighbour-ids target identity s)) - ([target filter-fn s] + ([^Map target ^IFn filter-fn ^Map s] (map :walkmap.id/id (neighbours target filter-fn s)))) diff --git a/src/cc/journeyman/walkmap/svg.clj b/src/cc/journeyman/walkmap/svg.clj index 6d78b5b..86f8c53 100644 --- a/src/cc/journeyman/walkmap/svg.clj +++ b/src/cc/journeyman/walkmap/svg.clj @@ -2,16 +2,12 @@ "Utility functions for writing stereolithography (STL) files (and possibly, later, other geometry files of interest to us) as scalable vector graphics (SVG)." - (:require [clojure.java.io :as io] - [clojure.string :as s] - [clojure.xml :as x] + (:require [clojure.string :as s] [dali.io :as neatly-folded-clock] [hiccup.core :refer [html]] - [taoensso.timbre :as l :refer [info error spy]] + [taoensso.timbre :refer [info]] [cc.journeyman.walkmap.ocean :refer [cull-ocean-facets]] - [cc.journeyman.walkmap.polygon :refer [polygon?]] - [cc.journeyman.walkmap.stl :refer [decode-binary-stl]] - [cc.journeyman.walkmap.vertex :refer [vertex?]])) + [cc.journeyman.walkmap.stl :refer [decode-binary-stl]])) (def ^:dynamic *preferred-svg-render* "Mainly for debugging dali; switch SVG renderer to use. Expected values: @@ -88,7 +84,7 @@ (map #(reduce max (map :y (:vertices %))) (:facets stl)))] - (l/info "Generating SVG for " *preferred-svg-render* " renderer") + (info "Generating SVG for " *preferred-svg-render* " renderer") (case *preferred-svg-render* :hiccup (hiccup-stl->svg stl minx maxx miny maxy) :dali (dali-stl->svg stl minx maxx miny maxy) @@ -102,7 +98,7 @@ (stl->svg (cull-ocean-facets (decode-binary-stl in-filename)))) ([in-filename out-filename] (let [s (binary-stl-file->svg in-filename)] - (l/info "Emitting SVG with " *preferred-svg-render* " renderer") + (info "Emitting SVG with " *preferred-svg-render* " renderer") (case *preferred-svg-render* :dali (neatly-folded-clock/render-svg s out-filename) :hiccup (spit out-filename (html s)) diff --git a/src/cc/journeyman/walkmap/tag.clj b/src/cc/journeyman/walkmap/tag.clj index 3ac12ce..00ed8e1 100644 --- a/src/cc/journeyman/walkmap/tag.clj +++ b/src/cc/journeyman/walkmap/tag.clj @@ -4,7 +4,8 @@ This is in an attempt to avoid name clashes with other uses of this key." (:require [clojure.set :refer [difference union]] [taoensso.timbre :as l] - [cc.journeyman.walkmap.utils :refer [kind-type]])) + [cc.journeyman.walkmap.utils :refer [kind-type]]) + (:import [clojure.lang Map])) (defn tagged? "True if this `object` is tagged with each of these `tags`. It is an error @@ -12,10 +13,7 @@ 1. `object` is not a map; 2. any of `tags` is not a keyword." - [object & tags] - (when-not (map? object) - (throw (IllegalArgumentException. - (str "Must be a map: " (kind-type object))))) + [^Map object & tags] (let [tags' (flatten tags)] (when-not (every? keyword? tags') (throw (IllegalArgumentException. @@ -35,11 +33,8 @@ It's legal to include sequences of keywords in `tags`, so that users can do useful things like `(tag obj (map keyword some-strings))`." - [object & tags] + [^Map object & tags] (l/debug "Tagging" (kind-type object) "with" tags) - (when-not (map? object) - (throw (IllegalArgumentException. - (str "Must be a map: " (kind-type object))))) (let [tags' (flatten tags)] (when-not (every? keyword? tags') (throw (IllegalArgumentException. @@ -57,10 +52,7 @@ 1. `object` is not a map; 2. any of `tags` is not a keyword or sequence of keywords." - [object & tags] - (when-not (map? object) - (throw (IllegalArgumentException. - (str "Must be a map: " (kind-type object))))) + [^Map object & tags] (let [tags' (flatten tags)] (when-not (every? keyword? tags') (throw (IllegalArgumentException. diff --git a/src/cc/journeyman/walkmap/utils.clj b/src/cc/journeyman/walkmap/utils.clj index 199d8e8..ecef977 100644 --- a/src/cc/journeyman/walkmap/utils.clj +++ b/src/cc/journeyman/walkmap/utils.clj @@ -3,16 +3,16 @@ (:require [clojure.edn :as edn :only [read]] [clojure.java.io :as io] [clojure.math.numeric-tower :as m] + [clojure.set :refer [union]] [clojure.string :as s])) (defn deep-merge "Recursively merges maps. If vals are not maps, the last value wins." - ;; TODO: not my implementation, not sure I entirely trust it. - ;; TODO TODO: if we are to successfully merge walkmap objects, we must - ;; return, on each object, the union of its tags if any. + ;; TODO: not my implementation, not sure I entirely trust it. [& vals] (if (every? map? vals) - (apply merge-with deep-merge vals) + (let [tags (apply union (map :cc.journeyman.walkmap.tag/tags vals))] + (assoc (apply merge-with deep-merge vals) :cc.journeyman.walkmap.tag/tags tags)) (last vals))) (defn truncate @@ -27,20 +27,20 @@ "Identify the type of an `object`, e.g. for logging. If it has a `:kind` key, it's one of ours, and that's what we want. Otherwise, we want its type; but the type of `nil` is `nil`, which doesn't get printed when assembling error - ,essages, so return \"nil\"." + messages, so return \"nil\"." [object] (or (:kind object) (type object) "nil")) (defn =ish "True if numbers `n1`, `n2` are roughly equal; that is to say, equal to within `tolerance` (defaults to one part in one hundred thousand)." - ([n1 n2] + ([^Number n1 ^Number n2] (if (and (number? n1) (number? n2)) (let [m (m/abs (min n1 n2)) t (if (zero? m) 0.00001 (* 0.00001 m))] (=ish n1 n2 t)) (= n1 n2))) - ([n1 n2 tolerance] + ([^Number n1 ^Number n2 ^Number tolerance] (if (and (number? n1) (number? n2)) (< (m/abs (- n1 n2)) tolerance) (= n1 n2)))) @@ -55,18 +55,18 @@ ([object expected] `(if-not (= (kind-type ~object) ~expected) (throw - (IllegalArgumentException. - (s/join - " " - ["Expected" ~expected "but found" (kind-type ~object)]))) + (IllegalArgumentException. + (s/join + " " + ["Expected" ~expected "but found" (kind-type ~object)]))) ~object)) ([object checkfn expected] `(if-not (~checkfn ~object) (throw - (IllegalArgumentException. - (s/join - " " - ["Expected" ~expected "but found" (kind-type ~object)]))) + (IllegalArgumentException. + (s/join + " " + ["Expected" ~expected "but found" (kind-type ~object)]))) ~object))) (defmacro check-kind-type-seq @@ -77,29 +77,29 @@ Macro, so that the exception is thrown from the calling function." ([s expected] - `(if-not (every? #(= (kind-type %) ~expected) ~s) - (throw + `(if-not (every? #(= (kind-type %) ~expected) ~s) + (throw (IllegalArgumentException. - (s/join - " " - ["Expected sequence of" - ~expected - "but found (" - (s/join ", " (remove #(= ~expected %) (map kind-type ~s))) - ")"]))) - ~s)) + (s/join + " " + ["Expected sequence of" + ~expected + "but found (" + (s/join ", " (remove #(= ~expected %) (map kind-type ~s))) + ")"]))) + ~s)) ([s checkfn expected] - `(if-not (every? #(~checkfn %) ~s) - (throw + `(if-not (every? #(~checkfn %) ~s) + (throw (IllegalArgumentException. - (s/join - " " - ["Expected sequence of" - ~expected - "but found (" - (s/join ", " (remove #(= ~expected %) (map kind-type ~s))) - ")"]))) - ~s))) + (s/join + " " + ["Expected sequence of" + ~expected + "but found (" + (s/join ", " (remove #(= ~expected %) (map kind-type ~s))) + ")"]))) + ~s))) (defn load-edn "Load edn from an io/reader source (filename or io/resource)." @@ -115,5 +115,5 @@ (defn not-yet-implemented [message] (throw - (UnsupportedOperationException. - (str "Not yet implemented: " message)))) + (UnsupportedOperationException. + (str "Not yet implemented: " message))))