diff --git a/docs/cloverage/coverage.css b/docs/cloverage/coverage.css
new file mode 100644
index 0000000..2be4e57
--- /dev/null
+++ b/docs/cloverage/coverage.css
@@ -0,0 +1,40 @@
+.covered {
+ font-family: 'Bitstream Vera Sans Mono', 'Courier', monospace;
+ background-color: #558B55;
+}
+
+.not-covered {
+ font-family: 'Bitstream Vera Sans Mono', 'Courier', monospace;
+ background-color: red;
+}
+
+.partial {
+ font-family: 'Bitstream Vera Sans Mono', 'Courier', monospace;
+ background-color: orange;
+}
+
+.not-tracked {
+ font-family: 'Bitstream Vera Sans Mono', 'Courier', monospace;
+}
+
+.blank {
+ font-family: 'Bitstream Vera Sans Mono', 'Courier', monospace;
+}
+
+td {
+ padding-right: 10px;
+}
+
+td.with-bar {
+ width: 250px;
+ text-align: center;
+}
+
+td.with-number {
+ text-align: right;
+}
+
+td.ns-name {
+ min-width: 150px;
+ padding-right: 25px;
+}
diff --git a/docs/cloverage/index.html b/docs/cloverage/index.html
new file mode 100644
index 0000000..adaca25
--- /dev/null
+++ b/docs/cloverage/index.html
@@ -0,0 +1,149 @@
+
+
+
+
+ Coverage Summary
+
+
+
+
+ Namespace |
+ Forms |
+Forms % |
+ Lines |
+Lines % |
+Total | Blank | Instrumented |
+
+
+ walkmap.core | 6 43 |
+12.24 % |
+ 5 8 |
+38.46 % |
+44 | 6 | 13 |
+
+
+ walkmap.edge | 136 60 |
+69.39 % |
+ 35 1 14 |
+72.00 % |
+82 | 7 | 50 |
+
+
+ walkmap.geometry | 2 121 |
+1.63 % |
+ 2 11 |
+15.38 % |
+24 | 3 | 13 |
+
+
+ walkmap.path | 3 51 |
+5.56 % |
+ 3 10 |
+23.08 % |
+30 | 4 | 13 |
+
+
+ walkmap.polygon | 2 25 |
+7.41 % |
+ 2 6 |
+25.00 % |
+17 | 3 | 8 |
+
+
+ walkmap.stl | 46 221 |
+17.23 % |
+ 18 46 |
+28.13 % |
+126 | 12 | 64 |
+
+
+ walkmap.svg | 4 135 |
+2.88 % |
+ 3 32 |
+8.57 % |
+50 | 2 | 35 |
+
+
+ walkmap.vertex | 62 23 |
+72.94 % |
+ 14 3 5 |
+77.27 % |
+43 | 5 | 22 |
+
+Totals: |
+ |
+27.77 % |
+ |
+39.45 % |
+
+
+
+
diff --git a/docs/cloverage/walkmap/core.clj.html b/docs/cloverage/walkmap/core.clj.html
new file mode 100644
index 0000000..58adc64
--- /dev/null
+++ b/docs/cloverage/walkmap/core.clj.html
@@ -0,0 +1,140 @@
+
+
+
+ walkmap/core.clj
+
+
+
+ 001 (ns walkmap.core
+
+
+ 002 "At this stage, primarily utility functions dealing with stereolithography
+
+
+ 003 (STL) files. Not a stable API yet!"
+
+
+ 004 (:require [clojure.java.io :as io :refer [file output-stream input-stream]]
+
+
+ 005 [clojure.string :as s]
+
+
+ 006 [hiccup.core :refer [html]]
+
+
+ 007 [me.raynes.fs :as fs]
+
+
+ 008 [taoensso.timbre :as l :refer [info error spy]]
+
+
+ 009 [walkmap.stl :refer [decode-binary-stl]]
+
+
+ 010 [walkmap.svg :refer [stl->svg]]))
+
+
+ 011
+
+
+ 012 (def ^:dynamic *sea-level*
+
+
+ 013 "The sea level on heightmaps we're currently handling. If characters are to
+
+
+ 014 be able to swin in the sea, we must model the sea bottom, so we need
+
+
+ 015 heightmaps which cover at least the continental shelf. However, the sea
+
+
+ 016 bottom is not walkable territory and can be culled from walkmaps.
+
+
+ 017
+
+
+ 018 **Note** must be a floating point number. `(= 0 0.0)` returns `false`!"
+
+
+ 019 0.0)
+
+
+ 020
+
+
+ 021 (defn ocean?
+
+
+ 022 "Of a `facet`, is the altitude of every vertice equal to `*sea-level*`?"
+
+
+ 023 [facet]
+
+
+ 024 (every?
+
+
+ 025 #(= % *sea-level*)
+
+
+ 026 (map :z (:vertices facet))))
+
+
+ 027
+
+
+ 028 (defn cull-ocean-facets
+
+
+ 029 "Ye cannae walk on water. Remove all facets from this `stl` structure which
+
+
+ 030 are at sea level."
+
+
+ 031 [stl]
+
+
+ 032 (assoc stl :facets (remove ocean? (:facets stl))))
+
+
+ 033
+
+
+ 034 (defn binary-stl-file->svg
+
+
+ 035 "Given only an `in-filename`, parse the indicated file, expected to be
+
+
+ 036 binary STL, and return an equivalent SVG structure. Given both `in-filename`
+
+
+ 037 and `out-filename`, as side-effect write the SVG to the indicated output file."
+
+
+ 038 ([in-filename]
+
+
+ 039 (stl->svg (cull-ocean-facets (decode-binary-stl in-filename))))
+
+
+ 040 ([in-filename out-filename]
+
+
+ 041 (let [s (binary-stl-file->svg in-filename)]
+
+
+ 042 (spit out-filename (html s))
+
+
+ 043 s)))
+
+
+ 044
+
+
+
diff --git a/docs/cloverage/walkmap/edge.clj.html b/docs/cloverage/walkmap/edge.clj.html
new file mode 100644
index 0000000..280afe2
--- /dev/null
+++ b/docs/cloverage/walkmap/edge.clj.html
@@ -0,0 +1,254 @@
+
+
+
+ walkmap/edge.clj
+
+
+
+ 001 (ns walkmap.edge
+
+
+ 002 "Essentially the specification for things we shall consider to be an edge.
+
+
+ 003 An edge is a line segment having just a start and an end, with no intervening
+
+
+ 004 nodes."
+
+
+ 005 (:require [clojure.math.numeric-tower :as m]
+
+
+ 006 [walkmap.path :refer [path? polygon->path]]
+
+
+ 007 [walkmap.polygon :refer [polygon?]]
+
+
+ 008 [walkmap.vertex :refer [ensure3d vertex?]]))
+
+
+ 009
+
+
+ 010 (defn edge?
+
+
+ 011 "True if `o` satisfies the conditions for a path. A path shall be a map
+
+
+ 012 having the keys `:start` and `:end`, such that the values of each of those
+
+
+ 013 keys shall be a vertex."
+
+
+ 014 [o]
+
+
+ 015 (and
+
+
+ 016 (map? o)
+
+
+ 017 (vertex? (:start o))
+
+
+ 018 (vertex? (:end o))))
+
+
+ 019
+
+
+ 020 (defn path->edges
+
+
+ 021 "if `o` is a path, a polygon, or a sequence of vertices, return a sequence of
+
+
+ 022 edges representing that path, polygon or sequence."
+
+
+ 023 [o]
+
+
+ 024 (cond
+
+
+ 025 (seq? o)
+
+
+ 026 (when
+
+
+ 027 (and
+
+
+ 028 (vertex? (first o))
+
+
+ 029 (vertex? (first (rest o))))
+
+
+ 030 (cons
+
+
+ 031 {:start (first o)
+
+
+ 032 :end (first (rest o))}
+
+
+ 033 (path->edges (rest o))))
+
+
+ 034 (path? o)
+
+
+ 035 (path->edges (:nodes o))
+
+
+ 036 (polygon? o)
+
+
+ 037 (path->edges (polygon->path o))))
+
+
+ 038
+
+
+ 039 (defn length
+
+
+ 040 "Return the length of the edge `e`."
+
+
+ 041 [e]
+
+
+ 042 (let [start (ensure3d (:start e))
+
+
+ 043 end (ensure3d (:end e))]
+
+
+ 044 (m/sqrt
+
+
+ 045 (reduce
+
+
+ 046 +
+
+
+ 047 (map
+
+
+ 048 #(m/expt (- (% end) (% start)) 2)
+
+
+ 049 [:x :y :z])))))
+
+
+ 050
+
+
+ 051 (defn unit-vector
+
+
+ 052 "Return an vertex parallel to `e` starting from the coordinate origin. Two
+
+
+ 053 edges which are parallel will have the same unit vector."
+
+
+ 054 [e]
+
+
+ 055 (let [e' {:start (ensure3d (:start e)) :end (ensure3d (:end e))}
+
+
+ 056 l (length e')]
+
+
+ 057 (reduce
+
+
+ 058 merge
+
+
+ 059 {}
+
+
+ 060 (map
+
+
+ 061 (fn [k]
+
+
+ 062 {k (/ (- (k (:end e')) (k (:start e'))) l)})
+
+
+ 063 [:x :y :z]))))
+
+
+ 064
+
+
+ 065 (defn parallel?
+
+
+ 066 "True if all `edges` passed are parallel with one another."
+
+
+ 067 ;; TODO: this bears being wary about, dealing with floating point arithmetic.
+
+
+ 068 ;; Keep an eye out for spurious errors.
+
+
+ 069 [& edges]
+
+
+ 070 (let [uvs (map unit-vector edges)]
+
+
+ 071 (every?
+
+
+ 072 #(= % (first uvs))
+
+
+ 073 (rest uvs))))
+
+
+ 074
+
+
+ 075 (defn collinear?
+
+
+ 076 "True if edges `e1` and `e2` are collinear with one another."
+
+
+ 077 [e1 e2]
+
+
+ 078 (parallel?
+
+
+ 079 e1
+
+
+ 080 e2
+
+
+ 081 {:start (:start e1) :end (:start e2)}))
+
+
+ 082
+
+
+
diff --git a/docs/cloverage/walkmap/geometry.clj.html b/docs/cloverage/walkmap/geometry.clj.html
new file mode 100644
index 0000000..c43eb85
--- /dev/null
+++ b/docs/cloverage/walkmap/geometry.clj.html
@@ -0,0 +1,80 @@
+
+
+
+ walkmap/geometry.clj
+
+
+
+ 001 (ns walkmap.geometry
+
+
+ 002 (:require [clojure.math.combinatorics :as combo]
+
+
+ 003 [clojure.math.numeric-tower :as m]
+
+
+ 004 [walkmap.edge :as e]
+
+
+ 005 [walkmap.path :refer [path? polygon->path]]
+
+
+ 006 [walkmap.polygon :refer [polygon?]]
+
+
+ 007 [walkmap.vertex :as v]))
+
+
+ 008
+
+
+ 009 (defn on?
+
+
+ 010 "True if the vertex `v` is on the edge `e`."
+
+
+ 011 [e v]
+
+
+ 012 (let [p (v/ensure3d (:start e))
+
+
+ 013 q (v/ensure3d v)
+
+
+ 014 r (v/ensure3d (:end e))]
+
+
+ 015 (and
+
+
+ 016 (e/collinear? p q r)
+
+
+ 017 (<= (:x q) (max (:x p) (:x r)))
+
+
+ 018 (>= (:x q) (min (:x p) (:x r)))
+
+
+ 019 (<= (:y q) (max (:y p) (:y r)))
+
+
+ 020 (>= (:y q) (min (:y p) (:y r)))
+
+
+ 021 (<= (:z q) (max (:z p) (:z r)))
+
+
+ 022 (>= (:z q) (min (:z p) (:z r))))))
+
+
+ 023
+
+
+ 024
+
+
+
diff --git a/docs/cloverage/walkmap/path.clj.html b/docs/cloverage/walkmap/path.clj.html
new file mode 100644
index 0000000..6b58a3d
--- /dev/null
+++ b/docs/cloverage/walkmap/path.clj.html
@@ -0,0 +1,98 @@
+
+
+
+ walkmap/path.clj
+
+
+
+ 001 (ns walkmap.path
+
+
+ 002 "Essentially the specification for things we shall consider to be path."
+
+
+ 003 (:require [walkmap.polygon :refer [polygon?]]
+
+
+ 004 [walkmap.vertex :refer [vertex?]]))
+
+
+ 005
+
+
+ 006 (defn path?
+
+
+ 007 "True if `o` satisfies the conditions for a path. A path shall be a map
+
+
+ 008 having the key `:nodes`, whose value shall be a sequence of vertices as
+
+
+ 009 defined in `walkmap.vertex`."
+
+
+ 010 [o]
+
+
+ 011 (let
+
+
+ 012 [v (:nodes o)]
+
+
+ 013 (and
+
+
+ 014 (seq? v)
+
+
+ 015 (> (count v) 2)
+
+
+ 016 (every? vertex? v))))
+
+
+ 017
+
+
+ 018 (defn polygon->path
+
+
+ 019 "If `o` is a polygon, return an equivalent path. What's different about
+
+
+ 020 a path is that in polygons there is an implicit edge between the first
+
+
+ 021 vertex and the last. In paths, there isn't, so we need to add that
+
+
+ 022 edge explicitly.
+
+
+ 023
+
+
+ 024 If `o` is not a polygon, will throw an exception."
+
+
+ 025 [o]
+
+
+ 026 (if
+
+
+ 027 (polygon? o)
+
+
+ 028 (assoc (dissoc o :vertices) :nodes (concat (:vertices o) (list (first (:vertices o)))))
+
+
+ 029 (throw (Exception. "Not a polygon!"))))
+
+
+ 030
+
+
+
diff --git a/docs/cloverage/walkmap/polygon.clj.html b/docs/cloverage/walkmap/polygon.clj.html
new file mode 100644
index 0000000..f8ebc8b
--- /dev/null
+++ b/docs/cloverage/walkmap/polygon.clj.html
@@ -0,0 +1,59 @@
+
+
+
+ walkmap/polygon.clj
+
+
+
+ 001 (ns walkmap.polygon
+
+
+ 002 "Essentially the specification for things we shall consider to be polygons."
+
+
+ 003 (:require [walkmap.vertex :refer [vertex?]]))
+
+
+ 004
+
+
+ 005 (defn polygon?
+
+
+ 006 "True if `o` satisfies the conditions for a polygon. A polygon shall be a
+
+
+ 007 map which has a value for the key `:vertices`, where that value is a sequence
+
+
+ 008 of vertices."
+
+
+ 009 [o]
+
+
+ 010 (let
+
+
+ 011 [v (:vertices o)]
+
+
+ 012 (and
+
+
+ 013 (seq? v)
+
+
+ 014 (> (count v) 2)
+
+
+ 015 (every? vertex? v))))
+
+
+ 016
+
+
+ 017
+
+
+
diff --git a/docs/cloverage/walkmap/stl.clj.html b/docs/cloverage/walkmap/stl.clj.html
new file mode 100644
index 0000000..8e48e58
--- /dev/null
+++ b/docs/cloverage/walkmap/stl.clj.html
@@ -0,0 +1,386 @@
+
+
+
+ walkmap/stl.clj
+
+
+
+ 001 (ns walkmap.stl
+
+
+ 002 "Utility functions dealing with stereolithography (STL) files. Not a stable API yet!"
+
+
+ 003 (:require [clojure.java.io :as io :refer [file output-stream input-stream]]
+
+
+ 004 [clojure.string :as s]
+
+
+ 005 [me.raynes.fs :as fs]
+
+
+ 006 [org.clojars.smee.binary.core :as b]
+
+
+ 007 [taoensso.timbre :as l :refer [info error spy]]
+
+
+ 008 [walkmap.polygon :refer [polygon?]])
+
+
+ 009 (:import org.clojars.smee.binary.core.BinaryIO
+
+
+ 010 java.io.DataInput))
+
+
+ 011
+
+
+ 012 (defn stl?
+
+
+ 013 "True if `o` is recogniseable as an STL structure. An STL structure must
+
+
+ 014 have a key `:facets`, whose value must be a sequence of polygons; and
+
+
+ 015 may have a key `:header` whose value should be a string, and/or a key
+
+
+ 016 `:count`, whose value should be a positive integer.
+
+
+ 017
+
+
+ 018 If `verify-count?` is passed and is not `false`, verify that the value of
+
+
+ 019 the `:count` header is equal to the number of facets."
+
+
+ 020 ([o]
+
+
+ 021 (stl? o false))
+
+
+ 022 ([o verify-count?]
+
+
+ 023 (and
+
+
+ 024 (map? o)
+
+
+ 025 (:facets o)
+
+
+ 026 (every? polygon? (:facets o))
+
+
+ 027 (if (:header o) (string? (:header o)) true)
+
+
+ 028 (if (:count o) (integer? (:count o)) true)
+
+
+ 029 (if verify-count? (= (:count o) (count (:facets o))) true))))
+
+
+ 030
+
+
+ 031 (def vect
+
+
+ 032 "A codec for vectors within a binary STL file."
+
+
+ 033 (b/ordered-map
+
+
+ 034 :x :float-le
+
+
+ 035 :y :float-le
+
+
+ 036 :z :float-le))
+
+
+ 037
+
+
+ 038 (def facet
+
+
+ 039 "A codec for a facet (triangle) within a binary STL file."
+
+
+ 040 (b/ordered-map
+
+
+ 041 :normal vect
+
+
+ 042 :vertices [vect vect vect]
+
+
+ 043 :abc :ushort-le))
+
+
+ 044
+
+
+ 045 (def binary-stl
+
+
+ 046 "A codec for binary STL files"
+
+
+ 047 (b/ordered-map
+
+
+ 048 :header (b/string "ISO-8859-1" :length 80) ;; for the time being we neither know nor care what's in this.
+
+
+ 049 :count :uint-le
+
+
+ 050 :facets (b/repeated facet)))
+
+
+ 051
+
+
+ 052 (defn decode-binary-stl
+
+
+ 053 "Parse a binary STL file from this `filename` and return an STL structure
+
+
+ 054 representing its contents.
+
+
+ 055
+
+
+ 056 **NOTE** that we've no way of verifying that the input file is binary STL
+
+
+ 057 data, if it is not this will run but will return garbage."
+
+
+ 058 [filename]
+
+
+ 059 (let [in (io/input-stream filename)]
+
+
+ 060 (b/decode binary-stl in)))
+
+
+ 061
+
+
+ 062 (defn- vect->str [prefix v]
+
+
+ 063 (str prefix " " (:x v) " " (:y v) " " (:z v) "\n"))
+
+
+ 064
+
+
+ 065 (defn- facet2str [tri]
+
+
+ 066 (str
+
+
+ 067 (vect->str "facet normal" (:normal tri))
+
+
+ 068 "outer loop\n"
+
+
+ 069 (apply str
+
+
+ 070 (map
+
+
+ 071 #(vect->str "vertex" %)
+
+
+ 072 (:vertices tri)))
+
+
+ 073 "endloop\nendfacet\n"))
+
+
+ 074
+
+
+ 075 (defn stl->ascii
+
+
+ 076 "Return as a string an ASCII rendering of the `stl` structure."
+
+
+ 077 ([stl]
+
+
+ 078 (stl->ascii stl "unknown"))
+
+
+ 079 ([stl solidname]
+
+
+ 080 (str
+
+
+ 081 "solid "
+
+
+ 082 solidname
+
+
+ 083 (s/trim (:header stl))
+
+
+ 084 "\n"
+
+
+ 085 (apply
+
+
+ 086 str
+
+
+ 087 (map
+
+
+ 088 facet2str
+
+
+ 089 (:facets stl)))
+
+
+ 090 "endsolid "
+
+
+ 091 solidname
+
+
+ 092 "\n")))
+
+
+ 093
+
+
+ 094 (defn write-ascii-stl
+
+
+ 095 "Write an `stl` structure as read by `decode-binary-stl` to this
+
+
+ 096 `filename` as ASCII encoded STL."
+
+
+ 097 ([filename stl]
+
+
+ 098 (let [b (fs/base-name filename true)]
+
+
+ 099 (write-ascii-stl
+
+
+ 100 filename stl
+
+
+ 101 (subs b 0 (or (s/index-of b ".") (count b))))))
+
+
+ 102 ([filename stl solidname]
+
+
+ 103 (l/debug "Solid name is " solidname)
+
+
+ 104 (spit
+
+
+ 105 filename
+
+
+ 106 (stl->ascii stl solidname))))
+
+
+ 107
+
+
+ 108 (defn binary-stl-to-ascii
+
+
+ 109 "Convert the binary STL file indicated by `in-filename`, and write it to
+
+
+ 110 `out-filename`, if specified; otherwise, to a file with the same basename
+
+
+ 111 as `in-filename` but the extension `.ascii.stl`."
+
+
+ 112 ([in-filename]
+
+
+ 113 (let [[_ ext] (fs/split-ext in-filename)]
+
+
+ 114 (binary-stl-to-ascii
+
+
+ 115 in-filename
+
+
+ 116 (str
+
+
+ 117 (subs
+
+
+ 118 in-filename
+
+
+ 119 0
+
+
+ 120 (or
+
+
+ 121 (s/last-index-of in-filename ".")
+
+
+ 122 (count in-filename)))
+
+
+ 123 ".ascii"
+
+
+ 124 ext))))
+
+
+ 125 ([in-filename out-filename]
+
+
+ 126 (write-ascii-stl out-filename (decode-binary-stl in-filename))))
+
+
+
diff --git a/docs/cloverage/walkmap/svg.clj.html b/docs/cloverage/walkmap/svg.clj.html
new file mode 100644
index 0000000..d465adf
--- /dev/null
+++ b/docs/cloverage/walkmap/svg.clj.html
@@ -0,0 +1,158 @@
+
+
+
+ walkmap/svg.clj
+
+
+
+ 001 (ns walkmap.svg
+
+
+ 002 "Utility functions for writing stereolithography (STL) files (and possibly,
+
+
+ 003 later, other geometry files of interest to us) as scalable vector graphics
+
+
+ 004 (SVG)."
+
+
+ 005 (:require [clojure.string :as s]
+
+
+ 006 [taoensso.timbre :as l :refer [info error spy]]
+
+
+ 007 [walkmap.polygon :refer [polygon?]]
+
+
+ 008 [walkmap.vertex :refer [vertex?]]))
+
+
+ 009
+
+
+ 010 (defn- facet->svg-poly
+
+
+ 011 [facet]
+
+
+ 012 [:polygon
+
+
+ 013 {:points (s/join " " (map #(str (:x %) "," (:y %)) (:vertices facet)))}])
+
+
+ 014
+
+
+ 015 (defn stl->svg
+
+
+ 016 "Convert this in-memory `stl` structure, as read by `decode-binary-stl`, into
+
+
+ 017 an in-memory hiccup representation of SVG structure, and return it."
+
+
+ 018 [stl]
+
+
+ 019 (let [minx (reduce
+
+
+ 020 min
+
+
+ 021 (map
+
+
+ 022 #(reduce min (map :x (:vertices %)))
+
+
+ 023 (:facets stl)))
+
+
+ 024 maxx (reduce
+
+
+ 025 max
+
+
+ 026 (map
+
+
+ 027 #(reduce max (map :x (:vertices %)))
+
+
+ 028 (:facets stl)))
+
+
+ 029 miny (reduce
+
+
+ 030 min
+
+
+ 031 (map
+
+
+ 032 #(reduce min (map :y (:vertices %)))
+
+
+ 033 (:facets stl)))
+
+
+ 034 maxy (reduce
+
+
+ 035 max
+
+
+ 036 (map
+
+
+ 037 #(reduce max (map :y (:vertices %)))
+
+
+ 038 (:facets stl)))]
+
+
+ 039 [:svg
+
+
+ 040 {:xmlns "http://www.w3.org/2000/svg"
+
+
+ 041 :version "1.2"
+
+
+ 042 :width (- maxx minx)
+
+
+ 043 :height (- maxy miny)
+
+
+ 044 :viewBox (s/join " " (map str [minx miny maxx maxy]))}
+
+
+ 045 (vec
+
+
+ 046 (cons
+
+
+ 047 :g
+
+
+ 048 (map
+
+
+ 049 facet->svg-poly
+
+
+ 050 (:facets stl))))]))
+
+
+
diff --git a/docs/cloverage/walkmap/vertex.clj.html b/docs/cloverage/walkmap/vertex.clj.html
new file mode 100644
index 0000000..382bc40
--- /dev/null
+++ b/docs/cloverage/walkmap/vertex.clj.html
@@ -0,0 +1,137 @@
+
+
+
+ walkmap/vertex.clj
+
+
+
+ 001 (ns walkmap.vertex
+
+
+ 002 "Essentially the specification for things we shall consider to be vertices.")
+
+
+ 003
+
+
+ 004 (defn vertex?
+
+
+ 005 "True if `o` satisfies the conditions for a vertex. That is, essentially,
+
+
+ 006 that it must rerpresent a two- or three- dimensional vector. A vertex is
+
+
+ 007 shall be a map having at least the keys `:x` and `:y`, where the value of
+
+
+ 008 those keys is a number. If the key `:z` is also present, its value must also
+
+
+ 009 be a number.
+
+
+ 010
+
+
+ 011 The name `vector?` was not used as that would clash with a function of that
+
+
+ 012 name in `clojure.core` whose semantics are entirely different."
+
+
+ 013 [o]
+
+
+ 014 (and
+
+
+ 015 (map? o)
+
+
+ 016 (number? (:x o))
+
+
+ 017 (number? (:y o))
+
+
+ 018 (or (nil? (:z o)) (number? (:z o)))))
+
+
+ 019
+
+
+ 020 (def ensure3d
+
+
+ 021 "Given a vertex `o`, if `o` has a `:z` value, just return `o`; otherwise
+
+
+ 022 return a vertex like `o` but having thie `dflt` value as the value of its
+
+
+ 023 `:z` key, or zero as the value of its `:z` key if `dflt` is not specified.
+
+
+ 024
+
+
+ 025 If `o` is not a vertex, throws an exception."
+
+
+ 026 (memoize
+
+
+ 027 (fn
+
+
+ 028 ([o]
+
+
+ 029 (ensure3d o 0.0))
+
+
+ 030 ([o dflt]
+
+
+ 031 (cond
+
+
+ 032 (not (vertex? o)) (throw (Exception. "Not a vertex!"))
+
+
+ 033 (:z o) o
+
+
+ 034 :else (assoc o :z dflt))))))
+
+
+ 035
+
+
+ 036 (def ensure2d
+
+
+ 037 "If `o` is a vertex, set its `:z` value to zero; else throw an exception."
+
+
+ 038 (memoize
+
+
+ 039 (fn [o]
+
+
+ 040 (if
+
+
+ 041 (vertex? o)
+
+
+ 042 (assoc o :z 0.0)
+
+
+ 043 (throw (Exception. "Not a vertex!"))))))
+
+
+
diff --git a/src/walkmap/edge.clj b/src/walkmap/edge.clj
index b77547d..9f50281 100644
--- a/src/walkmap/edge.clj
+++ b/src/walkmap/edge.clj
@@ -2,9 +2,10 @@
"Essentially the specification for things we shall consider to be an edge.
An edge is a line segment having just a start and an end, with no intervening
nodes."
- (:require [walkmap.path :refer [path? polygon->path]]
+ (:require [clojure.math.numeric-tower :as m]
+ [walkmap.path :refer [path? polygon->path]]
[walkmap.polygon :refer [polygon?]]
- [walkmap.vertex :refer [vertex?]]))
+ [walkmap.vertex :refer [ensure3d vertex?]]))
(defn edge?
"True if `o` satisfies the conditions for a path. A path shall be a map
@@ -35,3 +36,47 @@
(polygon? o)
(path->edges (polygon->path o))))
+(defn length
+ "Return the length of the edge `e`."
+ [e]
+ (let [start (ensure3d (:start e))
+ end (ensure3d (:end e))]
+ (m/sqrt
+ (reduce
+ +
+ (map
+ #(m/expt (- (% end) (% start)) 2)
+ [:x :y :z])))))
+
+(defn unit-vector
+ "Return an vertex parallel to `e` starting from the coordinate origin. Two
+ edges which are parallel will have the same unit vector."
+ [e]
+ (let [e' {:start (ensure3d (:start e)) :end (ensure3d (:end e))}
+ l (length e')]
+ (reduce
+ merge
+ {}
+ (map
+ (fn [k]
+ {k (/ (- (k (:end e')) (k (:start e'))) l)})
+ [:x :y :z]))))
+
+(defn parallel?
+ "True if all `edges` passed are parallel with one another."
+ ;; TODO: this bears being wary about, dealing with floating point arithmetic.
+ ;; Keep an eye out for spurious errors.
+ [& edges]
+ (let [uvs (map unit-vector edges)]
+ (every?
+ #(= % (first uvs))
+ (rest uvs))))
+
+(defn collinear?
+ "True if edges `e1` and `e2` are collinear with one another."
+ [e1 e2]
+ (parallel?
+ e1
+ e2
+ {:start (:start e1) :end (:start e2)}))
+
diff --git a/src/walkmap/geometry.clj b/src/walkmap/geometry.clj
index 61baf32..f152298 100644
--- a/src/walkmap/geometry.clj
+++ b/src/walkmap/geometry.clj
@@ -1,52 +1,19 @@
(ns walkmap.geometry
(:require [clojure.math.combinatorics :as combo]
[clojure.math.numeric-tower :as m]
- [walkmap.edge :as edge]
+ [walkmap.edge :as e]
[walkmap.path :refer [path? polygon->path]]
[walkmap.polygon :refer [polygon?]]
- [walkmap.vertex :as vertex]))
-
-(defn collinear?
- "True if these vertices `v1`, `v2`, `v3` are colinear; false otherwise."
- ;; This is failing...
- ;; see http://www.ambrsoft.com/TrigoCalc/Line3D/LineColinear.htm
- [v1 v2 v3]
- (let [a (m/sqrt (+ (- (:x v2) (:x v1)) (- (:y v2) (:y v1)) (- (:z v2) (:z v1))))
- b (m/sqrt (+ (- (:x v3) (:x v1)) (- (:y v3) (:y v1)) (- (:z v3) (:z v1))))
- c (m/sqrt (+ (- (:x v3) (:x v2)) (- (:y v3) (:y v2)) (- (:z v3) (:z v2))))]
- (not
- (and
- (> (+ a b) c)
- (> (+ a c) b)
- (> (+ b c) a)))))
-
-;; (collinear? {:x 0 :y 0 :z 0} {:x 1 :y 1 :z 1} {:x 7 :y 7 :z 7})
-;; (collinear? {:x 0 :y 0 :z 0} {:x 1 :y 2 :z 1} {:x 7 :y 7 :z 7})
-;; (collinear? {:x 0 :y 0 :z 0} {:x 0 :y 2 :z 0} {:x 0 :y 3 :z 0})
-
-;; (def v1 {:x 0 :y 0 :z 0})
-;; (def v2 {:x 0 :y 2 :z 0})
-;; (def v3 {:x 0 :y 7 :z 0})
-
-;; (def a (m/sqrt (+ (- (:x v2) (:x v1)) (- (:y v2) (:y v1)) (- (:z v2) (:z v1)))))
-;; a
-;; (def b (m/sqrt (+ (- (:x v3) (:x v1)) (- (:y v3) (:y v1)) (- (:z v3) (:z v1)))))
-;; b
-;; (def c (m/sqrt (+ (- (:x v3) (:x v2)) (- (:y v3) (:y v2)) (- (:z v3) (:z v2)))))
-;; c
-
-;; (> (+ b c) a)
-;; (> (+ a c) b)
-;; (> (+ a c) c)
+ [walkmap.vertex :as v]))
(defn on?
"True if the vertex `v` is on the edge `e`."
[e v]
- (let [p (vertex/ensure3d (:start e))
- q (vertex/ensure3d v)
- r (vertex/ensure3d (:end e))]
+ (let [p (v/ensure3d (:start e))
+ q (v/ensure3d v)
+ r (v/ensure3d (:end e))]
(and
- (collinear? p q r)
+ (e/collinear? p q r)
(<= (:x q) (max (:x p) (:x r)))
(>= (:x q) (min (:x p) (:x r)))
(<= (:y q) (max (:y p) (:y r)))
diff --git a/src/walkmap/vertex.clj b/src/walkmap/vertex.clj
index 78064ee..c62d1ee 100644
--- a/src/walkmap/vertex.clj
+++ b/src/walkmap/vertex.clj
@@ -17,24 +17,27 @@
(number? (:y o))
(or (nil? (:z o)) (number? (:z o)))))
-(defn ensure3d
+(def ensure3d
"Given a vertex `o`, if `o` has a `:z` value, just return `o`; otherwise
return a vertex like `o` but having thie `dflt` value as the value of its
`:z` key, or zero as the value of its `:z` key if `dflt` is not specified.
If `o` is not a vertex, throws an exception."
- ([o]
- (ensure3d o 0.0))
- ([o dflt]
- (cond
- (not (vertex? o)) (throw (Exception. "Not a vertex!"))
- (:z o) o
- :else (assoc o :z dflt))))
+ (memoize
+ (fn
+ ([o]
+ (ensure3d o 0.0))
+ ([o dflt]
+ (cond
+ (not (vertex? o)) (throw (Exception. "Not a vertex!"))
+ (:z o) o
+ :else (assoc o :z dflt))))))
-(defn ensure2d
+(def ensure2d
"If `o` is a vertex, set its `:z` value to zero; else throw an exception."
- [o]
- (if
- (vertex? o)
- (assoc o :z 0.0)
- (throw (Exception. "Not a vertex!"))))
+ (memoize
+ (fn [o]
+ (if
+ (vertex? o)
+ (assoc o :z 0.0)
+ (throw (Exception. "Not a vertex!"))))))
diff --git a/test/walkmap/core_test.clj b/test/walkmap/core_test.clj
index 2f97186..b84b538 100644
--- a/test/walkmap/core_test.clj
+++ b/test/walkmap/core_test.clj
@@ -2,6 +2,6 @@
(:require [clojure.test :refer :all]
[walkmap.core :refer :all]))
-(deftest a-test
- (testing "FIXME, I fail."
- (is (= 0 1))))
+;; (deftest a-test
+;; (testing "FIXME, I fail."
+;; (is (= 0 1))))
diff --git a/test/walkmap/edge_test.clj b/test/walkmap/edge_test.clj
new file mode 100644
index 0000000..b919623
--- /dev/null
+++ b/test/walkmap/edge_test.clj
@@ -0,0 +1,46 @@
+(ns walkmap.edge-test
+ (:require [clojure.test :refer :all]
+ [walkmap.edge :refer :all]))
+
+(deftest edge-test
+ (testing "identification of edges."
+ (is (edge? {:start {:x 0.0 :y 0.0 :z 0.0} :end {:x 3 :y 4 :z 0.0}}) "It is.")
+ (is (not (edge? {:start {:y 0.0 :z 0.0} :end {:x 3 :y 4 :z 0.0}})) "Start lacks :x key")
+ (is (not (edge? {:start {:x nil :y 0.0 :z 0.0} :end {:x 3 :y 4 :z 0.0}})) "Start lacks :x value")
+ (is (not (edge? {:begin {:x nil :y 0.0 :z 0.0} :end {:x 3 :y 4 :z 0.0}})) "Lacks start key")
+ (is (not (edge? {:start {:x nil :y 0.0 :z 0.0} :finish {:x 3 :y 4 :z 0.0}})) "Lacks end key")
+ (is (not (edge? {:start {:x "zero" :y 0.0 :z 0.0} :end {:x 3 :y 4 :z 0.0}})) "Value of x in start is not a number")
+ ))
+
+(deftest length-test
+ (testing "length of an edge"
+ (is (= (length {:start {:x 0.0 :y 0.0 :z 0.0} :end {:x 3.0 :y 4.0 :z 0.0}}) 5.0))))
+
+(deftest unit-vector-test
+ (testing "deriving the unit vector"
+ (is (=
+ (unit-vector {:start {:x 0.0 :y 0.0 :z 0.0} :end {:x 3 :y 4 :z 0.0}})
+ {:x 0.6, :y 0.8, :z 0.0}))
+ (is (=
+ (unit-vector {:start {:x 1.0 :y 2.0 :z 3.5} :end {:x 4.0 :y 6.0 :z 3.5}})
+ {:x 0.6, :y 0.8, :z 0.0}))))
+
+(deftest parallel-test
+ (testing "parallelism"
+ (is (parallel? {:start {:x 0.0 :y 0.0 :z 0.0} :end {:x 3 :y 4 :z 0.0}}
+ {:start {:x 1.0 :y 2.0 :z 3.5} :end {:x 4.0 :y 6.0 :z 3.5}})
+ "Should be")
+ (is (not
+ (parallel? {:start {:x 0.0 :y 0.0 :z 0.0} :end {:x 3 :y 4 :z 0.0}}
+ {:start {:x 1.0 :y 2.0 :z 3.5} :end {:x 4.0 :y 6.0 :z 3.49}}))
+ "Should not be!")))
+
+(deftest collinear-test
+ (testing "collinearity"
+ (is (collinear? {:start {:x 0.0 :y 0.0 :z 0.0} :end {:x 3.0 :y 4.0 :z 0.0}}
+ {:start {:x 3.0 :y 4.0 :z 0.0} :end {:x 9.0 :y 12.0 :z 0.0}})
+ "Should be")
+ (is (not
+ (collinear? {:start {:x 0.0 :y 0.0 :z 0.0} :end {:x 3 :y 4 :z 0.0}}
+ {:start {:x 1.0 :y 2.0 :z 3.5} :end {:x 4.0 :y 6.0 :z 3.5}}))
+ "Should not be!")))