#3 Inching forward, positively

This commit is contained in:
Simon Brooke 2020-05-26 23:30:07 +01:00
parent 9ee365b987
commit 79174af2c1
No known key found for this signature in database
GPG key ID: A7A4F18D1D4DF987
30 changed files with 766 additions and 410 deletions

View file

@ -3,10 +3,17 @@
An edge is a line segment having just a start and an end, with no intervening
nodes."
(:require [clojure.math.numeric-tower :as m]
[walkmap.path :refer [path? polygon->path]]
[walkmap.polygon :refer [polygon?]]
[walkmap.vertex :refer [ensure3d vertex?]]))
(defn edge
"Return an edge between vertices `v1` and `v2`."
[v1 v2]
(if
(and (vertex? v1) (vertex? v2))
{:kind :edge :id (keyword (gensym "edge")) :start v1 :end v2}
(throw (IllegalArgumentException. "Must be vertices."))))
(defn edge?
"True if `o` satisfies the conditions for a edge. An edge shall be a map
having the keys `:start` and `:end`, such that the values of each of those
@ -17,31 +24,6 @@
(vertex? (:start o))
(vertex? (:end o))))
(defn path->edges
"if `o` is a path, a polygon, or a sequence of vertices, return a sequence of
edges representing that path, polygon or sequence.
Throws `IllegalArgumentException` if `o` is not a path, a polygon, or
sequence of vertices."
[o]
(cond
(seq? o)
(when
(and
(vertex? (first o))
(vertex? (first (rest o))))
(cons
{:start (first o)
:end (first (rest o))}
(path->edges (rest o))))
(path? o)
(path->edges (:nodes o))
(polygon? o)
(path->edges (polygon->path o))
:else
(throw (IllegalArgumentException.
"Not a path, polygon, or sequence of vertices!"))))
(defn length
"Return the length of the edge `e`."
[e]

View file

@ -1,6 +1,9 @@
(ns walkmap.path
"Essentially the specification for things we shall consider to be path."
(:require [walkmap.polygon :refer [polygon?]]
"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 [walkmap.edge :as e]
[walkmap.polygon :refer [polygon?]]
[walkmap.vertex :refer [vertex?]]))
(defn path?
@ -14,14 +17,16 @@
(seq? v)
(> (count v) 2)
(every? vertex? v)
(:id o)
(or (nil? (:kind o)) (= (:kind o) :path)))))
(defn make-path
[nodes]
(defn path
"Return a path constructed from these `vertices`."
[& vertices]
(if
(every? vertex? nodes)
{:nodes nodes :id (keyword (gensym "path")) :kind :path}
(throw (Exception. "Each item on path must be a vertex."))))
(every? vertex? vertices)
{:nodes vertices :id (keyword (gensym "path")) :kind :path}
(throw (IllegalArgumentException. "Each item on path must be a vertex."))))
(defn polygon->path
"If `o` is a polygon, return an equivalent path. What's different about
@ -34,5 +39,40 @@
(if
(polygon? o)
(assoc (dissoc o :vertices) :kind :path :nodes (concat (:vertices o) (list (first (:vertices o)))))
(throw (Exception. "Not a polygon!"))))
(throw (IllegalArgumentException. "Not a polygon!"))))
(defn path->edges
"if `o` is a path, a polygon, or a sequence of vertices, return a sequence of
edges representing that path, polygon or sequence.
Throws `IllegalArgumentException` if `o` is not a path, a polygon, or
sequence of vertices."
[o]
(cond
(seq? o)
(when
(and
(vertex? (first o))
(vertex? (first (rest o))))
(cons
;; TODO: think about: when constructing an edge from a path, should the
;; constructed edge be tagged with the tags of the path?
(e/edge (first o) (rest o))
(path->edges (rest o))))
(path? o)
(path->edges (:nodes o))
:else
(throw (IllegalArgumentException.
"Not a path or sequence of vertices!"))))
(defn length
"Return the length of this path, in metres. **Note that**
1. This is not the same as the distance from the start to the end of the
path, which, except for absolutely straight paths, will be shorter;
2. It is not even quite the same as the length of the path *as rendered*,
since paths will generally be rendered as spline curves."
[path]
(if
(path? path)
(reduce + (map e/length (path->edges path)))
(throw (IllegalArgumentException. "Not a path!"))))

View file

@ -6,7 +6,7 @@
[org.clojars.smee.binary.core :as b]
[taoensso.timbre :as l :refer [info error spy]]
[walkmap.polygon :refer [polygon?]]
[walkmap.vertex :refer [canonicalise-vertex]])
[walkmap.vertex :as v])
(:import org.clojars.smee.binary.core.BinaryIO
java.io.DataInput))
@ -67,7 +67,7 @@
:kind :polygon
:vertices (canonicalise (:vertices o)))
;; if it has a value for :x it's a vertex, but it doesn't yet conform to `vertex?`
(:x o) (canonicalise-vertex o)
(:x o) (v/canonicalise o)
;; shouldn't happen
:else o))

View file

@ -5,7 +5,11 @@
(:require [clojure.set :refer [difference union]]))
(defn tagged?
"True if this `object` is tagged with each of these `tags`."
"True if this `object` is tagged with each of these `tags`. It is an error
(and an exception will be thrown) if
1. `object` is not a map;
2. any of `tags` is not a keyword."
[object & tags]
(if
(map? object)
@ -14,8 +18,7 @@
(let [ot (::tags object)]
(and
(set? ot)
(every? ot tags)
true))
(every? ot tags)))
(throw (IllegalArgumentException.
(str "Must be keyword(s): " (map type tags)))))
(throw (IllegalArgumentException.
@ -23,7 +26,11 @@
(defn tag
"Return an object like this `object` but with these `tags` added to its tags,
if they are not already present."
if they are not already present.It is an error (and an exception will be
thrown) if
1. `object` is not a map;
2. any of `tags` is not a keyword."
[object & tags]
(if
(map? object)
@ -35,9 +42,17 @@
(throw (IllegalArgumentException.
(str "Must be a map: " (type object))))))
(defmacro tags
"Return the tags of this object, if any."
[object]
`(::tags ~object))
(defn untag
"Return an object like this `object` but with these `tags` removed from its
tags, if present."
tags, if present. It is an error (and an exception will be thrown) if
1. `object` is not a map;
2. any of `tags` is not a keyword."
[object & tags]
(if
(map? object)

View file

@ -1,5 +1,8 @@
(ns walkmap.vertex
"Essentially the specification for things we shall consider to be vertices.")
"Essentially the specification for things we shall consider to be vertices.
Note that there's no `distance` function here; to find the distance between
two vertices, create an edge from them and use `walkmap.edge/length`.")
(defn vertex-key
"Making sure we get the same key everytime we key a vertex with the same
@ -29,7 +32,7 @@
(or (nil? (:z o)) (number? (:z o)))
(or (nil? (:kind o)) (= (:kind o) :vertex))))
(defn make-vertex
(defn vertex
"Make a vertex with this `x`, `y` and (if provided) `z` values. Returns a map
with those values, plus a unique `:id` value, and `:kind` set to `:vertex`.
It's not necessary to use this function to create a vertex, but the `:id`
@ -38,9 +41,9 @@
(let [v {:x x :y y :kind :vertex}]
(assoc v :id (vertex-key v))))
([x y z]
(assoc (make-vertex x y) :z z)))
(assoc (vertex x y) :z z)))
(defn canonicalise-vertex
(defn canonicalise
"If `o` is a map with numeric values for `:x`, `:y` and optionally `:z`,
upgrade it to something we will recognise as a vertex."
[o]
@ -51,7 +54,7 @@
(number? (:y o))
(or (nil? (:z o)) (number? (:z o))))
(assoc o :kind :vertex :id (vertex-key o))
(throw (IllegalArgumentException. "Not a vertex."))))
(throw (IllegalArgumentException. "Not a proto-vertex: must have numeric `:x` and `:y`."))))
(def ensure3d
"Given a vertex `o`, if `o` has a `:z` value, just return `o`; otherwise