Compare commits
40 commits
Author | SHA1 | Date | |
---|---|---|---|
|
fd15e41952 | ||
|
4187b52c66 | ||
|
a0b2b93f6b | ||
|
733bc7768d | ||
|
18fbc61d2b | ||
|
cb5041e684 | ||
|
156775fc79 | ||
|
56566a9c1c | ||
|
311edb233b | ||
|
a0cf33ac57 | ||
|
cf0b70e816 | ||
|
4161f4f04a | ||
|
48d9aacb69 | ||
|
99a6c6824a | ||
|
777b8bc077 | ||
|
9687e57666 | ||
|
4b76eb5e4f | ||
|
3ba8033be8 | ||
|
5328e89c96 | ||
|
e07dc7098c | ||
|
c7469051f4 | ||
|
819aa0fbc4 | ||
|
87177051da | ||
|
989a8fe91d | ||
|
75899f8a4d | ||
|
fde35f636f | ||
|
4c5867b390 | ||
|
a0882f7ebd | ||
|
f93432a241 | ||
|
7442673cbf | ||
|
f2c39f9017 | ||
|
9892af65e3 | ||
|
4df5c4dff4 | ||
|
1ab35dbe7d | ||
|
79174af2c1 | ||
|
9ee365b987 | ||
|
f4ca49f11b | ||
|
f616992191 | ||
|
f49a7495db | ||
|
387d817e9b |
21
.gitignore
vendored
21
.gitignore
vendored
|
@ -10,9 +10,30 @@ pom.xml.asc
|
|||
*.svg
|
||||
/.lein-*
|
||||
/.nrepl-port
|
||||
.clj-kondo
|
||||
.lsp
|
||||
.hgignore
|
||||
.hg/
|
||||
|
||||
resources/isle_of_man.svg
|
||||
|
||||
resources/small_hill.svg
|
||||
|
||||
s.edn
|
||||
|
||||
.eastwood
|
||||
|
||||
settlement_1.edn
|
||||
|
||||
small_hill.edn
|
||||
|
||||
small_hill.html
|
||||
|
||||
.calva/output-window/output.calva-repl
|
||||
.classpath
|
||||
.project
|
||||
.settings/org.eclipse.jdt.apt.core.prefs
|
||||
.settings/org.eclipse.jdt.core.prefs
|
||||
.settings/org.eclipse.m2e.core.prefs
|
||||
|
||||
resources/superstructure-raw-20240410.edn
|
||||
|
|
90
README.md
90
README.md
|
@ -8,6 +8,96 @@ This library is written in support of work on
|
|||
[The Great Game](https://simon-brooke.github.io/the-great-game/codox/Pathmaking.html), but is
|
||||
separate because it may be of some use in other settings.
|
||||
|
||||
## Explanation
|
||||
|
||||
*I'm writing this becaue, having returned to this project after four years of
|
||||
being distracted by other things, I couldn't even remember what it was for,
|
||||
let alone how to use it.*
|
||||
|
||||
### 'STL files'
|
||||
|
||||
'STL' is not an acronym but an abbreviation; an abreviation of
|
||||
'[stereolithography](https://en.wikipedia.org/wiki/Stereolithography)'. Even
|
||||
that name is completely misleading, since there is absolutely no sense in
|
||||
which this is printing with stone.
|
||||
|
||||
What an STL file is is in fact a representation of a mesh, originally intended
|
||||
for use in 3d printing, and therefore taken to be the bounding mesh of a solid.
|
||||
This makes it a better format for representing landforms for games than a
|
||||
simple heightmap, since heightmaps cannot represent things like overhangs and
|
||||
caves.
|
||||
|
||||
STL files come in two subformats: 'ASCII' (which this library can currently
|
||||
read and write), and 'Binary' (which this library can read but not yet write).
|
||||
|
||||
STL files are commonly used in game development and can be imported by all of
|
||||
|
||||
* [Blender](https://docs.blender.org/manual/en/latest/files/import_export/stl.html)
|
||||
* [Godot Engine](https://godotengine.org/asset-library/asset/961)
|
||||
* [Unity](https://github.com/karl-/pb_Stl) (unofficial)
|
||||
* [Unreal Engine](https://github.com/rdeioris/UnrealSTL) (unofficial)
|
||||
|
||||
Additionally, a number of programs exist to convert STL files into other 3D
|
||||
model file formats.
|
||||
|
||||
### 'Superstructure'
|
||||
|
||||
I can't remember if 'superstructure' is a name which I found in reading the
|
||||
literature on STL files, or whether it's one I came up with for my own
|
||||
purposes, but essentially in my usage of it:
|
||||
|
||||
1. [MicroWorld](https://github.com/simon-brooke/mw-engine) is a two dimensional
|
||||
cellular automaton, which I wrote, and which can be used (and which I use) to
|
||||
model ecological processes up to and including human settlement;
|
||||
2. As a two dimensional cellular automaton, it is necessarily essentially a
|
||||
flat mesh, but I essentially drape that over a
|
||||
[heightmap](https://github.com/simon-brooke/mw-engine/blob/master/src/mw_engine/heightmap.clj)
|
||||
to give a sort of notional two-and-a-half dimensional representation;
|
||||
3. As such MicroWorld is a moderately useful tool to transform an imagined land
|
||||
form into a vegetated and populated landscape, with biomes and human habitation
|
||||
distributed naturalistically.
|
||||
|
||||
A 'superstructure', then, in the terms I'm using it, is an intermediary
|
||||
representation between a heightmap and a stereolithography model, which carries
|
||||
data not only about shape, but about biomes, settlement, traversibility and so
|
||||
on. Thus it is a surface across which routes can efficiently be planned.
|
||||
|
||||
A superstructure can currently be constructed from a MicroWorld world, read in
|
||||
from an [Extensible Data Notation](https://github.com/edn-format/edn). There
|
||||
isn't currently a function to construct a superstructure from an in-memory
|
||||
MicroWorld world, because MicroWorld, when working with models of the size I'm
|
||||
using, is hugely memory hungry; but there clearly could be and that is an
|
||||
addition you should expect.
|
||||
|
||||
A superstructure cannot currently be constructed directly from a heightmap, but
|
||||
that would also be a trivial enhancement and is planned.
|
||||
|
||||
```clojure
|
||||
(use 'cc.journeyman.walkmap.microworld :reload)
|
||||
(require '[cc.journeyman.walkmap.vertex :refer [vertex]])
|
||||
(load-microworld-edn
|
||||
"../the-great-game/resources/test/galloway-populated-20240407.edn")
|
||||
```
|
||||
or
|
||||
```clojure
|
||||
(use 'cc.journeyman.walkmap.microworld :reload)
|
||||
(require '[cc.journeyman.walkmap.vertex :refer [vertex]])
|
||||
(load-microworld-edn
|
||||
"../the-great-game/resources/test/galloway-populated-20240407.edn" :mw {}
|
||||
(vertex 1000 1000 10))
|
||||
```
|
||||
|
||||
Note that this is a lot of processing and even on a fast machine is going to
|
||||
take significant walkclock time. You cannot reasonably do it, except for
|
||||
utterly trivial times, during game initialisation; instead you need to do it
|
||||
in the 'baking' phase of game development, and consider the STL file as the
|
||||
resoource to be shipped with the game.
|
||||
|
||||
However, although it is time-expensive, it is not, unlike MicroWorld, either
|
||||
memory or processor hungry. This will not blow up your machine.
|
||||
|
||||
A superstructure can be written to an ASCII STL file.
|
||||
|
||||
## Usage
|
||||
|
||||
What works:
|
||||
|
|
BIN
doc/Dali-performance.ods
Normal file
BIN
doc/Dali-performance.ods
Normal file
Binary file not shown.
148
doc/dali-performance.md
Normal file
148
doc/dali-performance.md
Normal file
|
@ -0,0 +1,148 @@
|
|||
# Dali performance
|
||||
|
||||
Notes written while trying to characterise the performance problem in Dali.
|
||||
|
||||
## Hypothesis one: it's the way I format the polygons that's the issue
|
||||
|
||||
Firstly, with both versions of `stl->svg` using the same version of `facet->svg-poly`, i.e. this one:
|
||||
|
||||
(defn- facet->svg-poly
|
||||
[facet]
|
||||
[:polygon
|
||||
{:points (s/join " " (map #(str (:x %) "," (:y %)) (:vertices facet)))}])
|
||||
|
||||
we get this performance using the smaller `isle_of_man` map:
|
||||
|
||||
walkmap.svg=> (def ^:dynamic *preferred-svg-render* :hiccup)
|
||||
#'walkmap.svg/*preferred-svg-render*
|
||||
walkmap.svg=> (time (def hiccup (binary-stl-file->svg "resources/isle_of_man.stl" "resources/isle_of_man.svg")))
|
||||
20-05-25 09:21:43 mason INFO [walkmap.svg:82] - Generating SVG for :hiccup renderer
|
||||
20-05-25 09:21:43 mason INFO [walkmap.svg:96] - Emitting SVG with :hiccup renderer
|
||||
"Elapsed time: 86.904891 msecs"
|
||||
#'walkmap.svg/hiccup
|
||||
walkmap.svg=> (def ^:dynamic *preferred-svg-render* :dali)
|
||||
#'walkmap.svg/*preferred-svg-render*
|
||||
walkmap.svg=> (time (def dali (binary-stl-file->svg "resources/isle_of_man.stl" "resources/isle_of_man.svg")))
|
||||
20-05-25 09:22:17 mason INFO [walkmap.svg:82] - Generating SVG for :dali renderer
|
||||
20-05-25 09:22:17 mason INFO [walkmap.svg:96] - Emitting SVG with :dali renderer
|
||||
"Elapsed time: 890.863814 msecs"
|
||||
#'walkmap.svg/dali
|
||||
|
||||
If we switch the Dali render to use my original version of `facet->svg-poly`, i.e. this one:
|
||||
|
||||
(defn- dali-facet->svg-poly
|
||||
[facet]
|
||||
(vec
|
||||
(cons
|
||||
:polygon
|
||||
(map #(vec (list (:x %) (:y %))) (:vertices facet)))))
|
||||
|
||||
we get this performance:
|
||||
|
||||
walkmap.svg=> (def ^:dynamic *preferred-svg-render* :hiccup)
|
||||
#'walkmap.svg/*preferred-svg-render*
|
||||
walkmap.svg=> (time (def hiccup (binary-stl-file->svg "resources/isle_of_man.stl" "resources/isle_of_man.svg")))
|
||||
20-05-25 09:35:33 mason INFO [walkmap.svg:82] - Generating SVG for :hiccup renderer
|
||||
20-05-25 09:35:33 mason INFO [walkmap.svg:96] - Emitting SVG with :hiccup renderer
|
||||
"Elapsed time: 84.09972 msecs"
|
||||
#'walkmap.svg/hiccup
|
||||
walkmap.svg=> (def ^:dynamic *preferred-svg-render* :dali)
|
||||
#'walkmap.svg/*preferred-svg-render*
|
||||
walkmap.svg=> (time (def dali (binary-stl-file->svg "resources/isle_of_man.stl" "resources/isle_of_man.svg")))
|
||||
20-05-25 09:35:41 mason INFO [walkmap.svg:82] - Generating SVG for :dali renderer
|
||||
20-05-25 09:35:41 mason INFO [walkmap.svg:96] - Emitting SVG with :dali renderer
|
||||
"Elapsed time: 874.292007 msecs"
|
||||
#'walkmap.svg/dali
|
||||
|
||||
No significant difference in performance.
|
||||
|
||||
If we generate but don't render, we get this:
|
||||
|
||||
walkmap.svg=> (def ^:dynamic *preferred-svg-render* :hiccup)
|
||||
#'walkmap.svg/*preferred-svg-render*
|
||||
walkmap.svg=> (time (def hiccup (binary-stl-file->svg "resources/isle_of_man.stl")))
|
||||
20-05-25 09:37:44 mason INFO [walkmap.svg:82] - Generating SVG for :hiccup renderer
|
||||
"Elapsed time: 52.614707 msecs"
|
||||
#'walkmap.svg/hiccup
|
||||
walkmap.svg=> (def ^:dynamic *preferred-svg-render* :dali)
|
||||
#'walkmap.svg/*preferred-svg-render*
|
||||
walkmap.svg=> (time (def dali (binary-stl-file->svg "resources/isle_of_man.stl")))
|
||||
20-05-25 09:38:07 mason INFO [walkmap.svg:82] - Generating SVG for :dali renderer
|
||||
"Elapsed time: 49.891043 msecs"
|
||||
#'walkmap.svg/dali
|
||||
|
||||
This implies that the problem is not in the way polygons are formatted.
|
||||
|
||||
The difference between the two versions of `facet->svg-poly` is as follows:
|
||||
|
||||
### New version, works with both Hiccup and Dali:
|
||||
|
||||
walkmap.svg=> (def stl (decode-binary-stl "resources/isle_of_man.stl"))
|
||||
#'walkmap.svg/stl
|
||||
walkmap.svg=> (def facet (first (:facets stl)))
|
||||
#'walkmap.svg/facet
|
||||
walkmap.svg=> (pprint facet)
|
||||
{:normal {:x -0.0, :y 0.0, :z 1.0},
|
||||
:vertices
|
||||
[{:x 3.0, :y 1.0, :z 1.0}
|
||||
{:x 2.0, :y 3.0, :z 1.0}
|
||||
{:x 0.0, :y 0.0, :z 1.0}],
|
||||
:abc 0}
|
||||
nil
|
||||
walkmap.svg=> (pprint (facet->svg-poly facet))
|
||||
[:polygon {:points "3.0,1.0 2.0,3.0 0.0,0.0"}]
|
||||
nil
|
||||
|
||||
In other words, the new version constructs the `:points` attribute of the `:polygon` tag by string concatenation, and the renderer just needs to output it.
|
||||
|
||||
### Older version, works with Dali only:
|
||||
|
||||
walkmap.svg=> (pprint (dali-facet->svg-poly facet))
|
||||
[:polygon [3.0 1.0] [2.0 3.0] [0.0 0.0]]
|
||||
nil
|
||||
|
||||
This means that the renderer is actually doing more work, since it has to compose the `:points` attribute itself; nevertheless there doesn't seem to be an increased time penalty.
|
||||
|
||||
### Conclusion
|
||||
|
||||
It doesn't seem that formatting the polygons is the issue.
|
||||
|
||||
## Hypothesis two: Dali renderer scales non-linearly with number of objects drawn
|
||||
|
||||
To test this, we need some otherwise-similar test files with different numbers of objects:
|
||||
|
||||
walkmap.svg=> (count (:facets stl))
|
||||
4416
|
||||
walkmap.svg=> (def small-stl (assoc stl :facets (take 400 (:facets stl))))
|
||||
#'walkmap.svg/small-stl
|
||||
walkmap.svg=> (count (:facets small-stl))
|
||||
400
|
||||
walkmap.svg=> (def large-stl (decode-binary-stl "../the-great-game/resources/maps/heightmap.stl"))
|
||||
#'walkmap.svg/large-stl
|
||||
walkmap.svg=> (count (:facets large-stl))
|
||||
746585
|
||||
walkmap.svg=> (def ^:dynamic *preferred-svg-render* :dali)
|
||||
#'walkmap.svg/*preferred-svg-render*
|
||||
walkmap.svg=> (time (dali.io/render-svg (stl->svg small-stl) "dali-small.svg"))
|
||||
20-05-25 10:12:25 mason INFO [walkmap.svg:92] - Generating SVG for :dali renderer
|
||||
"Elapsed time: 32.55506 msecs"
|
||||
nil
|
||||
walkmap.svg=> (def ^:dynamic *preferred-svg-render* :hiccup)
|
||||
#'walkmap.svg/*preferred-svg-render*
|
||||
walkmap.svg=> (time (spit "hiccup-small.svg" (hiccup.core/html (stl->svg small-stl))))
|
||||
20-05-25 10:14:07 mason INFO [walkmap.svg:92] - Generating SVG for :hiccup renderer
|
||||
"Elapsed time: 10.026369 msecs"
|
||||
|
||||
So we have
|
||||
|
||||
| | Dali | | Hiccup | | |
|
||||
| ----------- | ---------------- | ----------- | ------------ | ----------- | ------------------- |
|
||||
| # of facets | time (msecs) | objets/msec | time (msecs) | objets/msec | ratio (Dali/Hiccup) |
|
||||
| ----------- | ---------------- | ----------- | ------------ | ----------- | --------------------|
|
||||
| 400 | 32.55506 | 12.29 | 10.026369 | 39.89 | 3.35 |
|
||||
| 4416 | 874.292007 | 5.05 | 84.09972 | 52.51 | 10.40 |
|
||||
| 746585 | 29,695,695.61 | 0.03 | 16724.848222 | 44.64 | 1775.54 |
|
||||
|
||||
### Conclusion
|
||||
|
||||
What we're seeing is that Hiccup renders more or less linearly by the number of objects (bear in mind that all of these objects are triangles, so essentially equally complex to render), whereas the performance of Dali degrades significantly as the number of objects increases.
|
|
@ -14,135 +14,221 @@
|
|||
<td class="with-number">Lines %</td>
|
||||
<td class="with-number">Total</td><td class="with-number">Blank</td><td class="with-number">Instrumented</td>
|
||||
</tr></thead>
|
||||
<tr>
|
||||
<td><a href="walkmap/core.clj.html">walkmap.core</a></td><td class="with-bar"><div class="covered"
|
||||
style="width:12.244897959183673%;
|
||||
float:left;"> 6 </div><div class="not-covered"
|
||||
style="width:87.75510204081633%;
|
||||
float:left;"> 43 </div></td>
|
||||
<td class="with-number">12.24 %</td>
|
||||
<td class="with-bar"><div class="covered"
|
||||
style="width:38.46153846153846%;
|
||||
float:left;"> 5 </div><div class="not-covered"
|
||||
style="width:61.53846153846154%;
|
||||
float:left;"> 8 </div></td>
|
||||
<td class="with-number">38.46 %</td>
|
||||
<td class="with-number">44</td><td class="with-number">6</td><td class="with-number">13</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="walkmap/edge.clj.html">walkmap.edge</a></td><td class="with-bar"><div class="covered"
|
||||
style="width:69.38775510204081%;
|
||||
float:left;"> 136 </div><div class="not-covered"
|
||||
style="width:30.612244897959183%;
|
||||
float:left;"> 60 </div></td>
|
||||
<td class="with-number">69.39 %</td>
|
||||
style="width:98.37177747625509%;
|
||||
float:left;"> 725 </div><div class="not-covered"
|
||||
style="width:1.6282225237449117%;
|
||||
float:left;"> 12 </div></td>
|
||||
<td class="with-number">98.37 %</td>
|
||||
<td class="with-bar"><div class="covered"
|
||||
style="width:70.0%;
|
||||
float:left;"> 35 </div><div class="partial"
|
||||
style="width:2.0%;
|
||||
float:left;"> 1 </div><div class="not-covered"
|
||||
style="width:28.0%;
|
||||
float:left;"> 14 </div></td>
|
||||
<td class="with-number">72.00 %</td>
|
||||
<td class="with-number">82</td><td class="with-number">7</td><td class="with-number">50</td>
|
||||
style="width:95.32710280373831%;
|
||||
float:left;"> 102 </div><div class="partial"
|
||||
style="width:4.672897196261682%;
|
||||
float:left;"> 5 </div></td>
|
||||
<td class="with-number">100.00 %</td>
|
||||
<td class="with-number">177</td><td class="with-number">17</td><td class="with-number">107</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="walkmap/geometry.clj.html">walkmap.geometry</a></td><td class="with-bar"><div class="covered"
|
||||
style="width:1.6260162601626016%;
|
||||
float:left;"> 2 </div><div class="not-covered"
|
||||
style="width:98.3739837398374%;
|
||||
float:left;"> 121 </div></td>
|
||||
<td class="with-number">1.63 %</td>
|
||||
<td><a href="walkmap/id.clj.html">walkmap.id</a></td><td class="with-bar"><div class="covered"
|
||||
style="width:100.0%;
|
||||
float:left;"> 3 </div></td>
|
||||
<td class="with-number">100.00 %</td>
|
||||
<td class="with-bar"><div class="covered"
|
||||
style="width:15.384615384615385%;
|
||||
float:left;"> 2 </div><div class="not-covered"
|
||||
style="width:84.61538461538461%;
|
||||
float:left;"> 11 </div></td>
|
||||
<td class="with-number">15.38 %</td>
|
||||
<td class="with-number">24</td><td class="with-number">3</td><td class="with-number">13</td>
|
||||
style="width:100.0%;
|
||||
float:left;"> 2 </div></td>
|
||||
<td class="with-number">100.00 %</td>
|
||||
<td class="with-number">8</td><td class="with-number">2</td><td class="with-number">2</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="walkmap/microworld.clj.html">walkmap.microworld</a></td><td class="with-bar"><div class="covered"
|
||||
style="width:1.5957446808510638%;
|
||||
float:left;"> 3 </div><div class="not-covered"
|
||||
style="width:98.40425531914893%;
|
||||
float:left;"> 185 </div></td>
|
||||
<td class="with-number">1.60 %</td>
|
||||
<td class="with-bar"><div class="covered"
|
||||
style="width:7.5%;
|
||||
float:left;"> 3 </div><div class="not-covered"
|
||||
style="width:92.5%;
|
||||
float:left;"> 37 </div></td>
|
||||
<td class="with-number">7.50 %</td>
|
||||
<td class="with-number">75</td><td class="with-number">6</td><td class="with-number">40</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="walkmap/ocean.clj.html">walkmap.ocean</a></td><td class="with-bar"><div class="covered"
|
||||
style="width:100.0%;
|
||||
float:left;"> 28 </div></td>
|
||||
<td class="with-number">100.00 %</td>
|
||||
<td class="with-bar"><div class="covered"
|
||||
style="width:100.0%;
|
||||
float:left;"> 8 </div></td>
|
||||
<td class="with-number">100.00 %</td>
|
||||
<td class="with-number">25</td><td class="with-number">4</td><td class="with-number">8</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="walkmap/path.clj.html">walkmap.path</a></td><td class="with-bar"><div class="covered"
|
||||
style="width:5.555555555555555%;
|
||||
float:left;"> 3 </div><div class="not-covered"
|
||||
style="width:94.44444444444444%;
|
||||
float:left;"> 51 </div></td>
|
||||
<td class="with-number">5.56 %</td>
|
||||
style="width:93.9622641509434%;
|
||||
float:left;"> 249 </div><div class="not-covered"
|
||||
style="width:6.037735849056604%;
|
||||
float:left;"> 16 </div></td>
|
||||
<td class="with-number">93.96 %</td>
|
||||
<td class="with-bar"><div class="covered"
|
||||
style="width:23.076923076923077%;
|
||||
float:left;"> 3 </div><div class="not-covered"
|
||||
style="width:76.92307692307692%;
|
||||
float:left;"> 10 </div></td>
|
||||
<td class="with-number">23.08 %</td>
|
||||
<td class="with-number">30</td><td class="with-number">4</td><td class="with-number">13</td>
|
||||
style="width:88.88888888888889%;
|
||||
float:left;"> 32 </div><div class="partial"
|
||||
style="width:11.11111111111111%;
|
||||
float:left;"> 4 </div></td>
|
||||
<td class="with-number">100.00 %</td>
|
||||
<td class="with-number">93</td><td class="with-number">9</td><td class="with-number">36</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="walkmap/polygon.clj.html">walkmap.polygon</a></td><td class="with-bar"><div class="covered"
|
||||
style="width:7.407407407407407%;
|
||||
float:left;"> 2 </div><div class="not-covered"
|
||||
style="width:92.5925925925926%;
|
||||
float:left;"> 25 </div></td>
|
||||
<td class="with-number">7.41 %</td>
|
||||
style="width:50.87976539589443%;
|
||||
float:left;"> 347 </div><div class="not-covered"
|
||||
style="width:49.12023460410557%;
|
||||
float:left;"> 335 </div></td>
|
||||
<td class="with-number">50.88 %</td>
|
||||
<td class="with-bar"><div class="covered"
|
||||
style="width:25.0%;
|
||||
style="width:61.333333333333336%;
|
||||
float:left;"> 46 </div><div class="partial"
|
||||
style="width:9.333333333333334%;
|
||||
float:left;"> 7 </div><div class="not-covered"
|
||||
style="width:29.333333333333332%;
|
||||
float:left;"> 22 </div></td>
|
||||
<td class="with-number">70.67 %</td>
|
||||
<td class="with-number">155</td><td class="with-number">14</td><td class="with-number">75</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="walkmap/read_svg.clj.html">walkmap.read-svg</a></td><td class="with-bar"><div class="covered"
|
||||
style="width:2.857142857142857%;
|
||||
float:left;"> 7 </div><div class="not-covered"
|
||||
style="width:97.14285714285714%;
|
||||
float:left;"> 238 </div></td>
|
||||
<td class="with-number">2.86 %</td>
|
||||
<td class="with-bar"><div class="covered"
|
||||
style="width:15.217391304347826%;
|
||||
float:left;"> 7 </div><div class="not-covered"
|
||||
style="width:84.78260869565217%;
|
||||
float:left;"> 39 </div></td>
|
||||
<td class="with-number">15.22 %</td>
|
||||
<td class="with-number">100</td><td class="with-number">8</td><td class="with-number">46</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="walkmap/routing.clj.html">walkmap.routing</a></td><td class="with-bar"><div class="covered"
|
||||
style="width:5.678233438485805%;
|
||||
float:left;"> 18 </div><div class="not-covered"
|
||||
style="width:94.3217665615142%;
|
||||
float:left;"> 299 </div></td>
|
||||
<td class="with-number">5.68 %</td>
|
||||
<td class="with-bar"><div class="covered"
|
||||
style="width:17.56756756756757%;
|
||||
float:left;"> 13 </div><div class="partial"
|
||||
style="width:2.7027027027027026%;
|
||||
float:left;"> 2 </div><div class="not-covered"
|
||||
style="width:75.0%;
|
||||
float:left;"> 6 </div></td>
|
||||
<td class="with-number">25.00 %</td>
|
||||
<td class="with-number">17</td><td class="with-number">3</td><td class="with-number">8</td>
|
||||
style="width:79.72972972972973%;
|
||||
float:left;"> 59 </div></td>
|
||||
<td class="with-number">20.27 %</td>
|
||||
<td class="with-number">201</td><td class="with-number">21</td><td class="with-number">74</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="walkmap/stl.clj.html">walkmap.stl</a></td><td class="with-bar"><div class="covered"
|
||||
style="width:17.228464419475657%;
|
||||
float:left;"> 46 </div><div class="not-covered"
|
||||
style="width:82.77153558052434%;
|
||||
float:left;"> 221 </div></td>
|
||||
<td class="with-number">17.23 %</td>
|
||||
style="width:50.638297872340424%;
|
||||
float:left;"> 238 </div><div class="not-covered"
|
||||
style="width:49.361702127659576%;
|
||||
float:left;"> 232 </div></td>
|
||||
<td class="with-number">50.64 %</td>
|
||||
<td class="with-bar"><div class="covered"
|
||||
style="width:28.125%;
|
||||
float:left;"> 18 </div><div class="not-covered"
|
||||
style="width:71.875%;
|
||||
float:left;"> 46 </div></td>
|
||||
<td class="with-number">28.13 %</td>
|
||||
<td class="with-number">126</td><td class="with-number">12</td><td class="with-number">64</td>
|
||||
style="width:40.56603773584906%;
|
||||
float:left;"> 43 </div><div class="partial"
|
||||
style="width:9.433962264150944%;
|
||||
float:left;"> 10 </div><div class="not-covered"
|
||||
style="width:50.0%;
|
||||
float:left;"> 53 </div></td>
|
||||
<td class="with-number">50.00 %</td>
|
||||
<td class="with-number">206</td><td class="with-number">16</td><td class="with-number">106</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="walkmap/superstructure.clj.html">walkmap.superstructure</a></td><td class="with-bar"><div class="covered"
|
||||
style="width:44.61538461538461%;
|
||||
float:left;"> 261 </div><div class="not-covered"
|
||||
style="width:55.38461538461539%;
|
||||
float:left;"> 324 </div></td>
|
||||
<td class="with-number">44.62 %</td>
|
||||
<td class="with-bar"><div class="covered"
|
||||
style="width:48.507462686567166%;
|
||||
float:left;"> 65 </div><div class="partial"
|
||||
style="width:2.985074626865672%;
|
||||
float:left;"> 4 </div><div class="not-covered"
|
||||
style="width:48.507462686567166%;
|
||||
float:left;"> 65 </div></td>
|
||||
<td class="with-number">51.49 %</td>
|
||||
<td class="with-number">272</td><td class="with-number">22</td><td class="with-number">134</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="walkmap/svg.clj.html">walkmap.svg</a></td><td class="with-bar"><div class="covered"
|
||||
style="width:2.8776978417266186%;
|
||||
float:left;"> 4 </div><div class="not-covered"
|
||||
style="width:97.12230215827338%;
|
||||
float:left;"> 135 </div></td>
|
||||
<td class="with-number">2.88 %</td>
|
||||
style="width:4.21455938697318%;
|
||||
float:left;"> 11 </div><div class="not-covered"
|
||||
style="width:95.78544061302682%;
|
||||
float:left;"> 250 </div></td>
|
||||
<td class="with-number">4.21 %</td>
|
||||
<td class="with-bar"><div class="covered"
|
||||
style="width:8.571428571428571%;
|
||||
float:left;"> 3 </div><div class="not-covered"
|
||||
style="width:91.42857142857143%;
|
||||
float:left;"> 32 </div></td>
|
||||
<td class="with-number">8.57 %</td>
|
||||
<td class="with-number">50</td><td class="with-number">2</td><td class="with-number">35</td>
|
||||
style="width:12.121212121212121%;
|
||||
float:left;"> 8 </div><div class="not-covered"
|
||||
style="width:87.87878787878788%;
|
||||
float:left;"> 58 </div></td>
|
||||
<td class="with-number">12.12 %</td>
|
||||
<td class="with-number">110</td><td class="with-number">7</td><td class="with-number">66</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="walkmap/tag.clj.html">walkmap.tag</a></td><td class="with-bar"><div class="covered"
|
||||
style="width:100.0%;
|
||||
float:left;"> 159 </div></td>
|
||||
<td class="with-number">100.00 %</td>
|
||||
<td class="with-bar"><div class="covered"
|
||||
style="width:100.0%;
|
||||
float:left;"> 34 </div></td>
|
||||
<td class="with-number">100.00 %</td>
|
||||
<td class="with-number">68</td><td class="with-number">8</td><td class="with-number">34</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="walkmap/utils.clj.html">walkmap.utils</a></td><td class="with-bar"><div class="covered"
|
||||
style="width:96.31043256997455%;
|
||||
float:left;"> 757 </div><div class="not-covered"
|
||||
style="width:3.6895674300254453%;
|
||||
float:left;"> 29 </div></td>
|
||||
<td class="with-number">96.31 %</td>
|
||||
<td class="with-bar"><div class="covered"
|
||||
style="width:80.43478260869566%;
|
||||
float:left;"> 37 </div><div class="partial"
|
||||
style="width:2.1739130434782608%;
|
||||
float:left;"> 1 </div><div class="not-covered"
|
||||
style="width:17.391304347826086%;
|
||||
float:left;"> 8 </div></td>
|
||||
<td class="with-number">82.61 %</td>
|
||||
<td class="with-number">119</td><td class="with-number">10</td><td class="with-number">46</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="walkmap/vertex.clj.html">walkmap.vertex</a></td><td class="with-bar"><div class="covered"
|
||||
style="width:72.94117647058823%;
|
||||
float:left;"> 62 </div><div class="not-covered"
|
||||
style="width:27.058823529411764%;
|
||||
float:left;"> 23 </div></td>
|
||||
<td class="with-number">72.94 %</td>
|
||||
style="width:89.45454545454545%;
|
||||
float:left;"> 492 </div><div class="not-covered"
|
||||
style="width:10.545454545454545%;
|
||||
float:left;"> 58 </div></td>
|
||||
<td class="with-number">89.45 %</td>
|
||||
<td class="with-bar"><div class="covered"
|
||||
style="width:63.63636363636363%;
|
||||
float:left;"> 14 </div><div class="partial"
|
||||
style="width:13.636363636363637%;
|
||||
float:left;"> 3 </div><div class="not-covered"
|
||||
style="width:22.727272727272727%;
|
||||
float:left;"> 5 </div></td>
|
||||
<td class="with-number">77.27 %</td>
|
||||
<td class="with-number">43</td><td class="with-number">5</td><td class="with-number">22</td>
|
||||
style="width:83.78378378378379%;
|
||||
float:left;"> 62 </div><div class="partial"
|
||||
style="width:13.513513513513514%;
|
||||
float:left;"> 10 </div><div class="not-covered"
|
||||
style="width:2.7027027027027026%;
|
||||
float:left;"> 2 </div></td>
|
||||
<td class="with-number">97.30 %</td>
|
||||
<td class="with-number">151</td><td class="with-number">15</td><td class="with-number">74</td>
|
||||
</tr>
|
||||
<tr><td>Totals:</td>
|
||||
<td class="with-bar"></td>
|
||||
<td class="with-number">27.77 %</td>
|
||||
<td class="with-number">62.51 %</td>
|
||||
<td class="with-bar"></td>
|
||||
<td class="with-number">39.45 %</td>
|
||||
<td class="with-number">59.55 %</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
|
|
|
@ -8,10 +8,10 @@
|
|||
001 (ns walkmap.core
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
002 "At this stage, primarily utility functions dealing with stereolithography
|
||||
002 "This namespace mostly gets used as a scratchpad for ideas which haven't yet
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
003 (STL) files. Not a stable API yet!"
|
||||
003 solidified."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
004 (:require [clojure.java.io :as io :refer [file output-stream input-stream]]
|
||||
|
@ -26,115 +26,10 @@
|
|||
007 [me.raynes.fs :as fs]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
008 [taoensso.timbre :as l :refer [info error spy]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
009 [walkmap.stl :refer [decode-binary-stl]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
010 [walkmap.svg :refer [stl->svg]]))
|
||||
008 [taoensso.timbre :as l :refer [info error spy]]))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
011
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
012 (def ^:dynamic *sea-level*
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
013 "The sea level on heightmaps we're currently handling. If characters are to
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
014 be able to swin in the sea, we must model the sea bottom, so we need
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
015 heightmaps which cover at least the continental shelf. However, the sea
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
016 bottom is not walkable territory and can be culled from walkmaps.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
017
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
018 **Note** must be a floating point number. `(= 0 0.0)` returns `false`!"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
019 0.0)
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
020
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
021 (defn ocean?
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
022 "Of a `facet`, is the altitude of every vertice equal to `*sea-level*`?"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
023 [facet]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
024 (every?
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
025 #(= % *sea-level*)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 6 forms covered">
|
||||
026 (map :z (:vertices facet))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
027
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
028 (defn cull-ocean-facets
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
029 "Ye cannae walk on water. Remove all facets from this `stl` structure which
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
030 are at sea level."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
031 [stl]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 10 forms covered">
|
||||
032 (assoc stl :facets (remove ocean? (:facets stl))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
033
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
034 (defn binary-stl-file->svg
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
035 "Given only an `in-filename`, parse the indicated file, expected to be
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
036 binary STL, and return an equivalent SVG structure. Given both `in-filename`
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
037 and `out-filename`, as side-effect write the SVG to the indicated output file."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
038 ([in-filename]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 7 forms covered">
|
||||
039 (stl->svg (cull-ocean-facets (decode-binary-stl in-filename))))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
040 ([in-filename out-filename]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
041 (let [s (binary-stl-file->svg in-filename)]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 8 forms covered">
|
||||
042 (spit out-filename (html s))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
043 s)))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
044
|
||||
009
|
||||
</span><br/>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -20,235 +20,520 @@
|
|||
005 (:require [clojure.math.numeric-tower :as m]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
006 [walkmap.path :refer [path? polygon->path]]
|
||||
006 [walkmap.utils :as u]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
007 [walkmap.polygon :refer [polygon?]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
008 [walkmap.vertex :refer [ensure3d vertex?]]))
|
||||
007 [walkmap.vertex :refer [canonicalise check-vertex ensure2d ensure3d vertex vertex= vertex?]]))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
009
|
||||
008
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
010 (defn edge?
|
||||
009 (defn edge
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
011 "True if `o` satisfies the conditions for a path. A path shall be a map
|
||||
010 "Return an edge between vertices `v1` and `v2`."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
012 having the keys `:start` and `:end`, such that the values of each of those
|
||||
011 [v1 v2]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
013 keys shall be a vertex."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
014 [o]
|
||||
</span><br/>
|
||||
<span class="partial" title="8 out of 9 forms covered">
|
||||
015 (and
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
016 (map? o)
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
012 {:kind :edge
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
017 (vertex? (:start o))
|
||||
013 :walkmap.id/id (keyword (gensym "edge"))
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
018 (vertex? (:end o))))
|
||||
<span class="covered" title="16 out of 16 forms covered">
|
||||
014 :start (check-vertex v1)
|
||||
</span><br/>
|
||||
<span class="covered" title="16 out of 16 forms covered">
|
||||
015 :end (check-vertex v2)})
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
019
|
||||
016
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
020 (defn path->edges
|
||||
017 (defn edge?
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
021 "if `o` is a path, a polygon, or a sequence of vertices, return a sequence of
|
||||
018 "True if `o` satisfies the conditions for a edge. An edge shall be a map
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
022 edges representing that path, polygon or sequence."
|
||||
019 having the keys `:start` and `:end`, such that the values of each of those
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
023 [o]
|
||||
020 keys shall be a vertex."
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
024 (cond
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
021 [o]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
025 (seq? o)
|
||||
<span class="covered" title="9 out of 9 forms covered">
|
||||
022 (and
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
026 (when
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
023 (map? o)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
027 (and
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
024 (vertex? (:start o))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
028 (vertex? (first o))
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
025 (vertex? (:end o))))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 6 forms covered">
|
||||
029 (vertex? (first (rest o))))
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
026
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
030 (cons
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
027 (defn length
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 6 forms covered">
|
||||
031 {:start (first o)
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
028 "Return the length of the edge `e`."
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
032 :end (first (rest o))}
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
029 [e]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
033 (path->edges (rest o))))
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
030 (let [start (ensure3d (:start e))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
034 (path? o)
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
031 end (ensure3d (:end e))]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
035 (path->edges (:nodes o))
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
032 (m/sqrt
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
036 (polygon? o)
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
033 (reduce
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
037 (path->edges (polygon->path o))))
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
034 +
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
035 (map
|
||||
</span><br/>
|
||||
<span class="covered" title="11 out of 11 forms covered">
|
||||
036 #(m/expt (- (% end) (% start)) 2)
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
037 [:x :y :z])))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
038
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
039 (defn length
|
||||
039 (defn centre
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
040 "Return the length of the edge `e`."
|
||||
040 "Return the vertex that represents the centre of this `edge`."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
041 [e]
|
||||
041 [edge]
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
042 (let [start (ensure3d (:start e))
|
||||
042 (let [s (ensure3d (:start edge))
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
043 end (ensure3d (:end e))]
|
||||
043 e (ensure3d (:end edge))]
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
044 (m/sqrt
|
||||
044 (vertex
|
||||
</span><br/>
|
||||
<span class="covered" title="16 out of 16 forms covered">
|
||||
045 (+ (:x s) (/ (- (:x e) (:x s)) 2))
|
||||
</span><br/>
|
||||
<span class="covered" title="16 out of 16 forms covered">
|
||||
046 (+ (:y s) (/ (- (:y e) (:y s)) 2))
|
||||
</span><br/>
|
||||
<span class="covered" title="16 out of 16 forms covered">
|
||||
047 (+ (:z s) (/ (- (:z e) (:z s)) 2)))))
|
||||
</span><br/>
|
||||
<span class="blank" title="4 out of 4 forms covered">
|
||||
048
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
049 (defn unit-vector
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
050 "Return an vertex parallel to `e` starting from the coordinate origin. Two
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
045 (reduce
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
046 +
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
047 (map
|
||||
</span><br/>
|
||||
<span class="covered" title="11 out of 11 forms covered">
|
||||
048 #(m/expt (- (% end) (% start)) 2)
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
049 [:x :y :z])))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
050
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
051 (defn unit-vector
|
||||
051 edges which are parallel will have the same unit vector."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
052 "Return an vertex parallel to `e` starting from the coordinate origin. Two
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
053 edges which are parallel will have the same unit vector."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
054 [e]
|
||||
052 [e]
|
||||
</span><br/>
|
||||
<span class="covered" title="14 out of 14 forms covered">
|
||||
055 (let [e' {:start (ensure3d (:start e)) :end (ensure3d (:end e))}
|
||||
053 (let [e' {:start (ensure3d (:start e)) :end (ensure3d (:end e))}
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
056 l (length e')]
|
||||
054 l (length e')]
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
057 (reduce
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
058 merge
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
059 {}
|
||||
055 (canonicalise
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
060 (map
|
||||
056 (reduce
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
061 (fn [k]
|
||||
057 merge
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
058 {}
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
059 (map
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
060 (fn [k]
|
||||
</span><br/>
|
||||
<span class="covered" title="17 out of 17 forms covered">
|
||||
062 {k (/ (- (k (:end e')) (k (:start e'))) l)})
|
||||
061 {k (/ (- (k (:end e')) (k (:start e'))) l)})
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
063 [:x :y :z]))))
|
||||
062 [:x :y :z])))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
064
|
||||
063
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
065 (defn parallel?
|
||||
064 (defn parallel?
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
066 "True if all `edges` passed are parallel with one another."
|
||||
065 "True if all `edges` passed are parallel with one another."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
067 ;; TODO: this bears being wary about, dealing with floating point arithmetic.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
068 ;; Keep an eye out for spurious errors.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
069 [& edges]
|
||||
066 [& edges]
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
070 (let [uvs (map unit-vector edges)]
|
||||
067 (let [uvs (map unit-vector edges)]
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
071 (every?
|
||||
068 (every?
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
072 #(= % (first uvs))
|
||||
069 #(vertex= % (first uvs))
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
073 (rest uvs))))
|
||||
070 (rest uvs))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
074
|
||||
071
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
075 (defn collinear?
|
||||
072 (defn collinear?
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
076 "True if edges `e1` and `e2` are collinear with one another."
|
||||
073 "True if edges `e1` and `e2` are collinear with one another."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
077 [e1 e2]
|
||||
074 [e1 e2]
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
078 (parallel?
|
||||
075 (parallel?
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
079 e1
|
||||
076 e1
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
080 e2
|
||||
077 e2
|
||||
</span><br/>
|
||||
<span class="covered" title="9 out of 9 forms covered">
|
||||
081 {:start (:start e1) :end (:start e2)}))
|
||||
078 (if (vertex= (:start e1) (:start e2))
|
||||
</span><br/>
|
||||
<span class="covered" title="9 out of 9 forms covered">
|
||||
079 {:start (:start e1) :end (:end e2)}
|
||||
</span><br/>
|
||||
<span class="covered" title="9 out of 9 forms covered">
|
||||
080 {:start (:start e1) :end (:start e2)})))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
082
|
||||
081
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
082 (defn collinear2d?
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
083 "True if the projections of edges `e1`, `e2` onto the x, y plane are
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
084 collinear."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
085 [e1 e2]
|
||||
</span><br/>
|
||||
<span class="covered" title="15 out of 15 forms covered">
|
||||
086 (collinear? {:start (ensure2d (:start e1)) :end (ensure2d (:end e1))}
|
||||
</span><br/>
|
||||
<span class="covered" title="13 out of 13 forms covered">
|
||||
087 {:start (ensure2d (:start e2)) :end (ensure2d (:end e2))}))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
088
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
089 (defn minimaxd
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
090 "Apply function `f` to `coord` of the vertices at start and end of `edge`
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
091 and return the result. Intended use case is `f` = `min` or `max`, `coord`
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
092 is `:x`, `:y` or `:z`. No checks are made for sane arguments."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
093 [edge coord f]
|
||||
</span><br/>
|
||||
<span class="covered" title="15 out of 15 forms covered">
|
||||
094 (apply f (list (coord (:start edge)) (coord (:end edge)))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
095
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
096 (defn on?
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
097 "True if the vertex `v` is on the edge `e`."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
098 [e v]
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
099 (let [p (ensure3d (:start e))
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
100 q (ensure3d v)
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
101 r (ensure3d (:end e))]
|
||||
</span><br/>
|
||||
<span class="partial" title="20 out of 25 forms covered">
|
||||
102 (and
|
||||
</span><br/>
|
||||
<span class="covered" title="10 out of 10 forms covered">
|
||||
103 (collinear? (edge p q) (edge q r))
|
||||
</span><br/>
|
||||
<span class="covered" title="13 out of 13 forms covered">
|
||||
104 (<= (:x q) (max (:x p) (:x r)))
|
||||
</span><br/>
|
||||
<span class="covered" title="13 out of 13 forms covered">
|
||||
105 (>= (:x q) (min (:x p) (:x r)))
|
||||
</span><br/>
|
||||
<span class="covered" title="13 out of 13 forms covered">
|
||||
106 (<= (:y q) (max (:y p) (:y r)))
|
||||
</span><br/>
|
||||
<span class="covered" title="13 out of 13 forms covered">
|
||||
107 (>= (:y q) (min (:y p) (:y r)))
|
||||
</span><br/>
|
||||
<span class="covered" title="13 out of 13 forms covered">
|
||||
108 (<= (:z q) (max (:z p) (:z r)))
|
||||
</span><br/>
|
||||
<span class="covered" title="12 out of 12 forms covered">
|
||||
109 (>= (:z q) (min (:z p) (:z r))))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
110
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
111 (defn on2d?
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
112 "True if vertex `v` is on edge `e` when projected onto the x, y plane."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
113 [e v]
|
||||
</span><br/>
|
||||
<span class="covered" title="15 out of 15 forms covered">
|
||||
114 (on? (edge (ensure2d (:start e)) (ensure2d (:end e))) v))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
115
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
116 (defn overlaps2d?
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
117 "True if the recangle in the x,y plane bisected by edge `e1` overlaps that
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
118 bisected by edge `e2`. It is an error if either `e1` or `e2` is not an edge."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
119 [e1 e2]
|
||||
</span><br/>
|
||||
<span class="partial" title="11 out of 12 forms covered">
|
||||
120 (when (and (edge? e1) (edge? e2))
|
||||
</span><br/>
|
||||
<span class="partial" title="11 out of 13 forms covered">
|
||||
121 (and
|
||||
</span><br/>
|
||||
<span class="covered" title="12 out of 12 forms covered">
|
||||
122 (> (minimaxd e1 :x max) (minimaxd e2 :x min))
|
||||
</span><br/>
|
||||
<span class="covered" title="12 out of 12 forms covered">
|
||||
123 (< (minimaxd e1 :x min) (minimaxd e2 :x max))
|
||||
</span><br/>
|
||||
<span class="covered" title="12 out of 12 forms covered">
|
||||
124 (> (minimaxd e1 :y max) (minimaxd e2 :y min))
|
||||
</span><br/>
|
||||
<span class="covered" title="11 out of 11 forms covered">
|
||||
125 (< (minimaxd e1 :y min) (minimaxd e2 :y max)))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
126
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
127 (defn intersection2d
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
128 "The probability of two lines intersecting in 3d space is low, and actually
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
129 that is mostly not something we're interested in. We're interested in
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
130 intersection in the `x,y` plane. This function returns a vertex representing
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
131 a point vertically over the intersection of edges `e1`, `e2` in the `x,y`
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
132 plane, whose `z` coordinate is
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
133
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
134 * 0 if both edges are 2d (i.e. have missing or zero `z` coordinates);
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
135 * if one edge is 2d, then the point on the other edge over the intersection;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
136 * otherwise, the average of the z coordinates of the points on the two
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
137 edges over the intersection.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
138
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
139 If no such intersection exists, `nil` is returned.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
140
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
141 It is an error, and an exception will be thrown, if either `e1` or `e2` is
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
142 not an edge."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
143 [e1 e2]
|
||||
</span><br/>
|
||||
<span class="covered" title="11 out of 11 forms covered">
|
||||
144 (if (and (edge? e1) (edge? e2))
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
145 (when
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
146 (overlaps2d? e1 e2) ;; relatively cheap check
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
147 (if
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
148 (collinear2d? e1 e2)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
149 ;; any point within the overlap will do, but we'll pick the end of e1
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
150 ;; which is on e2
|
||||
</span><br/>
|
||||
<span class="partial" title="10 out of 13 forms covered">
|
||||
151 (if (on2d? e2 (:start e1)) (:start e1) (:end e1))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
152 ;; blatantly stolen from
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
153 ;; https://gist.github.com/cassiel/3e725b49670356a9b936
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
154 (let [x1 (:x (:start e1))
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
155 x2 (:x (:end e1))
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
156 x3 (:x (:start e2))
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
157 x4 (:x (:end e2))
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
158 y1 (:y (:start e1))
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
159 y2 (:y (:end e1))
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
160 y3 (:y (:start e2))
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
161 y4 (:y (:end e2))
|
||||
</span><br/>
|
||||
<span class="covered" title="12 out of 12 forms covered">
|
||||
162 denom (- (* (- x1 x2) (- y3 y4))
|
||||
</span><br/>
|
||||
<span class="covered" title="10 out of 10 forms covered">
|
||||
163 (* (- y1 y2) (- x3 x4)))
|
||||
</span><br/>
|
||||
<span class="covered" title="10 out of 10 forms covered">
|
||||
164 x1y2-y1x2 (- (* x1 y2) (* y1 x2))
|
||||
</span><br/>
|
||||
<span class="covered" title="10 out of 10 forms covered">
|
||||
165 x3y4-y3x4 (- (* x3 y4) (* y3 x4))
|
||||
</span><br/>
|
||||
<span class="covered" title="9 out of 9 forms covered">
|
||||
166 px-num (- (* x1y2-y1x2 (- x3 x4))
|
||||
</span><br/>
|
||||
<span class="covered" title="7 out of 7 forms covered">
|
||||
167 (* (- x1 x2) x3y4-y3x4))
|
||||
</span><br/>
|
||||
<span class="covered" title="9 out of 9 forms covered">
|
||||
168 py-num (- (* x1y2-y1x2 (- y3 y4))
|
||||
</span><br/>
|
||||
<span class="covered" title="7 out of 7 forms covered">
|
||||
169 (* (- y1 y2) x3y4-y3x4))
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
170 result (when-not (zero? denom)
|
||||
</span><br/>
|
||||
<span class="covered" title="10 out of 10 forms covered">
|
||||
171 (vertex (/ px-num denom) (/ py-num denom)))]
|
||||
</span><br/>
|
||||
<span class="partial" title="19 out of 20 forms covered">
|
||||
172 (when (and result (on2d? e1 result) (on2d? e2 result)) result))))
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
173 (throw (IllegalArgumentException.
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
174 (str
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
175 "Both `e1` and `e2` must be edges."
|
||||
</span><br/>
|
||||
<span class="covered" title="16 out of 16 forms covered">
|
||||
176 (map #(or (:kind %) (type %)) [e1 e2]))))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
177
|
||||
</span><br/>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -11,70 +11,49 @@
|
|||
002 (:require [clojure.math.combinatorics :as combo]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
003 [clojure.math.numeric-tower :as m]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
004 [walkmap.edge :as e]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
005 [walkmap.path :refer [path? polygon->path]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
006 [walkmap.polygon :refer [polygon?]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
007 [walkmap.vertex :as v]))
|
||||
003 [clojure.math.numeric-tower :as m]))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
008
|
||||
004
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
009 (defn on?
|
||||
005 (defn =ish
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
010 "True if the vertex `v` is on the edge `e`."
|
||||
006 "True if numbers `n1`, `n2` are roughly equal; that is to say, equal to
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
011 [e v]
|
||||
007 within `tolerance` (defaults to one part in a million)."
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 6 forms covered">
|
||||
012 (let [p (v/ensure3d (:start e))
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
008 ([n1 n2]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
013 q (v/ensure3d v)
|
||||
<span class="covered" title="11 out of 11 forms covered">
|
||||
009 (if (and (number? n1) (number? n2))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
014 r (v/ensure3d (:end e))]
|
||||
<span class="covered" title="7 out of 7 forms covered">
|
||||
010 (let [m (m/abs (min n1 n2))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 25 forms covered">
|
||||
015 (and
|
||||
<span class="covered" title="9 out of 9 forms covered">
|
||||
011 t (if (zero? m) 0.000001 (* 0.000001 m))]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
016 (e/collinear? p q r)
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
012 (=ish n1 n2 t))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 13 forms covered">
|
||||
017 (<= (:x q) (max (:x p) (:x r)))
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
013 (= n1 n2)))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 13 forms covered">
|
||||
018 (>= (:x q) (min (:x p) (:x r)))
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
014 ([n1 n2 tolerance]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 13 forms covered">
|
||||
019 (<= (:y q) (max (:y p) (:y r)))
|
||||
<span class="covered" title="11 out of 11 forms covered">
|
||||
015 (if (and (number? n1) (number? n2))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 13 forms covered">
|
||||
020 (>= (:y q) (min (:y p) (:y r)))
|
||||
<span class="covered" title="9 out of 9 forms covered">
|
||||
016 (< (m/abs (- n1 n2)) tolerance)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 13 forms covered">
|
||||
021 (<= (:z q) (max (:z p) (:z r)))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 12 forms covered">
|
||||
022 (>= (:z q) (min (:z p) (:z r))))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
023
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
024
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
017 (= n1 n2))))
|
||||
</span><br/>
|
||||
</body>
|
||||
</html>
|
||||
|
|
32
docs/cloverage/walkmap/id.clj.html
Normal file
32
docs/cloverage/walkmap/id.clj.html
Normal file
|
@ -0,0 +1,32 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<link rel="stylesheet" href="../coverage.css"/> <title> walkmap/id.clj </title>
|
||||
</head>
|
||||
<body>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
001 (ns walkmap.id
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
002 "The namespace within which the privileged keyword `:walkmap.id/id` is defined.")
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
003
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
004 (def ^:const id
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
005 "The magic id key walkmap uses, to distinguish it from all other uses of
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
006 the unprotected keyword."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
007 ::id)
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
008
|
||||
</span><br/>
|
||||
</body>
|
||||
</html>
|
233
docs/cloverage/walkmap/microworld.clj.html
Normal file
233
docs/cloverage/walkmap/microworld.clj.html
Normal file
|
@ -0,0 +1,233 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<link rel="stylesheet" href="../coverage.css"/> <title> walkmap/microworld.clj </title>
|
||||
</head>
|
||||
<body>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
001 (ns walkmap.microworld
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
002 "An interface between walkmap and microworld, to allow use of microworld
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
003 functionality to model things like rainfall, soil fertility, settlement
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
004 and so on."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
005 (:require [clojure.edn :as edn :only [read]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
006 [clojure.java.io :as io]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
007 [clojure.string :as s]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
008 [taoensso.timbre :as l]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
009 [walkmap.edge :as e]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
010 [walkmap.polygon :as p :only [rectangle]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
011 [walkmap.superstructure :refer [store]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
012 [walkmap.tag :as t :only [tag]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
013 [walkmap.vertex :as v :only [check-vertex vertex vertex?]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
014 [walkmap.utils :as u :only [truncate]]))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
015
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
016 (defn cell->polygon
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
017 "From this MicroWorld `cell`, construct a walkmap polygon (specifically,
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
018 a rectangle. If `scale-vector` passed and is a vertex, scale all the vertices
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
019 in the cell by that vector."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
020 ([cell]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 8 forms covered">
|
||||
021 (cell->polygon cell (v/vertex 1 1 1)))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
022 ([cell scale-vector]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
023 (t/tag
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
024 (assoc
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
025 (merge
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
026 cell
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 24 forms covered">
|
||||
027 (let [w (* (:x cell) (:x (v/check-vertex scale-vector)))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 8 forms covered">
|
||||
028 s (* (:y cell) (:y scale-vector))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 6 forms covered">
|
||||
029 e (+ w (:x scale-vector))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 6 forms covered">
|
||||
030 n (+ s (:y scale-vector))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 8 forms covered">
|
||||
031 z (* (:altitude cell) (:z scale-vector))]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
032 (p/rectangle
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
033 (v/vertex s w z)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
034 (v/vertex n e z))))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
035 :walkmap.id/id
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
036 (keyword (gensym "mw-cell")))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
037 (:state cell))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
038
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
039 (defn load-microworld-edn
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
040 "While it would be possible to call MicroWorld functions directly from
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
041 Walkmap, the fact is that running MicroWorld is so phenomenally
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
042 compute-heavy that it's much more sensible to do it in batch mode. So the
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
043 better plan is to be able to pull the output from MicroWorld - as an EDN
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
044 structure - into a walkmap superstructure."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
045 ([filename]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
046 (load-microworld-edn filename :mw))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
047 ([filename map-kind]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
048 (when-not
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
049 (keyword? map-kind)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
050 (throw (IllegalArgumentException.
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
051 (u/truncate
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 9 forms covered">
|
||||
052 (str "Must be a keyword: " (or map-kind "nil")) 80))))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
053 (load-microworld-edn filename map-kind nil))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
054 ([filename mapkind superstucture]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 10 forms covered">
|
||||
055 (load-microworld-edn filename mapkind superstucture (v/vertex 1 1 1)))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
056 ([filename map-kind superstructure scale-vertex]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
057 (let [mw (try
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 7 forms covered">
|
||||
058 (with-open [r (io/reader filename)]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
059 (edn/read (java.io.PushbackReader. r)))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
060 (catch RuntimeException e
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 15 forms covered">
|
||||
061 (l/error "Error parsing edn file '%s': %s\n"
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
062 filename (.getMessage e))))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
063 polys (reduce
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
064 concat
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 8 forms covered">
|
||||
065 (map (fn [row] (map cell->polygon row)) mw))]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
066 (if (map? superstructure)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
067 (reduce
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
068 #(store %2 %1)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
069 superstructure
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
070 polys)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
071 polys))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
072
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
073
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
074
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
075
|
||||
</span><br/>
|
||||
</body>
|
||||
</html>
|
83
docs/cloverage/walkmap/ocean.clj.html
Normal file
83
docs/cloverage/walkmap/ocean.clj.html
Normal file
|
@ -0,0 +1,83 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<link rel="stylesheet" href="../coverage.css"/> <title> walkmap/ocean.clj </title>
|
||||
</head>
|
||||
<body>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
001 (ns walkmap.ocean
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
002 "Deal with (specifically, at this stage, cull) ocean areas"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
003 (:require [walkmap.utils :refer [=ish]]))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
004
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
005 (def ^:dynamic *sea-level*
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
006 "The sea level on heightmaps we're currently handling. If characters are to
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
007 be able to swin in the sea, we must model the sea bottom, so we need
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
008 heightmaps which cover at least the continental shelf. However, the sea
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
009 bottom is not walkable territory and can be culled from walkmaps.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
010
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
011 **Note** must be a floating point number. `(= 0 0.0)` returns `false`!"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
012 0.0)
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
013
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
014 (defn ocean?
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
015 "Of a `facet`, is the altitude of every vertice equal to `*sea-level*`?"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
016 [facet]
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
017 (every?
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
018 #(=ish % *sea-level*)
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
019 (map :z (:vertices facet))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
020
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
021 (defn cull-ocean-facets
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
022 "Ye cannae walk on water. Remove all facets from this `stl` structure which
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
023 are at sea level."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
024 [stl]
|
||||
</span><br/>
|
||||
<span class="covered" title="10 out of 10 forms covered">
|
||||
025 (assoc stl :facets (remove ocean? (:facets stl))))
|
||||
</span><br/>
|
||||
</body>
|
||||
</html>
|
|
@ -8,91 +8,280 @@
|
|||
001 (ns walkmap.path
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
002 "Essentially the specification for things we shall consider to be path."
|
||||
002 "Essentially the specification for things we shall consider to be path.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
003 (:require [walkmap.polygon :refer [polygon?]]
|
||||
003 **Note that** for these purposes `path` means any continuous linear
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
004 [walkmap.vertex :refer [vertex?]]))
|
||||
004 feature, where such features specifically include watercourses."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
005 (:require [clojure.string :as s]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
006 [walkmap.edge :as e]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
007 [walkmap.polygon :refer [check-polygon polygon?]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
008 [walkmap.tag :refer [tag tags]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
009 [walkmap.utils :refer [check-kind-type check-kind-type-seq kind-type]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
010 [walkmap.vertex :refer [check-vertices vertex?]]))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
005
|
||||
011
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
006 (defn path?
|
||||
012 (defn path?
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
007 "True if `o` satisfies the conditions for a path. A path shall be a map
|
||||
013 "True if `o` satisfies the conditions for a path. A path shall be a map
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
008 having the key `:nodes`, whose value shall be a sequence of vertices as
|
||||
014 having the key `:vertices`, whose value shall be a sequence of vertices as
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
009 defined in `walkmap.vertex`."
|
||||
015 defined in `walkmap.vertex`."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
010 [o]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
011 (let
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
012 [v (:nodes o)]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 9 forms covered">
|
||||
013 (and
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
014 (seq? v)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 6 forms covered">
|
||||
015 (> (count v) 2)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
016 (every? vertex? v))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
017
|
||||
016 [o]
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
018 (defn polygon->path
|
||||
017 (let
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
019 "If `o` is a polygon, return an equivalent path. What's different about
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
018 [v (:vertices o)]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
020 a path is that in polygons there is an implicit edge between the first
|
||||
<span class="partial" title="19 out of 22 forms covered">
|
||||
019 (and
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
021 vertex and the last. In paths, there isn't, so we need to add that
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
020 (seq? v)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
022 edge explicitly.
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
021 (> (count v) 1)
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
022 (every? vertex? v)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
023 (:walkmap.id/id o)
|
||||
</span><br/>
|
||||
<span class="covered" title="9 out of 9 forms covered">
|
||||
024 (or (nil? (:kind o)) (= (:kind o) :path)))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
023
|
||||
025
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
026 (defn path
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
024 If `o` is not a polygon, will throw an exception."
|
||||
027 "Return a path constructed from these `vertices`."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
025 [o]
|
||||
028 [& vertices]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
026 (if
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
029 (if
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
027 (polygon? o)
|
||||
<span class="covered" title="30 out of 30 forms covered">
|
||||
030 (> (count (check-vertices vertices)) 1)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 19 forms covered">
|
||||
028 (assoc (dissoc o :vertices) :nodes (concat (:vertices o) (list (first (:vertices o)))))
|
||||
<span class="covered" title="11 out of 11 forms covered">
|
||||
031 {:vertices vertices :walkmap.id/id (keyword (gensym "path")) :kind :path}
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
029 (throw (Exception. "Not a polygon!"))))
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
032 (throw (IllegalArgumentException. "Path must have more than one vertex."))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
030
|
||||
033
|
||||
</span><br/>
|
||||
<span class="covered" title="22 out of 22 forms covered">
|
||||
034 (defmacro check-path
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
035 "If `o` is not a path, throw an `IllegalArgumentException` with an
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
036 appropriate message; otherwise, returns `o`. Macro, so exception is thrown
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
037 from the calling function."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
038 [o]
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
039 `(check-kind-type ~o path? :path))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
040
|
||||
</span><br/>
|
||||
<span class="covered" title="21 out of 21 forms covered">
|
||||
041 (defmacro check-paths
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
042 "If `o` is not a sequence of paths, throw an `IllegalArgumentException` with an
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
043 appropriate message; otherwise, returns `o`. Macro, so exception is thrown
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
044 from the calling function."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
045 [o]
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
046 `(check-kind-type-seq ~o path? :path))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
047
|
||||
</span><br/>
|
||||
<span class="partial" title="1 out of 3 forms covered">
|
||||
048 (defn polygon->path
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
049 "If `o` is a polygon, return an equivalent path. What's different about
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
050 a path is that in polygons there is an implicit edge between the first
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
051 vertex and the last. In paths, there isn't, so we need to add that
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
052 edge explicitly.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
053
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
054 If `o` is not a polygon, will throw an exception."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
055 [o]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
056 ;; this is breaking, but I have NO IDEA why!
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
057 ;; (check-polygon o polygon? :polygon)
|
||||
</span><br/>
|
||||
<span class="covered" title="10 out of 10 forms covered">
|
||||
058 (assoc (dissoc o :vertices)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
059 :kind :path
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
060 ;; `concat` rather than `conj` because order matters.
|
||||
</span><br/>
|
||||
<span class="covered" title="12 out of 12 forms covered">
|
||||
061 :vertices (concat (:vertices o) (list (first (:vertices o))))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
062
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
063 (defn path->edges
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
064 "if `o` is a path, a polygon, or a sequence of vertices, return a sequence of
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
065 edges representing that path, polygon or sequence.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
066
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
067 Throws `IllegalArgumentException` if `o` is not a path, a polygon, or
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
068 sequence of vertices."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
069 [o]
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
070 (cond
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
071 (seq? o) (when
|
||||
</span><br/>
|
||||
<span class="partial" title="13 out of 14 forms covered">
|
||||
072 (and
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
073 (vertex? (first o))
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
074 (vertex? (first (rest o))))
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
075 (cons
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
076 ;; TODO: think about: when constructing an edge from a path, should the
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
077 ;; constructed edge be tagged with the tags of the path?
|
||||
</span><br/>
|
||||
<span class="covered" title="10 out of 10 forms covered">
|
||||
078 (e/edge (first o) (first (rest o)))
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
079 (path->edges (rest o))))
|
||||
</span><br/>
|
||||
<span class="covered" title="8 out of 8 forms covered">
|
||||
080 (path? o) (path->edges (:vertices o))
|
||||
</span><br/>
|
||||
<span class="covered" title="8 out of 8 forms covered">
|
||||
081 (polygon? o) (path->edges (polygon->path o))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
082 :else
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
083 (throw (IllegalArgumentException.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
084 "Not a path or sequence of vertices!"))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
085
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
086 (defn length
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
087 "Return the length of this path, in metres. **Note that**
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
088 1. This is not the same as the distance from the start to the end of the
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
089 path, which, except for absolutely straight paths, will be shorter;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
090 2. It is not even quite the same as the length of the path *as rendered*,
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
091 since paths will generally be rendered as spline curves."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
092 [path]
|
||||
</span><br/>
|
||||
<span class="partial" title="14 out of 24 forms covered">
|
||||
093 (reduce + (map e/length (path->edges (check-path path)))))
|
||||
</span><br/>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -11,49 +11,463 @@
|
|||
002 "Essentially the specification for things we shall consider to be polygons."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
003 (:require [walkmap.vertex :refer [vertex?]]))
|
||||
003 (:require [clojure.string :as s]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
004 [walkmap.edge :as e]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
005 [walkmap.tag :as t]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
006 [walkmap.utils :refer [check-kind-type
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
007 check-kind-type-seq
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
008 kind-type
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
009 not-yet-implemented]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
010 [walkmap.vertex :refer [check-vertex check-vertices vertex vertex?]]))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
004
|
||||
011
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
005 (defn polygon?
|
||||
012 (defn polygon?
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
006 "True if `o` satisfies the conditions for a polygon. A polygon shall be a
|
||||
013 "True if `o` satisfies the conditions for a polygon. A polygon shall be a
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
007 map which has a value for the key `:vertices`, where that value is a sequence
|
||||
014 map which has a value for the key `:vertices`, where that value is a sequence
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
008 of vertices."
|
||||
015 of vertices."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
009 [o]
|
||||
016 [o]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
010 (let
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
017 (let
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
018 [v (:vertices o)]
|
||||
</span><br/>
|
||||
<span class="partial" title="18 out of 22 forms covered">
|
||||
019 (and
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
020 (coll? v)
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
021 (> (count v) 2)
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
022 (every? vertex? v)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
023 (:walkmap.id/id o)
|
||||
</span><br/>
|
||||
<span class="covered" title="9 out of 9 forms covered">
|
||||
024 (or (nil? (:kind o)) (= (:kind o) :polygon)))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
025
|
||||
</span><br/>
|
||||
<span class="covered" title="22 out of 22 forms covered">
|
||||
026 (defmacro check-polygon
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
027 "If `o` is not a polygon, throw an `IllegalArgumentException` with an
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
028 appropriate message; otherwise, returns `o`. Macro, so exception is thrown
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
029 from the calling function."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
030 [o]
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
031 `(check-kind-type ~o polygon? :polygon))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
032
|
||||
</span><br/>
|
||||
<span class="covered" title="21 out of 21 forms covered">
|
||||
033 (defmacro check-polygons
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
034 "If `o` is not a sequence of polygons, throw an `IllegalArgumentException` with an
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
035 appropriate message; otherwise, returns `o`. Macro, so exception is thrown
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
036 from the calling function."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
037 [o]
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
038 `(check-kind-type-seq ~o polygon? :polygon))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
039
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
040 (defn triangle?
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
041 "True if `o` satisfies the conditions for a triangle. A triangle shall be a
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
042 polygon with exactly three vertices."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
043 [o]
|
||||
</span><br/>
|
||||
<span class="partial" title="5 out of 6 forms covered">
|
||||
044 (and
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
045 (coll? o)
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
046 (= (count (:vertices o)) 3)))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
047
|
||||
</span><br/>
|
||||
<span class="partial" title="25 out of 33 forms covered">
|
||||
048 (defmacro check-triangle
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
049 "If `o` is not a triangle, throw an `IllegalArgumentException` with an
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
050 appropriate message; otherwise, returns `o`. Macro, so exception is thrown
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
051 from the calling function."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
052 [o]
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
053 `(check-kind-type ~o triangle? :triangle))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
054
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
055 (defn polygon
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
056 "Return a polygon constructed from these `vertices`."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
057 [& vertices]
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
058 (if
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
059 (> (count vertices) 2)
|
||||
</span><br/>
|
||||
<span class="covered" title="30 out of 30 forms covered">
|
||||
060 {:vertices (check-vertices vertices)
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
061 :walkmap.id/id (keyword (gensym "poly"))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
062 :kind :polygon}
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
063 (throw (IllegalArgumentException.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
064 "A polygon must have at least 3 vertices."))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
065
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
066 (defn rectangle
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
067 "Return a rectangle, with edges aligned east-west and north-south, whose
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
068 south-west corner is the vertex `vsw` and whose north-east corner is the
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
069 vertex `vne`."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
070 [vsw vne]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
071 ;; we can actually create any rectangle in the xy plane based on two opposite
|
||||
</span><br/>
|
||||
<span class="covered" title="9 out of 9 forms covered">
|
||||
072 ;; corners, but the maths are a bit to advanced for me today. TODO: do it!
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 21 forms covered">
|
||||
073 (let [vnw (vertex (:x (check-vertex vsw))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 18 forms covered">
|
||||
074 (:y (check-vertex vne))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 20 forms covered">
|
||||
075 (/ (reduce + (map #(or (:z %) 0) [vsw vne])) 2))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
076 vse (vertex (:x vne)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
011 [v (:vertices o)]
|
||||
077 (:y vsw)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 9 forms covered">
|
||||
012 (and
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
013 (seq? v)
|
||||
<span class="not-covered" title="0 out of 20 forms covered">
|
||||
078 (/ (reduce + (map #(or (:z %) 0) [vsw vne])) 2))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 6 forms covered">
|
||||
014 (> (count v) 2)
|
||||
079 height-order (sort-by :z [vsw vne])]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
015 (every? vertex? v))))
|
||||
080 (t/tag
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
081 (assoc
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 6 forms covered">
|
||||
082 (polygon vsw vnw vne vse)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
083 :gradient
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 10 forms covered">
|
||||
084 (e/unit-vector (e/edge (first height-order) (last height-order)))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
085 :centre
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 18 forms covered">
|
||||
086 (vertex (+ (:x vsw) (/ (- (:x vne) (:x vsw)) 2))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 16 forms covered">
|
||||
087 (+ (:x vsw) (/ (- (:y vne) (:y vsw)) 2))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
088 (:z vse)))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
089 :rectangle)))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
016
|
||||
090
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
091 ;; (rectangle (vertex 1 2 3) (vertex 7 9 4))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
017
|
||||
092
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
093 (defn gradient
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
094 "Return a polygon like `triangle` but with a key `:gradient` whose value is a
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
095 unit vector representing the gradient across `triangle`."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
096 [triangle]
|
||||
</span><br/>
|
||||
<span class="covered" title="12 out of 12 forms covered">
|
||||
097 (let [order (sort #(max (:z %1) (:z %2))
|
||||
</span><br/>
|
||||
<span class="partial" title="8 out of 18 forms covered">
|
||||
098 (:vertices (check-triangle triangle)))
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
099 highest (first order)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
100 lowest (last order)]
|
||||
</span><br/>
|
||||
<span class="covered" title="10 out of 10 forms covered">
|
||||
101 (assoc triangle :gradient (e/unit-vector (e/edge lowest highest)))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
102
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
103 (defn triangle-centre
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
104 "Return a canonicalised `facet` (i.e. a triangular polygon) with an added
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
105 key `:centre` whose value represents the centre of this facet in 3
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
106 dimensions. This only works for triangles, so is here not in
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
107 `walkmap.polygon`. It is an error (although no exception is currently
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
108 thrown) if the object past is not a triangular polygon."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
109 [facet]
|
||||
</span><br/>
|
||||
<span class="partial" title="9 out of 19 forms covered">
|
||||
110 (let [vs (:vertices (check-triangle facet))
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
111 v1 (first vs)
|
||||
</span><br/>
|
||||
<span class="covered" title="10 out of 10 forms covered">
|
||||
112 opposite (e/edge (nth vs 1) (nth vs 2))
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
113 oc (e/centre opposite)]
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
114 (assoc
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
115 facet
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
116 :centre
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
117 (vertex
|
||||
</span><br/>
|
||||
<span class="covered" title="16 out of 16 forms covered">
|
||||
118 (+ (:x v1) (* (- (:x oc) (:x v1)) 2/3))
|
||||
</span><br/>
|
||||
<span class="covered" title="16 out of 16 forms covered">
|
||||
119 (+ (:y v1) (* (- (:y oc) (:y v1)) 2/3))
|
||||
</span><br/>
|
||||
<span class="covered" title="16 out of 16 forms covered">
|
||||
120 (+ (:z v1) (* (- (:z oc) (:z v1)) 2/3))))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
121
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
122 (defn centre
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
123 [poly]
|
||||
</span><br/>
|
||||
<span class="covered" title="22 out of 22 forms covered">
|
||||
124 (case (count (:vertices (check-polygon poly)))
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
125 3 (triangle-centre poly)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
126 ;; else
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
127 (throw
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
128 (UnsupportedOperationException.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
129 "The general case of centre for polygons is not yet implemented."))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
130
|
||||
</span><br/>
|
||||
<span class="partial" title="4 out of 16 forms covered">
|
||||
131 (defmacro on2dtriangle?
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
132 "Is the projection of this `vertex` on the x, y plane within the
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
133 projection of this triangle on that plane?"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
134 [vertex poly]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
135 `(not-yet-implemented "on2d? for triangles."))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
136
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
137 (defn on2drectangle?
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
138 "Is the projection of this `vertex` on the x, y plane within the
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
139 projection of this rectangle on that plane?"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
140 [vertex rectangle]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 7 forms covered">
|
||||
141 (let [xo (sort-by :x (:vertices rectangle))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 6 forms covered">
|
||||
142 yo (sort-by :x (:vertices rectangle))]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
143 (and
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 15 forms covered">
|
||||
144 (< (:x (first xo)) (:x vertex) (:x (last xo)))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 14 forms covered">
|
||||
145 (< (:y (first yo)) (:y vertex) (:y (last yo))))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
146
|
||||
</span><br/>
|
||||
<span class="partial" title="4 out of 86 forms covered">
|
||||
147 (defmacro on2d?
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
148 "Is the projection of this `vertex` on the x, y plane within the
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
149 projection of this polygon `poly` on that plane?"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
150 [vertex poly]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
151 `(cond
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
152 (rectangle? ~poly) (on2drectangle? ~vertex ~poly)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
153 (triangle? ~poly) (on2dtriangle? ~vertex ~poly)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
154 :else
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
155 (not-yet-implemented "general case of on2d? for polygons.")))
|
||||
</span><br/>
|
||||
</body>
|
||||
</html>
|
||||
|
|
308
docs/cloverage/walkmap/read_svg.clj.html
Normal file
308
docs/cloverage/walkmap/read_svg.clj.html
Normal file
|
@ -0,0 +1,308 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<link rel="stylesheet" href="../coverage.css"/> <title> walkmap/read_svg.clj </title>
|
||||
</head>
|
||||
<body>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
001 (ns walkmap.read-svg
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
002 "Utility functions for scalable vector graphics (SVG) into walkmap
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
003 structures."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
004 (:require [clojure.data.zip :as dz]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
005 [clojure.data.zip.xml :as zx]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
006 [clojure.java.io :as io]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
007 [clojure.string :as s]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
008 [clojure.xml :as x]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
009 [clojure.zip :as z]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
010 [taoensso.timbre :as l]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
011 [walkmap.path :refer [path]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
012 [walkmap.tag :refer [tag]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
013 [walkmap.utils :refer [kind-type truncate]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
014 [walkmap.vertex :refer [vertex vertex?]]))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
015
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
016 (defn upper-case?
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
017 [s]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 6 forms covered">
|
||||
018 (every? #(Character/isUpperCase %) s))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
019
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
020 (defn match->vertex
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
021 [match-vector x y]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 6 forms covered">
|
||||
022 (when-not (empty? match-vector)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
023 (let [command (nth match-vector 1)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 6 forms covered">
|
||||
024 xcoord (read-string (nth match-vector 2))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 6 forms covered">
|
||||
025 ycoord (read-string (nth match-vector 3))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
026 ;; upper case command letters mean the coordinates that follow are
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
027 ;; absolute; lower case, relative.
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 9 forms covered">
|
||||
028 x' (if (upper-case? command) xcoord (+ x xcoord))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 9 forms covered">
|
||||
029 y' (if (upper-case? command) ycoord (+ y ycoord))]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 6 forms covered">
|
||||
030 (case (s/lower-case command)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 20 forms covered">
|
||||
031 ("m" "l") {:vertex (vertex x' y') :x x' :y y'}
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
032 nil))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
033
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
034 (defn command-string->vertices
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
035 "Return the destination of each successive line (`l`, `L`) and move (`m`, `M`)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
036 command in this string `s`, expected to be an SVG path command string."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
037 [s]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
038 (let [cmd-matcher ;; matches a 'command' in the string: a letter followed by
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
039 ;;spaces and numbers
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
040 (re-matcher #"[a-zA-Z][^a-zA-Z]*" s)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
041 seg-pattern ;; matches a command which initiates a move of the current
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
042 ;; position.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
043 #"([a-zA-Z]) +([-+]?[0-9]*\.?[0-9]+) +([-+]?[0-9]*\.?[0-9]+) +"]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 11 forms covered">
|
||||
044 (loop [match (re-find cmd-matcher)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
045 result []
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
046 x 0
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
047 y 0]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 8 forms covered">
|
||||
048 (if-not match
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 8 forms covered">
|
||||
049 (filter vertex? result)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 18 forms covered">
|
||||
050 (let [m (match->vertex (re-find seg-pattern match) x y)]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
051 (recur (re-find cmd-matcher) ;loop with 2 new arguments
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
052 (conj result (:vertex m))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
053 (or (:x m) x)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
054 (or (:y m) y)))))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
055
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
056 (defn path-elt->path
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
057 "Given the SVG path element `elt`, return a walkmap path structure
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
058 representing the line (`l`, `L`) and move (`m`, `M`) commands in
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
059 that path."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
060 [elt]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 7 forms covered">
|
||||
061 (if (= (:tag elt) :path)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 8 forms covered">
|
||||
062 (let [vs (command-string->vertices (-> elt :attrs :d))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 10 forms covered">
|
||||
063 p (when-not (empty? vs) (apply path vs))]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 11 forms covered">
|
||||
064 (if (and p (-> elt :attrs :class))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 14 forms covered">
|
||||
065 (tag p (map keyword (s/split (-> elt :attrs :class) #" ")))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
066 p))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
067 (throw (IllegalArgumentException.
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
068 (str "Must be an SVG `path` element: " elt)))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
069
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
070 (defn progeny
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
071 "Return all the nodes in the XML structure below this `elt` which match
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
072 this `predicate`."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
073 ;; the name `descendants` is bound in `clojure.core` for something quite
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
074 ;; different, and I chose not to rebind it.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
075 [elt predicate]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
076 (if
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 6 forms covered">
|
||||
077 (apply predicate (list elt))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
078 (list elt)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
079 (reduce
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
080 concat
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
081 (remove
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
082 empty?
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
083 (map
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
084 #(progeny % predicate)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
085 (:content elt))))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
086
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
087 (defn read-svg
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
088 ;; I tried to get this working with all the clever zip stuff in
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
089 ;; `clojure.zip`, `clojure.data.zip`, and so on. It would probably have
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
090 ;; been more elegant, but it kept crashing out of heap space on even
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
091 ;; quite small XML files. So I've implemented my own solution.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
092 ([file-name]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
093 (read-svg file-name nil))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
094 ([file-name map-kind]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 6 forms covered">
|
||||
095 (let [xml (x/parse (io/file file-name))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 10 forms covered">
|
||||
096 paths (progeny xml #(= (:tag %) :path))]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 7 forms covered">
|
||||
097 (remove nil? (map path-elt->path paths)))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
098
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
099 ;; (read-svg "resources/iom/manual_roads.svg")
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
100
|
||||
</span><br/>
|
||||
</body>
|
||||
</html>
|
611
docs/cloverage/walkmap/routing.clj.html
Normal file
611
docs/cloverage/walkmap/routing.clj.html
Normal file
|
@ -0,0 +1,611 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<link rel="stylesheet" href="../coverage.css"/> <title> walkmap/routing.clj </title>
|
||||
</head>
|
||||
<body>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
001 (ns walkmap.routing
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
002 "Finding optimal routes to traverse a map."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
003 (:require [clojure.math.numeric-tower :as m :only [expt]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
004 [clojure.set :refer [intersection]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
005 [walkmap.edge :as e]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
006 [walkmap.path :as p]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
007 [walkmap.polygon :as q]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
008 [walkmap.superstructure :as s]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
009 [walkmap.tag :as t]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
010 [walkmap.utils :as u]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
011 [walkmap.vertex :as v]))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
012
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
013 ;; Breadth first search is a good algorithm for terrain in which all steps have
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
014 ;; equal, but in our world (like the real world), they don't.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
015
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
016 ;; Reading list:
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
017 ;;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
018 ;; https://en.wikipedia.org/wiki/A*_search_algorithm
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
019 ;; https://www.redblobgames.com/pathfinding/a-star/introduction.html
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
020 ;; https://faculty.nps.edu/ncrowe/opmpaper2.htm
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
021 ;;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
022 ;; See https://simon-brooke.github.io/the-great-game/codox/Pathmaking.html
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
023
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
024 (def ^:dynamic *gradient-exponent*
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
025 "The exponent to be applied to `(inc (:z (unit-vector from to)))`
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
026 of a path segment to calculate the gradient-related part of the
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
027 cost of traversal. Dynamic, because we will want to tune this."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
028 2)
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
029
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
030 (def ^:dynamic *traversals-exponent*
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
031 "The (expected to be negative) exponent to be applied to the number
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
032 of traversals of a path to compute the road bonus. Paths more travelled by
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
033 should have larger bonuses, but not dramatically so - so the increase in
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
034 bonus needs to scale significantly less than linearly with the number
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
035 of traversals. Dynamic, because we will want to tune this."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
036 -2)
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
037
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
038 (defn traversable?
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
039 "True if this object can be considered as part of the walkmap."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
040 [object]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
041 (and
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
042 (or
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 6 forms covered">
|
||||
043 (and
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
044 (q/polygon? object)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
045 (:centre object))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
046 (p/path? object))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
047 (not (t/tagged? object :no-traversal))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 2 forms covered">
|
||||
048
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
049 (declare traversal-cost)
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
050
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
051 (defn vertices-traversal-cost
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
052 [vertices s]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
053 (reduce
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
054 +
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
055 (map
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
056 #(traversal-cost %1 %2 s)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 25 forms covered">
|
||||
057 (v/check-vertices vertices)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
058 (rest vertices))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
059
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
060 (defn path-traversal-cost
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
061 [path s]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 21 forms covered">
|
||||
062 (vertices-traversal-cost (:vertices (p/check-path path)) s))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
063
|
||||
</span><br/>
|
||||
<span class="partial" title="1 out of 2 forms covered">
|
||||
064 (defn barriers-crossed
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
065 "Search superstructure `s` and return a sequence of barriers, if any, which
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
066 obstruct traversal from vertex `from` to vertex `to`."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
067 [from to s]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
068 ;; TODO: implement
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
069 '())
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
070
|
||||
</span><br/>
|
||||
<span class="partial" title="1 out of 2 forms covered">
|
||||
071 (defn crossing-penalty
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 9 forms covered">
|
||||
072 "TODO: should return the cost of crossing this `barrier`, initially mainly
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
073 a watercourse, on the axis from vertex `from` to vertex `to`. in the context
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
074 of superstructure `s`. If there's a bridge, ferry or other crossing mechanism
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
075 in `s` at the intersection of the vertex and the barrier, then the penalty
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
076 should be substantially less than it would otherwise be."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
077 [barrier from to s]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
078 ;; TODO: implement
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
079 0)
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
080
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
081 (defn gradient-cost
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
082 "Compute the per-unit-distance cost of traversing this `edge`."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
083 [edge]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 6 forms covered">
|
||||
084 (let [g (:z (e/unit-vector edge))]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
085 (if (pos? g)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 6 forms covered">
|
||||
086 (m/expt (inc g) *gradient-exponent*)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
087 1)))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
088
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
089 ;; (gradient-cost (e/edge (v/vertex 0 0 0) (v/vertex 0 1 0)))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
090 ;; (gradient-cost (e/edge (v/vertex 0 0 0) (v/vertex 0 2 0)))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
091 ;; (gradient-cost (e/edge (v/vertex 0 0 0) (v/vertex 0 1 1)))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
092 ;; (gradient-cost (e/edge (v/vertex 0 0 0) (v/vertex 0 2 1)))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
093 ;; (gradient-cost (e/edge (v/vertex 0 0 0) (v/vertex 0 1 0.0001)))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
094
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
095 (defn best-road
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
096 "Find the best traversable path which links the vertices `from` and `to`
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
097 in this superstructure `s`, or nil if there are none."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
098 [from to s]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 9 forms covered">
|
||||
099 (let [f (fn [v] (set (s/touching v p/path? s)))]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
100 (first
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
101 (sort-by
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
102 ;;; I... chose the path more travelled by.
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 8 forms covered">
|
||||
103 #(or (:traversals %) 0)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 11 forms covered">
|
||||
104 (filter traversable? (intersection (f from) (f to)))))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
105
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
106 (defn road-bonus
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
107 "Calculate the road bonus of the edge represented by the vertices `from`,
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
108 `to`, in the context of the superstructure `s`. Obviously there only is
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
109 such a bonus if there actually is an existing thoroughfare to use. Road
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
110 bonuses scale with some fractional exponent of the number of traversals
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
111 which have been made of the road segment in question."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
112 [from to s]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 6 forms covered">
|
||||
113 (let [best (best-road from to s)]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
114 (when (:traversals best)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 6 forms covered">
|
||||
115 (m/expt (:traversals best) *traversals-exponent*))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
116
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
117 (defn traversal-cost
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
118 "Return the traversal cost of the edge represented by the vertices `from`,
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
119 `to`, in the context of the superstructure `s`. It is legitimate to pass
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
120 `nil` as the `to` argument, in which case the result will be zero, in order
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
121 to allow `reduce` to be used to compute total path costs."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
122 [from to s]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
123 (if (nil? to)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
124 0
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
125 (let [edge (e/edge from to)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
126 distance (e/length edge)]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
127 (/
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
128 (+
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
129 (* distance
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
130 (gradient-cost edge))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
131 (reduce +
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
132 (map
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 7 forms covered">
|
||||
133 #(crossing-penalty [% from to s])
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
134 (barriers-crossed from to s))))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 10 forms covered">
|
||||
135 (or (road-bonus from to s) 1)))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
136
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
137 ;; (def p '({:x 1.40625, :y 0, :kind :vertex, :walkmap.id/id :vert_1-40625_0}
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
138 ;; {:x 1.40625, :y -10.703125, :kind :vertex, :walkmap.id/id :vert_1-40625_-10-703125}
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
139 ;; {:x 7.578125, :y -10.703125, :kind :vertex, :walkmap.id/id :vert_7-578125_-10-703125}
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
140 ;; {:x 7.578125, :y 0, :kind :vertex, :walkmap.id/id :vert_7-578125_0}
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
141 ;; {:x 2.171875, :y -0.765625, :kind :vertex, :walkmap.id/id :vert_2-171875_-0-765625}
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
142 ;; {:x 6.8125, :y -0.765625, :kind :vertex, :walkmap.id/id :vert_6-8125_-0-765625}))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
143 ;; (v/check-vertices p)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
144 ;; (def p' (p/path p))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
145
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
146 ;; (traversal-cost (first p) (nth p 1) {})
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
147 ;; (vertices-traversal-cost p {})
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
148 ;; (path-traversal-cost (p/path p))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
149
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
150 (defn extend-frontier
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
151 "Return a sequence like `frontier` with all of these `candidates` which are
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
152 not already members either of `frontier` or of `rejects` appended."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
153 ([frontier candidates]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
154 (extend-frontier frontier candidates nil))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
155 ([frontier candidates rejects]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
156 (if
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
157 (empty? frontier)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
158 candidates
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 7 forms covered">
|
||||
159 (let [fs (set (concat frontier rejects))]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 7 forms covered">
|
||||
160 (concat frontier (remove fs candidates))))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
161
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
162 ;; (extend-frontier '(1 2 3 4 5) '(7 3 6 2 9 8) '(6 8))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
163 ;; (extend-frontier '(1 2 3 4 5) '(7 3 6 2 9 8))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
164 ;; (extend-frontier '(1 2 3 4 5) '())
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
165 ;; (extend-frontier '(1 2 3 4 5) nil)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
166 ;; (extend-frontier nil '(1 2 3 4 5))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
167
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
168 (defn route
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
169 ;; NOT YET GOOD ENOUGH! Simple breadth first, and although it will
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
170 ;; reach the goal
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
171 ([from to s search-radius]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
172 (loop [f from
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
173 t to
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
174 frontier (extend-frontier
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
175 nil
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
176 (s/neighbour-ids
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 6 forms covered">
|
||||
177 (s/nearest s from :centre search-radius)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
178 traversable?
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
179 s))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
180 visited nil
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
181 track nil]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 7 forms covered">
|
||||
182 (let [here (s/retrieve (first frontier) s)]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
183 (cond
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 11 forms covered">
|
||||
184 (< (e/length (e/edge (:centre here)) to) search-radius)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
185 ;; close enough
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 9 forms covered">
|
||||
186 (apply p/path (cons (:centre here) track))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
187 (empty? (rest frontier))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
188 ;; failed
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
189 nil
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
190 :else
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
191 (recur
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
192 f
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
193 t
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
194 (extend-frontier
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
195 (rest frontier)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
196 (s/neighbour-ids here traversable? s)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
197 visited)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
198 (cons here visited)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
199 ;; this is going to be wrong, and I need to think about how to fix.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
200 (cons here track)))))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
201
|
||||
</span><br/>
|
||||
</body>
|
||||
</html>
|
|
@ -23,364 +23,604 @@
|
|||
006 [org.clojars.smee.binary.core :as b]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
007 [taoensso.timbre :as l :refer [info error spy]]
|
||||
007 [taoensso.timbre :as l]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
008 [walkmap.polygon :refer [polygon?]])
|
||||
008 [walkmap.edge :as e]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
009 (:import org.clojars.smee.binary.core.BinaryIO
|
||||
009 [walkmap.ocean :as o]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
010 java.io.DataInput))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
011
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
012 (defn stl?
|
||||
010 [walkmap.polygon :refer [centre gradient polygon?]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
013 "True if `o` is recogniseable as an STL structure. An STL structure must
|
||||
011 [walkmap.superstructure :refer [store]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
014 have a key `:facets`, whose value must be a sequence of polygons; and
|
||||
012 [walkmap.tag :refer [tag]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
015 may have a key `:header` whose value should be a string, and/or a key
|
||||
013 [walkmap.utils :as u]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
016 `:count`, whose value should be a positive integer.
|
||||
014 [walkmap.vertex :as v])
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
015 (:import org.clojars.smee.binary.core.BinaryIO
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
016 java.io.DataInput))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
017
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
018 If `verify-count?` is passed and is not `false`, verify that the value of
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
018 (defn stl?
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
019 the `:count` header is equal to the number of facets."
|
||||
019 "True if `o` is recogniseable as an STL structure. An STL structure must
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
020 ([o]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
021 (stl? o false))
|
||||
020 have a key `:facets`, whose value must be a sequence of polygons; and
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
022 ([o verify-count?]
|
||||
021 may have a key `:header` whose value should be a string, and/or a key
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 22 forms covered">
|
||||
023 (and
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
024 (map? o)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
025 (:facets o)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 6 forms covered">
|
||||
026 (every? polygon? (:facets o))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 10 forms covered">
|
||||
027 (if (:header o) (string? (:header o)) true)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 10 forms covered">
|
||||
028 (if (:count o) (integer? (:count o)) true)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 11 forms covered">
|
||||
029 (if verify-count? (= (:count o) (count (:facets o))) true))))
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
022 `:count`, whose value should be a positive integer.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
030
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
031 (def vect
|
||||
023
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
032 "A codec for vectors within a binary STL file."
|
||||
</span><br/>
|
||||
<span class="covered" title="8 out of 8 forms covered">
|
||||
033 (b/ordered-map
|
||||
024 If `verify-count?` is passed and is not `false`, verify that the value of
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
034 :x :float-le
|
||||
025 the `:count` header is equal to the number of facets."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
035 :y :float-le
|
||||
026 ([o]
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
027 (stl? o false))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
036 :z :float-le))
|
||||
028 ([o verify-count?]
|
||||
</span><br/>
|
||||
<span class="partial" title="20 out of 26 forms covered">
|
||||
029 (and
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
030 (map? o)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
031 (:facets o)
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
032 (every? polygon? (:facets o))
|
||||
</span><br/>
|
||||
<span class="partial" title="9 out of 10 forms covered">
|
||||
033 (if (:header o) (string? (:header o)) true)
|
||||
</span><br/>
|
||||
<span class="partial" title="9 out of 10 forms covered">
|
||||
034 (if (:count o) (integer? (:count o)) true)
|
||||
</span><br/>
|
||||
<span class="partial" title="14 out of 15 forms covered">
|
||||
035 (or (nil? (:kind o)) (= (:kind o) :stl))
|
||||
</span><br/>
|
||||
<span class="partial" title="1 out of 11 forms covered">
|
||||
036 (if verify-count? (= (:count o) (count (:facets o))) true))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
037
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
038 (def facet
|
||||
038 (def vect
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
039 "A codec for a facet (triangle) within a binary STL file."
|
||||
039 "A codec for vectors within a binary STL file."
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
<span class="covered" title="8 out of 8 forms covered">
|
||||
040 (b/ordered-map
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
041 :normal vect
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
042 :vertices [vect vect vect]
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
041 :x :float-le
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
043 :abc :ushort-le))
|
||||
042 :y :float-le
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
043 :z :float-le))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
044
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
045 (def binary-stl
|
||||
045 (def facet
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
046 "A codec for binary STL files"
|
||||
046 "A codec for a facet (triangle) within a binary STL file."
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
047 (b/ordered-map
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
048 :header (b/string "ISO-8859-1" :length 80) ;; for the time being we neither know nor care what's in this.
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
048 :normal vect
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
049 :vertices [vect vect vect]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
049 :count :uint-le
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
050 :facets (b/repeated facet)))
|
||||
050 :abc :ushort-le))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
051
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
052 (defn decode-binary-stl
|
||||
052 (def binary-stl
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
053 "Parse a binary STL file from this `filename` and return an STL structure
|
||||
053 "A codec for binary STL files"
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
054 (b/ordered-map
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
055 :header (b/string "ISO-8859-1" :length 80) ;; for the time being we neither know nor care what's in this.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
054 representing its contents.
|
||||
056 :count :uint-le
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
057 :facets (b/repeated facet)))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
055
|
||||
058
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
059 (defn canonicalise
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
056 **NOTE** that we've no way of verifying that the input file is binary STL
|
||||
060 "Objects read in from STL won't have all the keys/values we need them to have.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
057 data, if it is not this will run but will return garbage."
|
||||
061 `o` may be a map (representing a facet or a vertex), or a sequence of such maps;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
058 [filename]
|
||||
062 if it isn't recognised it is at present just returned unchanged. `map-kind`, if
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
059 (let [in (io/input-stream filename)]
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
063 passed, must be a keyword indicating the value represented by the `z` axis
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
060 (b/decode binary-stl in)))
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
064 (defaults to `:height`). It is an error, and an exception will be thrown, if
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
061
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
065 `map-kind` is not a keyword."
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
066 ([o] (canonicalise o :height))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
067 ([o map-kind]
|
||||
</span><br/>
|
||||
<span class="covered" title="9 out of 9 forms covered">
|
||||
068 (canonicalise o map-kind (v/vertex 1 1 1)))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
069 ([o map-kind scale-vertex]
|
||||
</span><br/>
|
||||
<span class="partial" title="2 out of 3 forms covered">
|
||||
070 (when-not
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
071 (keyword? map-kind)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
072 (throw (IllegalArgumentException.
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 12 forms covered">
|
||||
073 (u/truncate (str "Must be a keyword: " (or map-kind "nil")) 80))))
|
||||
</span><br/>
|
||||
<span class="partial" title="4 out of 5 forms covered">
|
||||
074 (cond
|
||||
</span><br/>
|
||||
<span class="partial" title="19 out of 20 forms covered">
|
||||
075 (and (coll? o) (not (map? o))) (map #(canonicalise % map-kind) o)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
076 ;; if it has :facets it's an STL structure, but it doesn't yet conform to `stl?`
|
||||
</span><br/>
|
||||
<span class="covered" title="10 out of 10 forms covered">
|
||||
077 (:facets o) (assoc o
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
078 :kind :stl
|
||||
</span><br/>
|
||||
<span class="partial" title="11 out of 12 forms covered">
|
||||
079 :walkmap.id/id (or (:walkmap.id/id o) (keyword (gensym "stl")))
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
080 :facets (canonicalise (:facets o) map-kind))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
081 ;; if it has :vertices it's a polygon, but it may not yet conform to
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
082 ;; `polygon?`
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
083 (:vertices o) (let [f (gradient
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
062 (defn- vect->str [prefix v]
|
||||
084 (centre
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
085 (tag
|
||||
</span><br/>
|
||||
<span class="covered" title="7 out of 7 forms covered">
|
||||
086 (assoc o
|
||||
</span><br/>
|
||||
<span class="partial" title="4 out of 5 forms covered">
|
||||
087 :walkmap.id/id (or
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
088 (:walkmap.id/id o)
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
089 (keyword (gensym "poly")))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
090 :kind :polygon
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
091 :vertices (canonicalise
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
092 (:vertices o)
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
093 map-kind))
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
094 :facet map-kind)))]
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
095 (if (o/ocean? f)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
096 (tag f :ocean :no-traversal)
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
097 f))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
098 ;; if it has a value for :x it's a vertex, but it may not yet conform
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
099 ;; to `vertex?`; it should also be scaled using the scale-vertex, if any.
|
||||
</span><br/>
|
||||
<span class="covered" title="7 out of 7 forms covered">
|
||||
100 (:x o) (let [c (v/canonicalise o)]
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
101 (if scale-vertex
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
102 (v/vertex* c scale-vertex)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
103 c))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
104 ;; shouldn't happen
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
105 :else o)))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
106
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
107 (defn decode-binary-stl
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
108 "Parse a binary STL file from this `filename` and return an STL structure
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
109 representing its contents. `map-kind`, if passed, must be a keyword
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
110 or sequence of keywords indicating the semantic value represented by the `z`
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
111 axis (defaults to `:height`).
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
112
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
113 If `superstructure` is supplied and is a map, the generated STL structure
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
114 will be stored in that superstructure, which will be returned.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
115
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
116 If `scale-vertex` is supplied, it must be a three dimensional vertex (i.e.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
117 the `:z` key must have a numeric value) representing the amount by which
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
118 each of the vertices read from the STL will be scaled.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
119
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
120 It is an error, and an exception will be thrown, if `map-kind` is not a
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
121 keyword or sequence of keywords.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
122
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
123 **NOTE** that we've no way of verifying that the input file is binary STL
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
124 data, if it is not this will run but will return garbage."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
125 ([filename]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
126 (decode-binary-stl filename :height))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
127 ([filename map-kind]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
128 (when-not
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
129 (keyword? map-kind)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
130 (throw (IllegalArgumentException.
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 12 forms covered">
|
||||
131 (u/truncate (str "Must be a keyword: " (or map-kind "nil")) 80))))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
132 (decode-binary-stl filename map-kind nil))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
133 ([filename mapkind superstucture]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 10 forms covered">
|
||||
134 (decode-binary-stl filename mapkind superstucture (v/vertex 1 1 1)))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
135 ([filename map-kind superstructure scale-vertex]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
136 (let [in (io/input-stream filename)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 8 forms covered">
|
||||
137 stl (canonicalise (b/decode binary-stl in) map-kind scale-vertex)]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
138 (if
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
139 (map? superstructure)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
140 (store stl superstructure)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
141 stl))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
142
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
143 (defn- vect->str [prefix v]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 16 forms covered">
|
||||
063 (str prefix " " (:x v) " " (:y v) " " (:z v) "\n"))
|
||||
144 (str prefix " " (:x v) " " (:y v) " " (:z v) "\n"))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
064
|
||||
145
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
065 (defn- facet2str [tri]
|
||||
146 (defn- facet2str [tri]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
066 (str
|
||||
147 (str
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 6 forms covered">
|
||||
067 (vect->str "facet normal" (:normal tri))
|
||||
148 (vect->str "facet normal" (:normal tri))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
068 "outer loop\n"
|
||||
149 "outer loop\n"
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
150 (s/join
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
069 (apply str
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
070 (map
|
||||
151 (map
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
071 #(vect->str "vertex" %)
|
||||
152 #(vect->str "vertex" %)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
072 (:vertices tri)))
|
||||
153 (:vertices tri)))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
073 "endloop\nendfacet\n"))
|
||||
154 "endloop\nendfacet\n"))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
074
|
||||
155
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
075 (defn stl->ascii
|
||||
156 (defn stl->ascii
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
076 "Return as a string an ASCII rendering of the `stl` structure."
|
||||
157 "Return as a string an ASCII rendering of the `stl` structure."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
077 ([stl]
|
||||
158 ([stl]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
078 (stl->ascii stl "unknown"))
|
||||
159 (stl->ascii stl "unknown"))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
079 ([stl solidname]
|
||||
160 ([stl solidname]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 6 forms covered">
|
||||
080 (str
|
||||
161 (str
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
081 "solid "
|
||||
162 "solid "
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
082 solidname
|
||||
163 solidname
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
083 (s/trim (:header stl))
|
||||
164 (s/trim (:header stl))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
084 "\n"
|
||||
165 "\n"
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
085 (apply
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
086 str
|
||||
166 (s/join
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
087 (map
|
||||
167 (map
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
088 facet2str
|
||||
168 facet2str
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
089 (:facets stl)))
|
||||
169 (:facets stl)))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
090 "endsolid "
|
||||
170 "endsolid "
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
091 solidname
|
||||
171 solidname
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
092 "\n")))
|
||||
172 "\n")))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
093
|
||||
173
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
094 (defn write-ascii-stl
|
||||
174 (defn write-ascii-stl
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
095 "Write an `stl` structure as read by `decode-binary-stl` to this
|
||||
175 "Write an `stl` structure as read by `decode-binary-stl` to this
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
096 `filename` as ASCII encoded STL."
|
||||
176 `filename` as ASCII encoded STL."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
097 ([filename stl]
|
||||
177 ([filename stl]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
098 (let [b (fs/base-name filename true)]
|
||||
178 (let [b (fs/base-name filename true)]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
099 (write-ascii-stl
|
||||
179 (write-ascii-stl
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
100 filename stl
|
||||
180 filename stl
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 15 forms covered">
|
||||
101 (subs b 0 (or (s/index-of b ".") (count b))))))
|
||||
181 (subs b 0 (or (s/index-of b ".") (count b))))))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
102 ([filename stl solidname]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 16 forms covered">
|
||||
103 (l/debug "Solid name is " solidname)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
104 (spit
|
||||
182 ([filename stl solidname]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
105 filename
|
||||
183 (l/debug "Solid name is " solidname)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
184 (spit
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
185 filename
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
106 (stl->ascii stl solidname))))
|
||||
186 (stl->ascii stl solidname))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
107
|
||||
187
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
108 (defn binary-stl-to-ascii
|
||||
188 (defn binary-stl-to-ascii
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
109 "Convert the binary STL file indicated by `in-filename`, and write it to
|
||||
189 "Convert the binary STL file indicated by `in-filename`, and write it to
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
110 `out-filename`, if specified; otherwise, to a file with the same basename
|
||||
190 `out-filename`, if specified; otherwise, to a file with the same basename
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
111 as `in-filename` but the extension `.ascii.stl`."
|
||||
191 as `in-filename` but the extension `.ascii.stl`."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
112 ([in-filename]
|
||||
192 ([in-filename]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
113 (let [[_ ext] (fs/split-ext in-filename)]
|
||||
193 (let [[_ ext] (fs/split-ext in-filename)]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
114 (binary-stl-to-ascii
|
||||
194 (binary-stl-to-ascii
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
115 in-filename
|
||||
195 in-filename
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
116 (str
|
||||
196 (str
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
117 (subs
|
||||
197 (subs
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
118 in-filename
|
||||
198 in-filename
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
119 0
|
||||
199 0
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
120 (or
|
||||
200 (or
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
121 (s/last-index-of in-filename ".")
|
||||
201 (s/last-index-of in-filename ".")
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
122 (count in-filename)))
|
||||
202 (count in-filename)))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
123 ".ascii"
|
||||
203 ".ascii"
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
124 ext))))
|
||||
204 ext))))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
125 ([in-filename out-filename]
|
||||
205 ([in-filename out-filename]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 6 forms covered">
|
||||
126 (write-ascii-stl out-filename (decode-binary-stl in-filename))))
|
||||
206 (write-ascii-stl out-filename (decode-binary-stl in-filename))))
|
||||
</span><br/>
|
||||
</body>
|
||||
</html>
|
||||
|
|
824
docs/cloverage/walkmap/superstructure.clj.html
Normal file
824
docs/cloverage/walkmap/superstructure.clj.html
Normal file
|
@ -0,0 +1,824 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<link rel="stylesheet" href="../coverage.css"/> <title> walkmap/superstructure.clj </title>
|
||||
</head>
|
||||
<body>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
001 (ns walkmap.superstructure
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
002 "single indexing structure for walkmap objects"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
003 (:require [clojure.walk :refer [postwalk]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
004 [taoensso.timbre :as l]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
005 [walkmap.edge :refer [edge length]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
006 [walkmap.path :as p]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
007 [walkmap.polygon :as q]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
008 [walkmap.utils :as u]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
009 [walkmap.vertex :as v]))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
010
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
011 ;; TODO: Think about reification/dereification. How can we cull a polygon, if
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
012 ;; some vertices still index it? I *think* that what's needed is that when
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
013 ;; we store something in the superstructure, we replace all its vertices (and
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
014 ;; other dependent structures, if any with their ids - as well as, obviously,
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
015 ;; adding/merging those vertices/dependent structures into the superstructure
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
016 ;; as first class objects in themselves. That means, for each identified thing,
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
017 ;; the superstructure only contains one copy of it.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
018 ;;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
019 ;; The question then is, when we want to do things with those objects, do we
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
020 ;; exteract a copy with its dependent structures fixed back up (reification),
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
021 ;; or do we indirect through the superstructure every time we want to access
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
022 ;; them? In a sense, the copy in the superstructure is the 'one true copy',
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
023 ;; but it may become very difficult then to have one true copy of the
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
024 ;; superstructure - unless we replace the superstructure altogether with a
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
025 ;; database, which may be the Right Thing To Do.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
026
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
027 (def vertex-index ::vertex-index)
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
028
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
029 (defn vertices
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
030 "If `o` is an object with vertices, return those vertices, else nil."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
031 [o]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
032 (when (map? o)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
033 (reduce
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
034 concat
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
035 (remove
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
036 nil?
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
037 (map
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
038 #(cond
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 6 forms covered">
|
||||
039 (v/vertex? %) (list %)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 11 forms covered">
|
||||
040 (and (coll? %) (every? v/vertex? %)) %)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
041 (vals o))))))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
042 ;; (cond
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
043 ;; (v/vertex? o) (list o)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
044 ;; (q/polygon? o) (:vertices o)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
045 ;; (p/path? o) (:vertices o))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
046 ;; )
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
047
|
||||
</span><br/>
|
||||
<span class="partial" title="1 out of 5 forms covered">
|
||||
048 (defn index-vertex
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
049 "Return a superstructure like `s` in which object `o` is indexed by vertex
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
050 `v`. It is an error (and an exception may be thrown) if
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 2 forms covered">
|
||||
051
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
052 1. `s` is not a map;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
053 2. `o` is not a map;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
054 3. `o` does not have a value for the key `:walkmap.id/id`;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
055 4. `v` is not a vertex."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
056 [s o v]
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
057 (if-not (v/vertex? o)
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
058 (if (:walkmap.id/id o)
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
059 (if (v/vertex? v)
|
||||
</span><br/>
|
||||
<span class="covered" title="9 out of 9 forms covered">
|
||||
060 (let [vi (or (::vertex-index s) {})
|
||||
</span><br/>
|
||||
<span class="partial" title="9 out of 10 forms covered">
|
||||
061 current (or (vi (:walkmap.id/id v)) {})]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
062 ;; deep-merge doesn't merge sets, only maps; so at this
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
063 ;; stage we need to build a map.
|
||||
</span><br/>
|
||||
<span class="covered" title="15 out of 15 forms covered">
|
||||
064 (assoc vi (:walkmap.id/id v) (assoc current (:walkmap.id/id o) (:walkmap.id/id v))))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
065 (throw (IllegalArgumentException. "Not a vertex: " v)))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 9 forms covered">
|
||||
066 (throw (IllegalArgumentException. (u/truncate (str "No `:walkmap.id/id` value: " o) 80))))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
067 ;; it shouldn't actually be an error to try to index a vertex, but it
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
068 ;; also isn't useful to do so, so I'd be inclined to ignore it.
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
069 (::vertex-index s)))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
070
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
071 (defn index-vertices
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
072 "Return a superstructure like `s` in which object `o` is indexed by its
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
073 vertices. It is an error (and an exception may be thrown) if
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
074
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
075 1. `s` is not a map;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
076 2. `o` is not a map;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
077 3. `o` does not have a value for the key `:walkmap.id/id`."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
078 [s o]
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
079 (u/deep-merge
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
080 s
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
081 {::vertex-index
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
082 (reduce
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
083 u/deep-merge
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
084 {}
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
085 (map
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
086 #(index-vertex s o %)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
087 (:vertices o)))}))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
088
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
089 (defn in-retrieve
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
090 "Internal guts of `retrieve`, q.v. `x` can be anything; `s` must be a
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
091 walkmap superstructure. TODO: recursive, quite likely to blow the fragile
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
092 Clojure stack. Probably better to do this with `walk`, but I don't yet
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
093 understand that."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
094 [x s]
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
095 (cond
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
096 ;; if it's a keyword identifying something in s, retrieve that something.
|
||||
</span><br/>
|
||||
<span class="covered" title="7 out of 7 forms covered">
|
||||
097 (keyword? x) (if (s x)
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
098 (in-retrieve (s x) s)
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
099 x)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
100 ;; if it's a map, for every key which is not `:walkmap.id/id`, recurse.
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
101 (map? x) (let [v (reduce
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
102 (fn [m k]
|
||||
</span><br/>
|
||||
<span class="covered" title="10 out of 10 forms covered">
|
||||
103 (assoc m k (in-retrieve (x k) s)))
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
104 {}
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
105 (keys (dissoc x :walkmap.id/id)))
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
106 id (:walkmap.id/id x)]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
107 ;; if it has an id, bind it to that id in the returned value.
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
108 (if id
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
109 (assoc
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
110 v
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
111 :walkmap.id/id
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
112 (:walkmap.id/id x))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
113 v))
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
114 (set? x) x ;; TODO: should I search in sets for objects when storing?
|
||||
</span><br/>
|
||||
<span class="covered" title="11 out of 11 forms covered">
|
||||
115 (coll? x) (map #(in-retrieve % s) x)
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
116 :else x))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
117
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
118 (defn retrieve
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
119 "Retrieve the canonical representation of the object with this `id` from the
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
120 superstructure `s`."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
121 [id s]
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
122 (in-retrieve (id s) s))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
123
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
124 (defn in-store-find-objects
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
125 "Return an id -> object map of every object within `o`. Internal to
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
126 `in-store`, q.v. Use at your own peril."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
127 ([o]
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
128 (in-store-find-objects o {}))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
129 ([o s]
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
130 (l/debug "Finding objects in:" o)
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
131 (cond
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
132 (set? o) s ;; TODO: should I search in sets for objects when storing?
|
||||
</span><br/>
|
||||
<span class="covered" title="7 out of 7 forms covered">
|
||||
133 (map? o) (if (:walkmap.id/id o)
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
134 (assoc
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
135 (in-store-find-objects (vals o) s)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
136 (:walkmap.id/id o)
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
137 o)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 6 forms covered">
|
||||
138 (in-store-find-objects (vals o) s))
|
||||
</span><br/>
|
||||
<span class="covered" title="15 out of 15 forms covered">
|
||||
139 (coll? o) (reduce merge s (map #(in-store-find-objects % s) o))
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
140 :else s)))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
141
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
142 (defn in-store-replace-with-keys
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
143 "Return a copy of `o` in which each reified walkmap object within `o` has
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
144 been replaced with the `:walkmap.id/id` of that object. Internal to
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
145 `in-store`, q.v. Use at your own peril."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
146 [o]
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
147 (assoc
|
||||
</span><br/>
|
||||
<span class="covered" title="15 out of 15 forms covered">
|
||||
148 (postwalk #(or (:walkmap.id/id %) %) (dissoc o :walkmap.id/id))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
149 :walkmap.id/id
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
150 (:walkmap.id/id o)))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
151
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
152 ;; (in-store-replace-with-keys (p/path (v/vertex 0 0 0) (v/vertex 0 1 2) (v/vertex 3 3 3)))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
153 ;; (in-store-find-objects (p/path (v/vertex 0 0 0) (v/vertex 0 1 2) (v/vertex 3 3 3)))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
154
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
155 (defn store
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
156 "Return a superstructure like `s` with object `o` added. If only one
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
157 argument is supplied it will be assumed to represent `o` and a new
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
158 superstructure will be returned.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
159
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
160 It is an error (and an exception may be thrown) if
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
161
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
162 1. `s` is not a map;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
163 2. `o` is not a recognisable walkmap object"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
164 ([o]
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
165 (store o {}))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
166 ([o s]
|
||||
</span><br/>
|
||||
<span class="partial" title="5 out of 6 forms covered">
|
||||
167 (when-not (:walkmap.id/id o)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
168 (throw
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
169 (IllegalArgumentException.
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
170 (str "Not a walkmap object: no value for `:walkmap.id/id`: "
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
171 (u/kind-type o)))))
|
||||
</span><br/>
|
||||
<span class="partial" title="5 out of 6 forms covered">
|
||||
172 (when-not (map? s)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
173 (throw
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
174 (IllegalArgumentException.
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 6 forms covered">
|
||||
175 (str "Superstructure must be a map: " (u/kind-type s)))))
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
176 (assoc
|
||||
</span><br/>
|
||||
<span class="covered" title="10 out of 10 forms covered">
|
||||
177 (u/deep-merge s (in-store-find-objects o) (index-vertices s o))
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
178 (:walkmap.id/id o)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
179 (in-store-replace-with-keys o))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
180
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
181 (defn search-vertices
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
182 "Search superstructure `s` for vertices within the box defined by vertices
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
183 `minv` and `maxv`. Every coordinate in `minv` must have a lower value than
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
184 the equivalent coordinate in `maxv`. If `d2?` is supplied and not false,
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
185 search only in the x,y projection.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
186
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
187 **NOTE THAT** this depends on the fact that vertices do not currently
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
188 have properties which will be denormalised by `store`, and therefore do not
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
189 have to restored with `retrieve`. If properties are added to vertices
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
190 whose values are objects, then this will have to be rewritten."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
191 ([s minv maxv]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 6 forms covered">
|
||||
192 (search-vertices s minv maxv false))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
193 ([s minv maxv d2?]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 9 forms covered">
|
||||
194 (let [minv' (if d2? (assoc minv :z Double/NEGATIVE_INFINITY) minv)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 8 forms covered">
|
||||
195 maxv' (if d2? (assoc maxv :z Double/POSITIVE_INFINITY) maxv)]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
196 (filter
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
197 #(v/within-box? % minv maxv)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 12 forms covered">
|
||||
198 (filter #(= (:kind %) :vertex) (vals s))))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
199
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
200 (defn nearest
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
201 "Search superstructure `s` for the nearest object matching `filter-fn` to
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
202 the `target` vertex. Searches only with `radius` (slight misnomer, area
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
203 actually searched is a cube). Returns one object, or `nil` if no matching
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
204 object found.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
205
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
206 WARNING: currently only returns objects which have a defined `:centre`
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
207 (but most of the significant objects we have do)."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
208 [s target filter-fn radius]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
209 (let [minv (v/vertex
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 21 forms covered">
|
||||
210 (- (:x (v/check-vertex target)) radius)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 17 forms covered">
|
||||
211 (- (:y target) radius) (- (or (:z target) 0) radius))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
212 maxv (v/vertex
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 12 forms covered">
|
||||
213 (+ (:x target) 0.5) (+ (:y target) 0.5)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 11 forms covered">
|
||||
214 (+ (or (:z target) 0) 0.5))]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
215 ;; filter those objects with the filter function, then sort that list
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
216 ;; by the edge distance from the target to the `:centre` of the object
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
217 ;; and take the first
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
218 (first
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
219 (sort-by
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 8 forms covered">
|
||||
220 #(length (edge target (:centre %)))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
221 (filter
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
222 :centre
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 7 forms covered">
|
||||
223 (map #(retrieve % s)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
224 ;; for each vertex id in vids, get the objects associated with that id
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
225 ;; in the vertex index as a single flat list
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
226 (reduce
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
227 concat
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
228 (remove
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
229 nil?
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
230 (map
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 7 forms covered">
|
||||
231 #(-> s ::vertex-index % keys)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
232 ;; get all the vertex ids within radius of the target
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
233 (set
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
234 (map
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
235 :walkmap.id/id
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
236 (search-vertices s minv maxv))))))))))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
237
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
238 (defn touching
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
239 "Return a sequence of all objects in superstructure `s` which are
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
240 indexed as touching the vertex `v`."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
241 ([vertex s]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
242 (map
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
243 #(retrieve % s)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 25 forms covered">
|
||||
244 (set (-> s :vertex-index (:walkmap.id/id (v/check-vertex vertex)) keys))))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
245 ([vertex filter-fn s]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
246 (filter
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
247 filter-fn
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
248 (touching vertex s))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
249
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
250 (defn neighbours
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
251 "Return a sequence of all those objects in superstructure `s` which share
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
252 at least one vertex with `target`, and which are matched by `filter-fn`
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
253 if supplied."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
254 ([target s]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
255 (neighbours identity s))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
256 ([target filter-fn s]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
257 (remove
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
258 #(= target %)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
259 (reduce
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
260 concat
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
261 (remove
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
262 nil?
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 11 forms covered">
|
||||
263 (map #(touching % filter-fn s) (vertices target)))))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
264
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
265 (defn neighbour-ids
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
266 "Return a sequence of the ids all those objects in superstructure `s` which
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
267 share at least one vertex with `target`, and which are matched by
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
268 `filter-fn` if supplied."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
269 ([target s]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
270 (neighbour-ids target identity s))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
271 ([target filter-fn s]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 8 forms covered">
|
||||
272 (map :walkmap.id/id (neighbours target filter-fn s))))
|
||||
</span><br/>
|
||||
</body>
|
||||
</html>
|
|
@ -17,142 +17,322 @@
|
|||
004 (SVG)."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
005 (:require [clojure.string :as s]
|
||||
005 (:require [clojure.java.io :as io]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
006 [taoensso.timbre :as l :refer [info error spy]]
|
||||
006 [clojure.string :as s]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
007 [walkmap.polygon :refer [polygon?]]
|
||||
007 [clojure.xml :as x]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
008 [walkmap.vertex :refer [vertex?]]))
|
||||
008 [dali.io :as neatly-folded-clock]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
009 [hiccup.core :refer [html]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
010 [taoensso.timbre :as l :refer [info error spy]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
011 [walkmap.ocean :refer [cull-ocean-facets]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
012 [walkmap.polygon :refer [polygon?]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
013 [walkmap.stl :refer [decode-binary-stl]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
014 [walkmap.vertex :refer [vertex?]]))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
009
|
||||
015
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
010 (defn- facet->svg-poly
|
||||
016 (def ^:dynamic *preferred-svg-render*
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
011 [facet]
|
||||
017 "Mainly for debugging dali; switch SVG renderer to use. Expected values:
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
012 [:polygon
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
018 `:dali`, `:hiccup`."
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 20 forms covered">
|
||||
013 {:points (s/join " " (map #(str (:x %) "," (:y %)) (:vertices facet)))}])
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
019 :dali)
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
014
|
||||
020
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
021 (defn- facet->svg-poly
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
022 [facet]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
023 [:polygon
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 20 forms covered">
|
||||
024 {:points (s/join " " (map #(str (:x %) "," (:y %)) (:vertices facet)))}])
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
025
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
026 (defn- dali-facet->svg-poly
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
027 [facet]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
028 (vec
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
029 (cons
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
030 :polygon
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 16 forms covered">
|
||||
031 (map #(vec (list (:x %) (:y %))) (:vertices facet)))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
032
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
015 (defn stl->svg
|
||||
033 (defn dali-stl->svg
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
016 "Convert this in-memory `stl` structure, as read by `decode-binary-stl`, into
|
||||
034 "Format this `stl` as SVG for the `dali` renderer on a page with these
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
017 an in-memory hiccup representation of SVG structure, and return it."
|
||||
035 bounds."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
018 [stl]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
019 (let [minx (reduce
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
020 min
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
021 (map
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 9 forms covered">
|
||||
022 #(reduce min (map :x (:vertices %)))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
023 (:facets stl)))
|
||||
036 [stl minx maxx miny maxy]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
024 maxx (reduce
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
025 max
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
026 (map
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 9 forms covered">
|
||||
027 #(reduce max (map :x (:vertices %)))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
028 (:facets stl)))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
029 miny (reduce
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
030 min
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
031 (map
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 9 forms covered">
|
||||
032 #(reduce min (map :y (:vertices %)))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
033 (:facets stl)))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
034 maxy (reduce
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
035 max
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
036 (map
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 9 forms covered">
|
||||
037 #(reduce max (map :y (:vertices %)))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
038 (:facets stl)))]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
039 [:svg
|
||||
037 [:dali/page
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 8 forms covered">
|
||||
040 {:xmlns "http://www.w3.org/2000/svg"
|
||||
038 {:xmlns "http://www.w3.org/2000/svg"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
041 :version "1.2"
|
||||
039 :version "1.2"
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
042 :width (- maxx minx)
|
||||
040 :width (- maxx minx)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
043 :height (- maxy miny)
|
||||
041 :height (- maxy miny)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 11 forms covered">
|
||||
044 :viewBox (s/join " " (map str [minx miny maxx maxy]))}
|
||||
042 :viewBox (s/join " " (map str [minx miny maxx maxy]))}
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
045 (vec
|
||||
043 (vec
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
046 (cons
|
||||
044 (cons
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
047 :g
|
||||
045 :g
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
048 (map
|
||||
046 (map
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
049 facet->svg-poly
|
||||
047 dali-facet->svg-poly
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
050 (:facets stl))))]))
|
||||
048 (:facets stl))))])
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
049
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
050 (defn hiccup-stl->svg
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
051 "Format this `stl` as SVG for the `hiccup` renderer on a page with these
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
052 bounds."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
053 [stl minx maxx miny maxy]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
054 [:svg
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 8 forms covered">
|
||||
055 {:xmlns "http://www.w3.org/2000/svg"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
056 :version "1.2"
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
057 :width (- maxx minx)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
058 :height (- maxy miny)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 11 forms covered">
|
||||
059 :viewBox (s/join " " (map str [minx miny maxx maxy]))}
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
060 (vec
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
061 (cons
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
062 :g
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
063 (map
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
064 facet->svg-poly
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
065 (:facets stl))))])
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
066
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
067 (defn stl->svg
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
068 "Convert this in-memory `stl` structure, as read by `decode-binary-stl`, into
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
069 an in-memory hiccup representation of SVG structure, and return it."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
070 [stl]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
071 (let [minx (reduce
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
072 min
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
073 (map
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 9 forms covered">
|
||||
074 #(reduce min (map :x (:vertices %)))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
075 (:facets stl)))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
076 maxx (reduce
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
077 max
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
078 (map
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 9 forms covered">
|
||||
079 #(reduce max (map :x (:vertices %)))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
080 (:facets stl)))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
081 miny (reduce
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
082 min
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
083 (map
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 9 forms covered">
|
||||
084 #(reduce min (map :y (:vertices %)))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
085 (:facets stl)))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
086 maxy (reduce
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
087 max
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
088 (map
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 9 forms covered">
|
||||
089 #(reduce max (map :y (:vertices %)))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
090 (:facets stl)))]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
091 (l/info "Generating SVG for " *preferred-svg-render* " renderer")
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
092 (case *preferred-svg-render*
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 7 forms covered">
|
||||
093 :hiccup (hiccup-stl->svg stl minx maxx miny maxy)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 7 forms covered">
|
||||
094 :dali (dali-stl->svg stl minx maxx miny maxy)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
095 (throw (Exception. "Unexpected renderer value: " *preferred-svg-render*)))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
096
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
097 (defn binary-stl-file->svg
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
098 "Given only an `in-filename`, parse the indicated file, expected to be
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
099 binary STL, and return an equivalent SVG structure. Given both `in-filename`
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
100 and `out-filename`, as side-effect write the SVG to the indicated output file."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
101 ([in-filename]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 7 forms covered">
|
||||
102 (stl->svg (cull-ocean-facets (decode-binary-stl in-filename))))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
103 ([in-filename out-filename]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
104 (let [s (binary-stl-file->svg in-filename)]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
105 (l/info "Emitting SVG with " *preferred-svg-render* " renderer")
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
106 (case *preferred-svg-render*
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
107 :dali (neatly-folded-clock/render-svg s out-filename)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 8 forms covered">
|
||||
108 :hiccup (spit out-filename (html s))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
109 (throw (Exception. "Unexpected renderer value: " *preferred-svg-render*)))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
110 s)))
|
||||
</span><br/>
|
||||
</body>
|
||||
</html>
|
||||
|
|
212
docs/cloverage/walkmap/tag.clj.html
Normal file
212
docs/cloverage/walkmap/tag.clj.html
Normal file
|
@ -0,0 +1,212 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<link rel="stylesheet" href="../coverage.css"/> <title> walkmap/tag.clj </title>
|
||||
</head>
|
||||
<body>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
001 (ns walkmap.tag
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
002 "Code for tagging, untagging, and finding tags on objects. Note the use of
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
003 the namespaced keyword, `:walkmap.tag/tags`, denoted in this file `::tags`.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
004 This is in an attempt to avoid name clashes with other uses of this key."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
005 (:require [clojure.set :refer [difference union]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
006 [taoensso.timbre :as l]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
007 [walkmap.utils :refer [kind-type]]))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
008
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
009 (defn tagged?
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
010 "True if this `object` is tagged with each of these `tags`. It is an error
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
011 (and an exception will be thrown) if
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
012
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
013 1. `object` is not a map;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
014 2. any of `tags` is not a keyword."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
015 [object & tags]
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
016 (when-not (map? object)
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
017 (throw (IllegalArgumentException.
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
018 (str "Must be a map: " (kind-type object)))))
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
019 (let [tags' (flatten tags)]
|
||||
</span><br/>
|
||||
<span class="covered" title="7 out of 7 forms covered">
|
||||
020 (when-not (every? keyword? tags')
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
021 (throw (IllegalArgumentException.
|
||||
</span><br/>
|
||||
<span class="covered" title="7 out of 7 forms covered">
|
||||
022 (str "Must be keywords: " (map kind-type tags')))))
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
023 (let [ot (::tags object)]
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
024 (and
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
025 (set? ot)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
026 (every? ot tags')))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
027
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
028 (defn tag
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
029 "Return an object like this `object` but with these `tags` added to its tags,
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
030 if they are not already present. It is an error (and an exception will be
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
031 thrown) if
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
032
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
033 1. `object` is not a map;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
034 2. any of `tags` is not a keyword or sequence of keywords.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
035
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
036 It's legal to include sequences of keywords in `tags`, so that users can do
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
037 useful things like `(tag obj (map keyword some-strings))`."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
038 [object & tags]
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
039 (l/debug "Tagging" (kind-type object) "with" tags)
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
040 (when-not (map? object)
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
041 (throw (IllegalArgumentException.
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
042 (str "Must be a map: " (kind-type object)))))
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
043 (let [tags' (flatten tags)]
|
||||
</span><br/>
|
||||
<span class="covered" title="7 out of 7 forms covered">
|
||||
044 (when-not (every? keyword? tags')
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
045 (throw (IllegalArgumentException.
|
||||
</span><br/>
|
||||
<span class="covered" title="7 out of 7 forms covered">
|
||||
046 (str "Must be keywords: " (map kind-type tags')))))
|
||||
</span><br/>
|
||||
<span class="covered" title="12 out of 12 forms covered">
|
||||
047 (assoc object ::tags (union (set tags') (::tags object)))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
048
|
||||
</span><br/>
|
||||
<span class="covered" title="15 out of 15 forms covered">
|
||||
049 (defmacro tags
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
050 "Return the tags of this object, if any."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
051 [object]
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
052 `(::tags ~object))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
053
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
054 (defn untag
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
055 "Return an object like this `object` but with these `tags` removed from its
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
056 tags, if present. It is an error (and an exception will be thrown) if
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
057
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
058 1. `object` is not a map;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
059 2. any of `tags` is not a keyword or sequence of keywords."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
060 [object & tags]
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
061 (when-not (map? object)
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
062 (throw (IllegalArgumentException.
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
063 (str "Must be a map: " (kind-type object)))))
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
064 (let [tags' (flatten tags)]
|
||||
</span><br/>
|
||||
<span class="covered" title="7 out of 7 forms covered">
|
||||
065 (when-not (every? keyword? tags')
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
066 (throw (IllegalArgumentException.
|
||||
</span><br/>
|
||||
<span class="covered" title="7 out of 7 forms covered">
|
||||
067 (str "Must be keywords: " (map kind-type tags')))))
|
||||
</span><br/>
|
||||
<span class="covered" title="9 out of 9 forms covered">
|
||||
068 (update-in object [:walkmap.tag/tags] difference (set tags'))))
|
||||
</span><br/>
|
||||
</body>
|
||||
</html>
|
365
docs/cloverage/walkmap/utils.clj.html
Normal file
365
docs/cloverage/walkmap/utils.clj.html
Normal file
|
@ -0,0 +1,365 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<link rel="stylesheet" href="../coverage.css"/> <title> walkmap/utils.clj </title>
|
||||
</head>
|
||||
<body>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
001 (ns walkmap.utils
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
002 "Miscellaneous utility functions."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
003 (:require [clojure.edn :as edn :only [read]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
004 [clojure.java.io :as io]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
005 [clojure.math.numeric-tower :as m]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
006 [clojure.string :as s]))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
007
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
008 (defn deep-merge
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
009 "Recursively merges maps. If vals are not maps, the last value wins."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
010 ;; TODO: not my implementation, not sure I entirely trust it.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
011 ;; TODO TODO: if we are to successfully merge walkmap objects, we must
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
012 ;; return, on each object, the union of its tags if any.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
013 [& vals]
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
014 (if (every? map? vals)
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
015 (apply merge-with deep-merge vals)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
016 (last vals)))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
017
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
018 (defn truncate
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
019 "If string `s` is more than `n` characters long, return the first `n`
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
020 characters; otherwise, return `s`."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
021 [s n]
|
||||
</span><br/>
|
||||
<span class="partial" title="20 out of 21 forms covered">
|
||||
022 (if (and (string? s) (number? n) (> (count s) n))
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
023 (subs s 0 n)
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
024 s))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
025
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
026 (defn kind-type
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
027 "Identify the type of an `object`, e.g. for logging. If it has a `:kind` key,
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
028 it's one of ours, and that's what we want. Otherwise, we want its type; but
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
029 the type of `nil` is `nil`, which doesn't get printed when assembling error
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
030 ,essages, so return \"nil\"."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
031 [object]
|
||||
</span><br/>
|
||||
<span class="covered" title="15 out of 15 forms covered">
|
||||
032 (or (:kind object) (type object) "nil"))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
033
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
034 (defn =ish
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
035 "True if numbers `n1`, `n2` are roughly equal; that is to say, equal to
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
036 within `tolerance` (defaults to one part in one hundred thousand)."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
037 ([n1 n2]
|
||||
</span><br/>
|
||||
<span class="covered" title="11 out of 11 forms covered">
|
||||
038 (if (and (number? n1) (number? n2))
|
||||
</span><br/>
|
||||
<span class="covered" title="7 out of 7 forms covered">
|
||||
039 (let [m (m/abs (min n1 n2))
|
||||
</span><br/>
|
||||
<span class="covered" title="9 out of 9 forms covered">
|
||||
040 t (if (zero? m) 0.00001 (* 0.00001 m))]
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
041 (=ish n1 n2 t))
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
042 (= n1 n2)))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
043 ([n1 n2 tolerance]
|
||||
</span><br/>
|
||||
<span class="covered" title="11 out of 11 forms covered">
|
||||
044 (if (and (number? n1) (number? n2))
|
||||
</span><br/>
|
||||
<span class="covered" title="9 out of 9 forms covered">
|
||||
045 (< (m/abs (- n1 n2)) tolerance)
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
046 (= n1 n2))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
047
|
||||
</span><br/>
|
||||
<span class="covered" title="198 out of 198 forms covered">
|
||||
048 (defmacro check-kind-type
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
049 "If `object` is not of kind-type `expected`, throws an
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
050 IllegalArgumentException with an appropriate message; otherwise, returns
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
051 `object`. If `checkfn` is supplied, it should be a function which tests
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
052 whether the object is of the expected kind-type.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
053
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
054 Macro, so that the exception is thrown from the calling function."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
055 ([object expected]
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
056 `(if-not (= (kind-type ~object) ~expected)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
057 (throw
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
058 (IllegalArgumentException.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
059 (s/join
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
060 " "
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
061 ["Expected" ~expected "but found" (kind-type ~object)])))
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
062 ~object))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
063 ([object checkfn expected]
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
064 `(if-not (~checkfn ~object)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
065 (throw
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
066 (IllegalArgumentException.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
067 (s/join
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
068 " "
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
069 ["Expected" ~expected "but found" (kind-type ~object)])))
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
070 ~object)))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
071
|
||||
</span><br/>
|
||||
<span class="covered" title="416 out of 416 forms covered">
|
||||
072 (defmacro check-kind-type-seq
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
073 "If some item on sequence `s` is not of the `expected` kind-type, throws an
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
074 IllegalArgumentException with an appropriate message; otherwise, returns
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
075 `object`. If `checkfn` is supplied, it should be a function which tests
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
076 whether the object is of the expected kind-type.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
077
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
078 Macro, so that the exception is thrown from the calling function."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
079 ([s expected]
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
080 `(if-not (every? #(= (kind-type %) ~expected) ~s)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
081 (throw
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
082 (IllegalArgumentException.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
083 (s/join
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
084 " "
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
085 ["Expected sequence of"
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
086 ~expected
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
087 "but found ("
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
088 (s/join ", " (remove #(= ~expected %) (map kind-type ~s)))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
089 ")"])))
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
090 ~s))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
091 ([s checkfn expected]
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
092 `(if-not (every? #(~checkfn %) ~s)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
093 (throw
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
094 (IllegalArgumentException.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
095 (s/join
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
096 " "
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
097 ["Expected sequence of"
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
098 ~expected
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
099 "but found ("
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
100 (s/join ", " (remove #(= ~expected %) (map kind-type ~s)))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
101 ")"])))
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
102 ~s)))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
103
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
104 (defn load-edn
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
105 "Load edn from an io/reader source (filename or io/resource)."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
106 [source]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
107 (try
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 7 forms covered">
|
||||
108 (with-open [r (io/reader source)]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
109 (edn/read (java.io.PushbackReader. r)))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
110 (catch java.io.IOException e
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
111 (printf "Couldn't open '%s': %s\n" source (.getMessage e)))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
112 (catch RuntimeException e
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
113 (printf "Error parsing edn file '%s': %s\n" source (.getMessage e)))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
114
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
115 (defn not-yet-implemented
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
116 [message]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
117 (throw
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
118 (UnsupportedOperationException.
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
119 (str "Not yet implemented: " message))))
|
||||
</span><br/>
|
||||
</body>
|
||||
</html>
|
|
@ -8,130 +8,454 @@
|
|||
001 (ns walkmap.vertex
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
002 "Essentially the specification for things we shall consider to be vertices.")
|
||||
002 "Essentially the specification for things we shall consider to be vertices.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
003
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
004 (defn vertex?
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
004 Note that there's no `distance` function here; to find the distance between
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
005 "True if `o` satisfies the conditions for a vertex. That is, essentially,
|
||||
005 two vertices, create an edge from them and use `walkmap.edge/length`."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
006 that it must rerpresent a two- or three- dimensional vector. A vertex is
|
||||
006 (:require [clojure.math.numeric-tower :as m]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
007 shall be a map having at least the keys `:x` and `:y`, where the value of
|
||||
007 [clojure.string :as s]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
008 those keys is a number. If the key `:z` is also present, its value must also
|
||||
008 [taoensso.timbre :as l]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
009 be a number.
|
||||
009 [walkmap.utils :refer [=ish check-kind-type check-kind-type-seq kind-type truncate]]))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
010
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
011 The name `vector?` was not used as that would clash with a function of that
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
012 name in `clojure.core` whose semantics are entirely different."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
013 [o]
|
||||
</span><br/>
|
||||
<span class="partial" title="15 out of 17 forms covered">
|
||||
014 (and
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
015 (map? o)
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
016 (number? (:x o))
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
017 (number? (:y o))
|
||||
</span><br/>
|
||||
<span class="covered" title="9 out of 9 forms covered">
|
||||
018 (or (nil? (:z o)) (number? (:z o)))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
019
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
020 (def ensure3d
|
||||
011 (defn vertex-key
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
021 "Given a vertex `o`, if `o` has a `:z` value, just return `o`; otherwise
|
||||
012 "Making sure we get the same key everytime we key a vertex with the same
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
022 return a vertex like `o` but having thie `dflt` value as the value of its
|
||||
013 coordinates. `o` must have numeric values for `:x`, `:y`, and optionally
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
023 `:z` key, or zero as the value of its `:z` key if `dflt` is not specified.
|
||||
014 `:z`; it is an error and an exception will be thrown if `o` does not
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
015 conform to this specification.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
024
|
||||
016
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
025 If `o` is not a vertex, throws an exception."
|
||||
017 **Note:** these keys can be quite long. No apology is made: it is required
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
018 that the same key can *never* refer to two different locations in space."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
019 [o]
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
026 (memoize
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
027 (fn
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
028 ([o]
|
||||
020 (keyword
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
029 (ensure3d o 0.0))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
030 ([o dflt]
|
||||
021 (s/replace
|
||||
</span><br/>
|
||||
<span class="partial" title="2 out of 3 forms covered">
|
||||
031 (cond
|
||||
022 (cond
|
||||
</span><br/>
|
||||
<span class="partial" title="5 out of 8 forms covered">
|
||||
032 (not (vertex? o)) (throw (Exception. "Not a vertex!"))
|
||||
<span class="partial" title="15 out of 17 forms covered">
|
||||
023 (and (:x o) (:y o) (:z o))
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
033 (:z o) o
|
||||
<span class="covered" title="14 out of 14 forms covered">
|
||||
024 (str "vert_" (:x o) "_" (:y o) "_" (:z o))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
034 :else (assoc o :z dflt))))))
|
||||
<span class="partial" title="9 out of 10 forms covered">
|
||||
025 (and (:x o) (:y o))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
035
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
036 (def ensure2d
|
||||
<span class="covered" title="10 out of 10 forms covered">
|
||||
026 (str "vert_" (:x o) "_" (:y o))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
037 "If `o` is a vertex, set its `:z` value to zero; else throw an exception."
|
||||
027 :else
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
038 (memoize
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
028 (throw (IllegalArgumentException.
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 12 forms covered">
|
||||
029 (truncate (str "Not a vertex: " (or o "nil")) 80))))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
030 "."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
031 "-")))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
032
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
039 (fn [o]
|
||||
033 (defn vertex?
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
040 (if
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
034 "True if `o` satisfies the conditions for a vertex. That is, essentially,
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
041 (vertex? o)
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
035 that it must rerpresent a two- or three- dimensional vector. A vertex is
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
042 (assoc o :z 0.0)
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
036 shall be a map having at least the keys `:x` and `:y`, where the value of
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
043 (throw (Exception. "Not a vertex!"))))))
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
037 those keys is a number. If the key `:z` is also present, its value must also
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
038 be a number.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
039
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
040 The name `vector?` was not used as that would clash with a function of that
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
041 name in `clojure.core` whose semantics are entirely different."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
042 [o]
|
||||
</span><br/>
|
||||
<span class="partial" title="24 out of 26 forms covered">
|
||||
043 (and
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
044 (map? o)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
045 (:walkmap.id/id o)
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
046 (number? (:x o))
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
047 (number? (:y o))
|
||||
</span><br/>
|
||||
<span class="partial" title="20 out of 26 forms covered">
|
||||
048 (or (nil? (:z o)) (number? (:z o)))
|
||||
</span><br/>
|
||||
<span class="covered" title="9 out of 9 forms covered">
|
||||
049 (or (nil? (:kind o)) (= (:kind o) :vertex))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
050
|
||||
</span><br/>
|
||||
<span class="covered" title="27 out of 27 forms covered">
|
||||
051 (defmacro check-vertex
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
052 "If `o` is not a vertex, throw an `IllegalArgumentException` with an
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
053 appropriate message; otherwise, returns `o`. Macro, so exception is thrown
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
054 from the calling function."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
055 [o]
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
056 `(check-kind-type ~o vertex? :vertex))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
057
|
||||
</span><br/>
|
||||
<span class="covered" title="22 out of 22 forms covered">
|
||||
058 (defmacro check-vertices
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
059 "If `o` is not a sequence of vertices, throw an `IllegalArgumentException` with an
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
060 appropriate message; otherwise, returns `o`. Macro, so exception is thrown
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
061 from the calling function."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
062 [o]
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
063 `(check-kind-type-seq ~o vertex? :vertex))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
064
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
065 (defn vertex=
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
066 "True if vertices `v1`, `v2` represent the same vertex."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
067 [v1 v2]
|
||||
</span><br/>
|
||||
<span class="partial" title="6 out of 16 forms covered">
|
||||
068 (check-vertex v1)
|
||||
</span><br/>
|
||||
<span class="covered" title="16 out of 16 forms covered">
|
||||
069 (check-vertex v2)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
070 (every?
|
||||
</span><br/>
|
||||
<span class="covered" title="8 out of 8 forms covered">
|
||||
071 #(=ish (% v1) (% v2))
|
||||
</span><br/>
|
||||
<span class="covered" title="13 out of 13 forms covered">
|
||||
072 [:x :y :z]))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
073
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
074 (defn vertex*
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
075 "Return a vertex like `v1`, but with each of its coordinates multiplied
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
076 by the equivalent vertex in `v2`. It is an error, and an exception will
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
077 be thrown, if either `v1` or `v2` is not a vertex."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
078 [v1 v2]
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
079 (let [f (fn [v1 v2 coord]
|
||||
</span><br/>
|
||||
<span class="covered" title="10 out of 10 forms covered">
|
||||
080 (* (or (coord v1) 0)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
081 ;; one here is deliberate!
|
||||
</span><br/>
|
||||
<span class="covered" title="8 out of 8 forms covered">
|
||||
082 (or (coord v2) 1)))]
|
||||
</span><br/>
|
||||
<span class="covered" title="41 out of 41 forms covered">
|
||||
083 (assoc v1 :x (f (check-vertex v1) (check-vertex v2) :x)
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
084 :y (f v1 v2 :y)
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
085 :z (f v1 v2 :z))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
086
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
087 (defn vertex
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
088 "Make a vertex with this `x`, `y` and (if provided) `z` values. Returns a map
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
089 with those values, plus a unique `:walkmap.id/id` value, and `:kind` set to `:vertex`.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
090 It's not necessary to use this function to create a vertex, but the `:walkmap.id/id`
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
091 must be present and must be unique."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
092 ([x y]
|
||||
</span><br/>
|
||||
<span class="covered" title="8 out of 8 forms covered">
|
||||
093 (let [v {:x x :y y :kind :vertex}]
|
||||
</span><br/>
|
||||
<span class="covered" title="7 out of 7 forms covered">
|
||||
094 (assoc v :walkmap.id/id (vertex-key v))))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
095 ([x y z]
|
||||
</span><br/>
|
||||
<span class="covered" title="10 out of 10 forms covered">
|
||||
096 (let [v {:x x :y y :z z :kind :vertex}]
|
||||
</span><br/>
|
||||
<span class="covered" title="7 out of 7 forms covered">
|
||||
097 (assoc v :walkmap.id/id (vertex-key v)))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
098
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
099 (defn canonicalise
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
100 "If `o` is a map with numeric values for `:x`, `:y` and optionally `:z`,
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
101 upgrade it to something we will recognise as a vertex."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
102 [o]
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
103 (if
|
||||
</span><br/>
|
||||
<span class="partial" title="16 out of 17 forms covered">
|
||||
104 (and
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
105 (map? o)
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
106 (number? (:x o))
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
107 (number? (:y o))
|
||||
</span><br/>
|
||||
<span class="covered" title="9 out of 9 forms covered">
|
||||
108 (or (nil? (:z o)) (number? (:z o))))
|
||||
</span><br/>
|
||||
<span class="covered" title="9 out of 9 forms covered">
|
||||
109 (assoc o :kind :vertex :walkmap.id/id (vertex-key o))
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
110 (throw
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
111 (IllegalArgumentException.
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
112 (truncate
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
113 (str "Not a proto-vertex: must have numeric `:x` and `:y`: "
|
||||
</span><br/>
|
||||
<span class="partial" title="5 out of 6 forms covered">
|
||||
114 (or o "nil"))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
115 80)))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
116
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
117 (def ensure3d
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
118 "Given a vertex `o`, if `o` has a `:z` value, just return `o`; otherwise
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
119 return a vertex like `o` but having this `dflt` value as the value of its
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
120 `:z` key, or zero as the value of its `:z` key if `dflt` is not specified.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
121
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
122 If `o` is not a vertex, throws an exception."
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
123 (memoize
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
124 (fn
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
125 ([o]
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
126 (ensure3d o 0.0))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
127 ([o dflt]
|
||||
</span><br/>
|
||||
<span class="partial" title="9 out of 19 forms covered">
|
||||
128 (if (:z (check-vertex o))
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
129 o
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
130 (assoc o :z dflt))))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
131
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
132 (def ensure2d
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
133 "If `o` is a vertex, set its `:z` value to zero; else throw an exception."
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
134 (memoize
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
135 (fn [o]
|
||||
</span><br/>
|
||||
<span class="partial" title="10 out of 20 forms covered">
|
||||
136 (assoc (check-vertex o) :z 0.0))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
137
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
138 (defn within-box?
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
139 "True if `target` is within the box defined by `minv` and `maxv`. All
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
140 arguments must be vertices; additionally, both `minv` and `maxv` must
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
141 have `:z` coordinates."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
142 [target minv maxv]
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
143 (do
|
||||
</span><br/>
|
||||
<span class="covered" title="34 out of 34 forms covered">
|
||||
144 (check-vertices [target minv maxv])
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
145 (every?
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
146 true?
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
147 (map
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
148 #(if (% target)
|
||||
</span><br/>
|
||||
<span class="covered" title="11 out of 11 forms covered">
|
||||
149 (<= (% minv) (% target) (% maxv))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
150 true)
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
151 [:x :y :z]))))
|
||||
</span><br/>
|
||||
</body>
|
||||
</html>
|
||||
|
|
171
docs/codox/dali-performance.html
Normal file
171
docs/codox/dali-performance.html
Normal file
|
@ -0,0 +1,171 @@
|
|||
<!DOCTYPE html PUBLIC ""
|
||||
"">
|
||||
<html><head><meta charset="UTF-8" /><title>Dali performance</title><link rel="stylesheet" type="text/css" href="css/default.css" /><link rel="stylesheet" type="text/css" href="css/highlight.css" /><script type="text/javascript" src="js/highlight.min.js"></script><script type="text/javascript" src="js/jquery.min.js"></script><script type="text/javascript" src="js/page_effects.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body><div id="header"><h2>Generated by <a href="https://github.com/weavejester/codox">Codox</a></h2><h1><a href="index.html"><span class="project-title"><span class="project-name">Walkmap</span> <span class="project-version">0.1.0-SNAPSHOT</span></span></a></h1></div><div class="sidebar primary"><h3 class="no-link"><span class="inner">Project</span></h3><ul class="index-link"><li class="depth-1 "><a href="index.html"><div class="inner">Index</div></a></li></ul><h3 class="no-link"><span class="inner">Topics</span></h3><ul><li class="depth-1 current"><a href="dali-performance.html"><div class="inner"><span>Dali performance</span></div></a></li><li class="depth-1 "><a href="intro.html"><div class="inner"><span>Introduction to walkmap</span></div></a></li></ul><h3 class="no-link"><span class="inner">Namespaces</span></h3><ul><li class="depth-1"><div class="no-link"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>walkmap</span></div></div></li><li class="depth-2 branch"><a href="walkmap.edge.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>edge</span></div></a></li><li class="depth-2 branch"><a href="walkmap.id.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>id</span></div></a></li><li class="depth-2 branch"><a href="walkmap.microworld.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>microworld</span></div></a></li><li class="depth-2 branch"><a href="walkmap.ocean.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>ocean</span></div></a></li><li class="depth-2 branch"><a href="walkmap.path.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>path</span></div></a></li><li class="depth-2 branch"><a href="walkmap.polygon.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>polygon</span></div></a></li><li class="depth-2 branch"><a href="walkmap.read-svg.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>read-svg</span></div></a></li><li class="depth-2 branch"><a href="walkmap.routing.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>routing</span></div></a></li><li class="depth-2 branch"><a href="walkmap.stl.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>stl</span></div></a></li><li class="depth-2 branch"><a href="walkmap.superstructure.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>superstructure</span></div></a></li><li class="depth-2 branch"><a href="walkmap.svg.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>svg</span></div></a></li><li class="depth-2 branch"><a href="walkmap.tag.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>tag</span></div></a></li><li class="depth-2 branch"><a href="walkmap.utils.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>utils</span></div></a></li><li class="depth-2"><a href="walkmap.vertex.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>vertex</span></div></a></li></ul></div><div class="document" id="content"><div class="doc"><div class="markdown"><h1><a href="#dali-performance" name="dali-performance"></a>Dali performance</h1>
|
||||
<p>Notes written while trying to characterise the performance problem in Dali.</p>
|
||||
<h2><a href="#hypothesis-one-its-the-way-i-format-the-polygons-thats-the-issue" name="hypothesis-one-its-the-way-i-format-the-polygons-thats-the-issue"></a>Hypothesis one: it’s the way I format the polygons that’s the issue</h2>
|
||||
<p>Firstly, with both versions of <code>stl->svg</code> using the same version of <code>facet->svg-poly</code>, i.e. this one:</p>
|
||||
<pre><code>(defn- facet->svg-poly
|
||||
[facet]
|
||||
[:polygon
|
||||
{:points (s/join " " (map #(str (:x %) "," (:y %)) (:vertices facet)))}])
|
||||
</code></pre>
|
||||
<p>we get this performance using the smaller <code>isle_of_man</code> map:</p>
|
||||
<pre><code>walkmap.svg=> (def ^:dynamic *preferred-svg-render* :hiccup)
|
||||
#'walkmap.svg/*preferred-svg-render*
|
||||
walkmap.svg=> (time (def hiccup (binary-stl-file->svg "resources/isle_of_man.stl" "resources/isle_of_man.svg")))
|
||||
20-05-25 09:21:43 mason INFO [walkmap.svg:82] - Generating SVG for :hiccup renderer
|
||||
20-05-25 09:21:43 mason INFO [walkmap.svg:96] - Emitting SVG with :hiccup renderer
|
||||
"Elapsed time: 86.904891 msecs"
|
||||
#'walkmap.svg/hiccup
|
||||
walkmap.svg=> (def ^:dynamic *preferred-svg-render* :dali)
|
||||
#'walkmap.svg/*preferred-svg-render*
|
||||
walkmap.svg=> (time (def dali (binary-stl-file->svg "resources/isle_of_man.stl" "resources/isle_of_man.svg")))
|
||||
20-05-25 09:22:17 mason INFO [walkmap.svg:82] - Generating SVG for :dali renderer
|
||||
20-05-25 09:22:17 mason INFO [walkmap.svg:96] - Emitting SVG with :dali renderer
|
||||
"Elapsed time: 890.863814 msecs"
|
||||
#'walkmap.svg/dali
|
||||
</code></pre>
|
||||
<p>If we switch the Dali render to use my original version of <code>facet->svg-poly</code>, i.e. this one:</p>
|
||||
<pre><code>(defn- dali-facet->svg-poly
|
||||
[facet]
|
||||
(vec
|
||||
(cons
|
||||
:polygon
|
||||
(map #(vec (list (:x %) (:y %))) (:vertices facet)))))
|
||||
</code></pre>
|
||||
<p>we get this performance:</p>
|
||||
<pre><code>walkmap.svg=> (def ^:dynamic *preferred-svg-render* :hiccup)
|
||||
#'walkmap.svg/*preferred-svg-render*
|
||||
walkmap.svg=> (time (def hiccup (binary-stl-file->svg "resources/isle_of_man.stl" "resources/isle_of_man.svg")))
|
||||
20-05-25 09:35:33 mason INFO [walkmap.svg:82] - Generating SVG for :hiccup renderer
|
||||
20-05-25 09:35:33 mason INFO [walkmap.svg:96] - Emitting SVG with :hiccup renderer
|
||||
"Elapsed time: 84.09972 msecs"
|
||||
#'walkmap.svg/hiccup
|
||||
walkmap.svg=> (def ^:dynamic *preferred-svg-render* :dali)
|
||||
#'walkmap.svg/*preferred-svg-render*
|
||||
walkmap.svg=> (time (def dali (binary-stl-file->svg "resources/isle_of_man.stl" "resources/isle_of_man.svg")))
|
||||
20-05-25 09:35:41 mason INFO [walkmap.svg:82] - Generating SVG for :dali renderer
|
||||
20-05-25 09:35:41 mason INFO [walkmap.svg:96] - Emitting SVG with :dali renderer
|
||||
"Elapsed time: 874.292007 msecs"
|
||||
#'walkmap.svg/dali
|
||||
</code></pre>
|
||||
<p>No significant difference in performance.</p>
|
||||
<p>If we generate but don’t render, we get this:</p>
|
||||
<pre><code>walkmap.svg=> (def ^:dynamic *preferred-svg-render* :hiccup)
|
||||
#'walkmap.svg/*preferred-svg-render*
|
||||
walkmap.svg=> (time (def hiccup (binary-stl-file->svg "resources/isle_of_man.stl")))
|
||||
20-05-25 09:37:44 mason INFO [walkmap.svg:82] - Generating SVG for :hiccup renderer
|
||||
"Elapsed time: 52.614707 msecs"
|
||||
#'walkmap.svg/hiccup
|
||||
walkmap.svg=> (def ^:dynamic *preferred-svg-render* :dali)
|
||||
#'walkmap.svg/*preferred-svg-render*
|
||||
walkmap.svg=> (time (def dali (binary-stl-file->svg "resources/isle_of_man.stl")))
|
||||
20-05-25 09:38:07 mason INFO [walkmap.svg:82] - Generating SVG for :dali renderer
|
||||
"Elapsed time: 49.891043 msecs"
|
||||
#'walkmap.svg/dali
|
||||
</code></pre>
|
||||
<p>This implies that the problem is not in the way polygons are formatted.</p>
|
||||
<p>The difference between the two versions of <code>facet->svg-poly</code> is as follows:</p>
|
||||
<h3><a href="#new-version-works-with-both-hiccup-and-dali-" name="new-version-works-with-both-hiccup-and-dali-"></a>New version, works with both Hiccup and Dali:</h3>
|
||||
<pre><code>walkmap.svg=> (def stl (decode-binary-stl "resources/isle_of_man.stl"))
|
||||
#'walkmap.svg/stl
|
||||
walkmap.svg=> (def facet (first (:facets stl)))
|
||||
#'walkmap.svg/facet
|
||||
walkmap.svg=> (pprint facet)
|
||||
{:normal {:x -0.0, :y 0.0, :z 1.0},
|
||||
:vertices
|
||||
[{:x 3.0, :y 1.0, :z 1.0}
|
||||
{:x 2.0, :y 3.0, :z 1.0}
|
||||
{:x 0.0, :y 0.0, :z 1.0}],
|
||||
:abc 0}
|
||||
nil
|
||||
walkmap.svg=> (pprint (facet->svg-poly facet))
|
||||
[:polygon {:points "3.0,1.0 2.0,3.0 0.0,0.0"}]
|
||||
nil
|
||||
</code></pre>
|
||||
<p>In other words, the new version constructs the <code>:points</code> attribute of the <code>:polygon</code> tag by string concatenation, and the renderer just needs to output it.</p>
|
||||
<h3><a href="#older-version-works-with-dali-only-" name="older-version-works-with-dali-only-"></a>Older version, works with Dali only:</h3>
|
||||
<pre><code>walkmap.svg=> (pprint (dali-facet->svg-poly facet))
|
||||
[:polygon [3.0 1.0] [2.0 3.0] [0.0 0.0]]
|
||||
nil
|
||||
</code></pre>
|
||||
<p>This means that the renderer is actually doing more work, since it has to compose the <code>:points</code> attribute itself; nevertheless there doesn’t seem to be an increased time penalty.</p>
|
||||
<h3><a href="#conclusion" name="conclusion"></a>Conclusion</h3>
|
||||
<p>It doesn’t seem that formatting the polygons is the issue.</p>
|
||||
<h2><a href="#hypothesis-two-dali-renderer-scales-non-linearly-with-number-of-objects-drawn" name="hypothesis-two-dali-renderer-scales-non-linearly-with-number-of-objects-drawn"></a>Hypothesis two: Dali renderer scales non-linearly with number of objects drawn</h2>
|
||||
<p>To test this, we need some otherwise-similar test files with different numbers of objects:</p>
|
||||
<pre><code>walkmap.svg=> (count (:facets stl))
|
||||
4416
|
||||
walkmap.svg=> (def small-stl (assoc stl :facets (take 400 (:facets stl))))
|
||||
#'walkmap.svg/small-stl
|
||||
walkmap.svg=> (count (:facets small-stl))
|
||||
400
|
||||
walkmap.svg=> (def large-stl (decode-binary-stl "../the-great-game/resources/maps/heightmap.stl"))
|
||||
#'walkmap.svg/large-stl
|
||||
walkmap.svg=> (count (:facets large-stl))
|
||||
746585
|
||||
walkmap.svg=> (def ^:dynamic *preferred-svg-render* :dali)
|
||||
#'walkmap.svg/*preferred-svg-render*
|
||||
walkmap.svg=> (time (dali.io/render-svg (stl->svg small-stl) "dali-small.svg"))
|
||||
20-05-25 10:12:25 mason INFO [walkmap.svg:92] - Generating SVG for :dali renderer
|
||||
"Elapsed time: 32.55506 msecs"
|
||||
nil
|
||||
walkmap.svg=> (def ^:dynamic *preferred-svg-render* :hiccup)
|
||||
#'walkmap.svg/*preferred-svg-render*
|
||||
walkmap.svg=> (time (spit "hiccup-small.svg" (hiccup.core/html (stl->svg small-stl))))
|
||||
20-05-25 10:14:07 mason INFO [walkmap.svg:92] - Generating SVG for :hiccup renderer
|
||||
"Elapsed time: 10.026369 msecs"
|
||||
</code></pre>
|
||||
<p>So we have</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th> </th>
|
||||
<th>Dali </th>
|
||||
<th> </th>
|
||||
<th>Hiccup </th>
|
||||
<th> </th>
|
||||
<th> </th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td># of facets </td>
|
||||
<td>time (msecs) </td>
|
||||
<td>objets/msec </td>
|
||||
<td>time (msecs) </td>
|
||||
<td>objets/msec </td>
|
||||
<td>ratio (Dali/Hiccup) </td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>400 </td>
|
||||
<td>32.55506 </td>
|
||||
<td>12.29 </td>
|
||||
<td>10.026369 </td>
|
||||
<td>39.89 </td>
|
||||
<td>3.35 </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>4416 </td>
|
||||
<td>874.292007 </td>
|
||||
<td>5.05 </td>
|
||||
<td>84.09972 </td>
|
||||
<td>52.51 </td>
|
||||
<td>10.40 </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>746585 </td>
|
||||
<td>29,695,695.61 </td>
|
||||
<td>0.03 </td>
|
||||
<td>16724.848222 </td>
|
||||
<td>44.64 </td>
|
||||
<td>1775.54 </td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h3><a href="#conclusion" name="conclusion"></a>Conclusion</h3>
|
||||
<p>What we’re seeing is that Hiccup renders more or less linearly by the number of objects (bear in mind that all of these objects are triangles, so essentially equally complex to render), whereas trhe performance of Dali degrades significantly as the number of objects increases.</p></div></div></div></body></html>
|
File diff suppressed because one or more lines are too long
|
@ -1,6 +1,6 @@
|
|||
<!DOCTYPE html PUBLIC ""
|
||||
"">
|
||||
<html><head><meta charset="UTF-8" /><title>Introduction to walkmap</title><link rel="stylesheet" type="text/css" href="css/default.css" /><link rel="stylesheet" type="text/css" href="css/highlight.css" /><script type="text/javascript" src="js/highlight.min.js"></script><script type="text/javascript" src="js/jquery.min.js"></script><script type="text/javascript" src="js/page_effects.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body><div id="header"><h2>Generated by <a href="https://github.com/weavejester/codox">Codox</a></h2><h1><a href="index.html"><span class="project-title"><span class="project-name">Walkmap</span> <span class="project-version">0.1.0-SNAPSHOT</span></span></a></h1></div><div class="sidebar primary"><h3 class="no-link"><span class="inner">Project</span></h3><ul class="index-link"><li class="depth-1 "><a href="index.html"><div class="inner">Index</div></a></li></ul><h3 class="no-link"><span class="inner">Topics</span></h3><ul><li class="depth-1 current"><a href="intro.html"><div class="inner"><span>Introduction to walkmap</span></div></a></li></ul><h3 class="no-link"><span class="inner">Namespaces</span></h3><ul><li class="depth-1"><div class="no-link"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>walkmap</span></div></div></li><li class="depth-2 branch"><a href="walkmap.core.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>core</span></div></a></li><li class="depth-2 branch"><a href="walkmap.edge.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>edge</span></div></a></li><li class="depth-2 branch"><a href="walkmap.geometry.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>geometry</span></div></a></li><li class="depth-2 branch"><a href="walkmap.path.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>path</span></div></a></li><li class="depth-2 branch"><a href="walkmap.polygon.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>polygon</span></div></a></li><li class="depth-2 branch"><a href="walkmap.stl.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>stl</span></div></a></li><li class="depth-2 branch"><a href="walkmap.svg.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>svg</span></div></a></li><li class="depth-2"><a href="walkmap.vertex.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>vertex</span></div></a></li></ul></div><div class="document" id="content"><div class="doc"><div class="markdown"><h1><a href="#introduction-to-walkmap" name="introduction-to-walkmap"></a>Introduction to walkmap</h1>
|
||||
<html><head><meta charset="UTF-8" /><title>Introduction to walkmap</title><link rel="stylesheet" type="text/css" href="css/default.css" /><link rel="stylesheet" type="text/css" href="css/highlight.css" /><script type="text/javascript" src="js/highlight.min.js"></script><script type="text/javascript" src="js/jquery.min.js"></script><script type="text/javascript" src="js/page_effects.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body><div id="header"><h2>Generated by <a href="https://github.com/weavejester/codox">Codox</a></h2><h1><a href="index.html"><span class="project-title"><span class="project-name">Walkmap</span> <span class="project-version">0.1.0-SNAPSHOT</span></span></a></h1></div><div class="sidebar primary"><h3 class="no-link"><span class="inner">Project</span></h3><ul class="index-link"><li class="depth-1 "><a href="index.html"><div class="inner">Index</div></a></li></ul><h3 class="no-link"><span class="inner">Topics</span></h3><ul><li class="depth-1 "><a href="dali-performance.html"><div class="inner"><span>Dali performance</span></div></a></li><li class="depth-1 current"><a href="intro.html"><div class="inner"><span>Introduction to walkmap</span></div></a></li></ul><h3 class="no-link"><span class="inner">Namespaces</span></h3><ul><li class="depth-1"><div class="no-link"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>walkmap</span></div></div></li><li class="depth-2 branch"><a href="walkmap.edge.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>edge</span></div></a></li><li class="depth-2 branch"><a href="walkmap.id.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>id</span></div></a></li><li class="depth-2 branch"><a href="walkmap.microworld.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>microworld</span></div></a></li><li class="depth-2 branch"><a href="walkmap.ocean.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>ocean</span></div></a></li><li class="depth-2 branch"><a href="walkmap.path.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>path</span></div></a></li><li class="depth-2 branch"><a href="walkmap.polygon.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>polygon</span></div></a></li><li class="depth-2 branch"><a href="walkmap.read-svg.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>read-svg</span></div></a></li><li class="depth-2 branch"><a href="walkmap.routing.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>routing</span></div></a></li><li class="depth-2 branch"><a href="walkmap.stl.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>stl</span></div></a></li><li class="depth-2 branch"><a href="walkmap.superstructure.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>superstructure</span></div></a></li><li class="depth-2 branch"><a href="walkmap.svg.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>svg</span></div></a></li><li class="depth-2 branch"><a href="walkmap.tag.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>tag</span></div></a></li><li class="depth-2 branch"><a href="walkmap.utils.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>utils</span></div></a></li><li class="depth-2"><a href="walkmap.vertex.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>vertex</span></div></a></li></ul></div><div class="document" id="content"><div class="doc"><div class="markdown"><h1><a href="#introduction-to-walkmap" name="introduction-to-walkmap"></a>Introduction to walkmap</h1>
|
||||
<p>This library is written in support of work on <a href="https://simon-brooke.github.io/the-great-game/codox/Pathmaking.html">The Great Game</a>, but is separate because it may be of some use in other settings.</p>
|
||||
<h2><a href="#usage" name="usage"></a>Usage</h2>
|
||||
<p>What works:</p>
|
||||
|
@ -10,6 +10,10 @@
|
|||
<p>Lein dependency:</p>
|
||||
<pre><code>[walkmap "0.1.0-SNAPSHOT"]
|
||||
</code></pre>
|
||||
<ul>
|
||||
<li><a href="https://simon-brooke.github.io/walkmap/codox/index.html">API documentation</a></li>
|
||||
<li><a href="https://simon-brooke.github.io/walkmap/cloverage/index.html">Test coverage</a></li>
|
||||
</ul>
|
||||
<h3><a href="#converting-heightmaps-to-stl" name="converting-heightmaps-to-stl"></a>Converting heightmaps to STL</h3>
|
||||
<p>Doesn’t work yet, and is not a priority. Use <a href="https://github.com/fogleman/hmm">hmm</a> instead.</p>
|
||||
<h3><a href="#reading-binary-stl-files" name="reading-binary-stl-files"></a>Reading binary STL files</h3>
|
||||
|
@ -36,10 +40,19 @@
|
|||
<pre><code>(require '[walkmap.core :refer [binary-stl-file->svg]])
|
||||
(binary-stl-file->svg "path/to/input-file.stl" "path-to-output-file.svg")
|
||||
</code></pre>
|
||||
<p>As above, but, as a side effect, writes the SVG to the specified output file. Works for smaller test files, as above.</p>
|
||||
<p>As above, but, as a side effect, writes the SVG to the specified output file.</p>
|
||||
<h3><a href="#merging-exclusion-maps-and-reserved-area-maps" name="merging-exclusion-maps-and-reserved-area-maps"></a>Merging exclusion maps and reserved area maps</h3>
|
||||
<p>It is intended that it should be possible to merge exclusion maps (maps of areas which should be excluded from the traversable area) with maps derived from height maps. These exclusion maps will probably be represented as SVG.</p>
|
||||
<p>This is not yet implemented.</p>
|
||||
<p>Culling facets in ocean areas is implemented and works:</p>
|
||||
<pre><code>(require '[walkmap.core :refer [cull-ocean-facets *sea-level*]])
|
||||
(cull-ocean-facets stl)
|
||||
</code></pre>
|
||||
<p>If sea level in your heightmaps is not zero, e.g. is 5, set it thus:</p>
|
||||
<pre><code>(def ^:dynamic *sea-level* 5.0)
|
||||
(cull-ocean-facets stl)
|
||||
</code></pre>
|
||||
<p>It is <strong>strongly recomended</strong> that you set <code>*sea-level*</code> to a floating point number, not an integer, because numbers are specified in the STL file as floating point, and in Clojure, <code>(= 5 5.0)</code> returns <code>false</code>.</p>
|
||||
<h3><a href="#merging-road-maps-and-river-system-maps" name="merging-road-maps-and-river-system-maps"></a>Merging road maps and river system maps</h3>
|
||||
<p>It is intended that it should be possible to merge road maps (maps of already computed routes) with maps derived from height maps. These exclusion maps will probably be represented as SVG. This is not yet implemented.</p>
|
||||
<p>River system maps are conceptually similar to road maps; this too is not yet implemented.</p>
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
<!DOCTYPE html PUBLIC ""
|
||||
"">
|
||||
<html><head><meta charset="UTF-8" /><title>walkmap.core documentation</title><link rel="stylesheet" type="text/css" href="css/default.css" /><link rel="stylesheet" type="text/css" href="css/highlight.css" /><script type="text/javascript" src="js/highlight.min.js"></script><script type="text/javascript" src="js/jquery.min.js"></script><script type="text/javascript" src="js/page_effects.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body><div id="header"><h2>Generated by <a href="https://github.com/weavejester/codox">Codox</a></h2><h1><a href="index.html"><span class="project-title"><span class="project-name">Walkmap</span> <span class="project-version">0.1.0-SNAPSHOT</span></span></a></h1></div><div class="sidebar primary"><h3 class="no-link"><span class="inner">Project</span></h3><ul class="index-link"><li class="depth-1 "><a href="index.html"><div class="inner">Index</div></a></li></ul><h3 class="no-link"><span class="inner">Topics</span></h3><ul><li class="depth-1 "><a href="intro.html"><div class="inner"><span>Introduction to walkmap</span></div></a></li></ul><h3 class="no-link"><span class="inner">Namespaces</span></h3><ul><li class="depth-1"><div class="no-link"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>walkmap</span></div></div></li><li class="depth-2 branch current"><a href="walkmap.core.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>core</span></div></a></li><li class="depth-2 branch"><a href="walkmap.edge.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>edge</span></div></a></li><li class="depth-2 branch"><a href="walkmap.geometry.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>geometry</span></div></a></li><li class="depth-2 branch"><a href="walkmap.path.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>path</span></div></a></li><li class="depth-2 branch"><a href="walkmap.polygon.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>polygon</span></div></a></li><li class="depth-2 branch"><a href="walkmap.stl.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>stl</span></div></a></li><li class="depth-2 branch"><a href="walkmap.svg.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>svg</span></div></a></li><li class="depth-2"><a href="walkmap.vertex.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>vertex</span></div></a></li></ul></div><div class="sidebar secondary"><h3><a href="#top"><span class="inner">Public Vars</span></a></h3><ul><li class="depth-1"><a href="walkmap.core.html#var-*sea-level*"><div class="inner"><span>*sea-level*</span></div></a></li><li class="depth-1"><a href="walkmap.core.html#var-binary-stl-file-.3Esvg"><div class="inner"><span>binary-stl-file->svg</span></div></a></li><li class="depth-1"><a href="walkmap.core.html#var-cull-ocean-facets"><div class="inner"><span>cull-ocean-facets</span></div></a></li><li class="depth-1"><a href="walkmap.core.html#var-ocean.3F"><div class="inner"><span>ocean?</span></div></a></li></ul></div><div class="namespace-docs" id="content"><h1 class="anchor" id="top">walkmap.core</h1><div class="doc"><div class="markdown"><p>At this stage, primarily utility functions dealing with stereolithography (STL) files. Not a stable API yet!</p></div></div><div class="public anchor" id="var-*sea-level*"><h3>*sea-level*</h3><h4 class="dynamic">dynamic</h4><div class="usage"></div><div class="doc"><div class="markdown"><p>The sea level on heightmaps we’re currently handling. If characters are to be able to swin in the sea, we must model the sea bottom, so we need heightmaps which cover at least the continental shelf. However, the sea bottom is not walkable territory and can be culled from walkmaps.</p>
|
||||
<p><strong>Note</strong> must be a floating point number. <code>(= 0 0.0)</code> returns <code>false</code>!</p></div></div><div class="src-link"><a href="https://github.com/simon-brooke/walkmap/blob/master/src/walkmap/core.clj#L12">view source</a></div></div><div class="public anchor" id="var-binary-stl-file-.3Esvg"><h3>binary-stl-file->svg</h3><div class="usage"><code>(binary-stl-file->svg in-filename)</code><code>(binary-stl-file->svg in-filename out-filename)</code></div><div class="doc"><div class="markdown"><p>Given only an <code>in-filename</code>, parse the indicated file, expected to be binary STL, and return an equivalent SVG structure. Given both <code>in-filename</code> and <code>out-filename</code>, as side-effect write the SVG to the indicated output file.</p></div></div><div class="src-link"><a href="https://github.com/simon-brooke/walkmap/blob/master/src/walkmap/core.clj#L34">view source</a></div></div><div class="public anchor" id="var-cull-ocean-facets"><h3>cull-ocean-facets</h3><div class="usage"><code>(cull-ocean-facets stl)</code></div><div class="doc"><div class="markdown"><p>Ye cannae walk on water. Remove all facets from this <code>stl</code> structure which are at sea level.</p></div></div><div class="src-link"><a href="https://github.com/simon-brooke/walkmap/blob/master/src/walkmap/core.clj#L28">view source</a></div></div><div class="public anchor" id="var-ocean.3F"><h3>ocean?</h3><div class="usage"><code>(ocean? facet)</code></div><div class="doc"><div class="markdown"><p>Of a <code>facet</code>, is the altitude of every vertice equal to <code>*sea-level*</code>?</p></div></div><div class="src-link"><a href="https://github.com/simon-brooke/walkmap/blob/master/src/walkmap/core.clj#L21">view source</a></div></div></div></body></html>
|
||||
<html><head><meta charset="UTF-8" /><title>walkmap.core documentation</title><link rel="stylesheet" type="text/css" href="css/default.css" /><link rel="stylesheet" type="text/css" href="css/highlight.css" /><script type="text/javascript" src="js/highlight.min.js"></script><script type="text/javascript" src="js/jquery.min.js"></script><script type="text/javascript" src="js/page_effects.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body><div id="header"><h2>Generated by <a href="https://github.com/weavejester/codox">Codox</a></h2><h1><a href="index.html"><span class="project-title"><span class="project-name">Walkmap</span> <span class="project-version">0.1.0-SNAPSHOT</span></span></a></h1></div><div class="sidebar primary"><h3 class="no-link"><span class="inner">Project</span></h3><ul class="index-link"><li class="depth-1 "><a href="index.html"><div class="inner">Index</div></a></li></ul><h3 class="no-link"><span class="inner">Topics</span></h3><ul><li class="depth-1 "><a href="dali-performance.html"><div class="inner"><span>Dali performance</span></div></a></li><li class="depth-1 "><a href="intro.html"><div class="inner"><span>Introduction to walkmap</span></div></a></li></ul><h3 class="no-link"><span class="inner">Namespaces</span></h3><ul><li class="depth-1"><div class="no-link"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>walkmap</span></div></div></li><li class="depth-2 branch current"><a href="walkmap.core.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>core</span></div></a></li><li class="depth-2 branch"><a href="walkmap.edge.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>edge</span></div></a></li><li class="depth-2 branch"><a href="walkmap.geometry.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>geometry</span></div></a></li><li class="depth-2 branch"><a href="walkmap.ocean.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>ocean</span></div></a></li><li class="depth-2 branch"><a href="walkmap.path.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>path</span></div></a></li><li class="depth-2 branch"><a href="walkmap.polygon.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>polygon</span></div></a></li><li class="depth-2 branch"><a href="walkmap.stl.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>stl</span></div></a></li><li class="depth-2 branch"><a href="walkmap.superstructure.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>superstructure</span></div></a></li><li class="depth-2 branch"><a href="walkmap.svg.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>svg</span></div></a></li><li class="depth-2 branch"><a href="walkmap.tag.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>tag</span></div></a></li><li class="depth-2 branch"><a href="walkmap.utils.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>utils</span></div></a></li><li class="depth-2"><a href="walkmap.vertex.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>vertex</span></div></a></li></ul></div><div class="sidebar secondary"><h3><a href="#top"><span class="inner">Public Vars</span></a></h3><ul></ul></div><div class="namespace-docs" id="content"><h1 class="anchor" id="top">walkmap.core</h1><div class="doc"><div class="markdown"><p>This namespace mostly gets used as a scratchpad for ideas which haven’t yet solidified.</p></div></div></div></body></html>
|
File diff suppressed because one or more lines are too long
|
@ -1,3 +1,3 @@
|
|||
<!DOCTYPE html PUBLIC ""
|
||||
"">
|
||||
<html><head><meta charset="UTF-8" /><title>walkmap.geometry documentation</title><link rel="stylesheet" type="text/css" href="css/default.css" /><link rel="stylesheet" type="text/css" href="css/highlight.css" /><script type="text/javascript" src="js/highlight.min.js"></script><script type="text/javascript" src="js/jquery.min.js"></script><script type="text/javascript" src="js/page_effects.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body><div id="header"><h2>Generated by <a href="https://github.com/weavejester/codox">Codox</a></h2><h1><a href="index.html"><span class="project-title"><span class="project-name">Walkmap</span> <span class="project-version">0.1.0-SNAPSHOT</span></span></a></h1></div><div class="sidebar primary"><h3 class="no-link"><span class="inner">Project</span></h3><ul class="index-link"><li class="depth-1 "><a href="index.html"><div class="inner">Index</div></a></li></ul><h3 class="no-link"><span class="inner">Topics</span></h3><ul><li class="depth-1 "><a href="intro.html"><div class="inner"><span>Introduction to walkmap</span></div></a></li></ul><h3 class="no-link"><span class="inner">Namespaces</span></h3><ul><li class="depth-1"><div class="no-link"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>walkmap</span></div></div></li><li class="depth-2 branch"><a href="walkmap.core.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>core</span></div></a></li><li class="depth-2 branch"><a href="walkmap.edge.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>edge</span></div></a></li><li class="depth-2 branch current"><a href="walkmap.geometry.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>geometry</span></div></a></li><li class="depth-2 branch"><a href="walkmap.path.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>path</span></div></a></li><li class="depth-2 branch"><a href="walkmap.polygon.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>polygon</span></div></a></li><li class="depth-2 branch"><a href="walkmap.stl.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>stl</span></div></a></li><li class="depth-2 branch"><a href="walkmap.svg.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>svg</span></div></a></li><li class="depth-2"><a href="walkmap.vertex.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>vertex</span></div></a></li></ul></div><div class="sidebar secondary"><h3><a href="#top"><span class="inner">Public Vars</span></a></h3><ul><li class="depth-1"><a href="walkmap.geometry.html#var-collinear.3F"><div class="inner"><span>collinear?</span></div></a></li><li class="depth-1"><a href="walkmap.geometry.html#var-on.3F"><div class="inner"><span>on?</span></div></a></li></ul></div><div class="namespace-docs" id="content"><h1 class="anchor" id="top">walkmap.geometry</h1><div class="doc"><div class="markdown"><p><strong>TODO</strong>: write docs</p></div></div><div class="public anchor" id="var-collinear.3F"><h3>collinear?</h3><div class="usage"><code>(collinear? v1 v2 v3)</code></div><div class="doc"><div class="markdown"><p>True if these vertices <code>v1</code>, <code>v2</code>, <code>v3</code> are colinear; false otherwise.</p></div></div><div class="src-link"><a href="https://github.com/simon-brooke/walkmap/blob/master/src/walkmap/geometry.clj#L9">view source</a></div></div><div class="public anchor" id="var-on.3F"><h3>on?</h3><div class="usage"><code>(on? e v)</code></div><div class="doc"><div class="markdown"><p>True if the vertex <code>v</code> is on the edge <code>e</code>.</p></div></div><div class="src-link"><a href="https://github.com/simon-brooke/walkmap/blob/master/src/walkmap/geometry.clj#L42">view source</a></div></div></div></body></html>
|
||||
<html><head><meta charset="UTF-8" /><title>walkmap.geometry documentation</title><link rel="stylesheet" type="text/css" href="css/default.css" /><link rel="stylesheet" type="text/css" href="css/highlight.css" /><script type="text/javascript" src="js/highlight.min.js"></script><script type="text/javascript" src="js/jquery.min.js"></script><script type="text/javascript" src="js/page_effects.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body><div id="header"><h2>Generated by <a href="https://github.com/weavejester/codox">Codox</a></h2><h1><a href="index.html"><span class="project-title"><span class="project-name">Walkmap</span> <span class="project-version">0.1.0-SNAPSHOT</span></span></a></h1></div><div class="sidebar primary"><h3 class="no-link"><span class="inner">Project</span></h3><ul class="index-link"><li class="depth-1 "><a href="index.html"><div class="inner">Index</div></a></li></ul><h3 class="no-link"><span class="inner">Topics</span></h3><ul><li class="depth-1 "><a href="dali-performance.html"><div class="inner"><span>Dali performance</span></div></a></li><li class="depth-1 "><a href="intro.html"><div class="inner"><span>Introduction to walkmap</span></div></a></li></ul><h3 class="no-link"><span class="inner">Namespaces</span></h3><ul><li class="depth-1"><div class="no-link"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>walkmap</span></div></div></li><li class="depth-2 branch"><a href="walkmap.core.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>core</span></div></a></li><li class="depth-2 branch"><a href="walkmap.edge.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>edge</span></div></a></li><li class="depth-2 branch current"><a href="walkmap.geometry.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>geometry</span></div></a></li><li class="depth-2 branch"><a href="walkmap.ocean.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>ocean</span></div></a></li><li class="depth-2 branch"><a href="walkmap.path.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>path</span></div></a></li><li class="depth-2 branch"><a href="walkmap.polygon.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>polygon</span></div></a></li><li class="depth-2 branch"><a href="walkmap.stl.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>stl</span></div></a></li><li class="depth-2 branch"><a href="walkmap.superstructure.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>superstructure</span></div></a></li><li class="depth-2 branch"><a href="walkmap.svg.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>svg</span></div></a></li><li class="depth-2 branch"><a href="walkmap.tag.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>tag</span></div></a></li><li class="depth-2 branch"><a href="walkmap.utils.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>utils</span></div></a></li><li class="depth-2"><a href="walkmap.vertex.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>vertex</span></div></a></li></ul></div><div class="sidebar secondary"><h3><a href="#top"><span class="inner">Public Vars</span></a></h3><ul></ul></div><div class="namespace-docs" id="content"><h1 class="anchor" id="top">walkmap.geometry</h1><div class="doc"><div class="markdown"><p><strong>TODO</strong>: write docs</p></div></div></div></body></html>
|
3
docs/codox/walkmap.id.html
Normal file
3
docs/codox/walkmap.id.html
Normal file
|
@ -0,0 +1,3 @@
|
|||
<!DOCTYPE html PUBLIC ""
|
||||
"">
|
||||
<html><head><meta charset="UTF-8" /><title>walkmap.id documentation</title><link rel="stylesheet" type="text/css" href="css/default.css" /><link rel="stylesheet" type="text/css" href="css/highlight.css" /><script type="text/javascript" src="js/highlight.min.js"></script><script type="text/javascript" src="js/jquery.min.js"></script><script type="text/javascript" src="js/page_effects.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body><div id="header"><h2>Generated by <a href="https://github.com/weavejester/codox">Codox</a></h2><h1><a href="index.html"><span class="project-title"><span class="project-name">Walkmap</span> <span class="project-version">0.1.0-SNAPSHOT</span></span></a></h1></div><div class="sidebar primary"><h3 class="no-link"><span class="inner">Project</span></h3><ul class="index-link"><li class="depth-1 "><a href="index.html"><div class="inner">Index</div></a></li></ul><h3 class="no-link"><span class="inner">Topics</span></h3><ul><li class="depth-1 "><a href="dali-performance.html"><div class="inner"><span>Dali performance</span></div></a></li><li class="depth-1 "><a href="intro.html"><div class="inner"><span>Introduction to walkmap</span></div></a></li></ul><h3 class="no-link"><span class="inner">Namespaces</span></h3><ul><li class="depth-1"><div class="no-link"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>walkmap</span></div></div></li><li class="depth-2 branch"><a href="walkmap.edge.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>edge</span></div></a></li><li class="depth-2 branch current"><a href="walkmap.id.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>id</span></div></a></li><li class="depth-2 branch"><a href="walkmap.microworld.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>microworld</span></div></a></li><li class="depth-2 branch"><a href="walkmap.ocean.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>ocean</span></div></a></li><li class="depth-2 branch"><a href="walkmap.path.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>path</span></div></a></li><li class="depth-2 branch"><a href="walkmap.polygon.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>polygon</span></div></a></li><li class="depth-2 branch"><a href="walkmap.read-svg.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>read-svg</span></div></a></li><li class="depth-2 branch"><a href="walkmap.routing.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>routing</span></div></a></li><li class="depth-2 branch"><a href="walkmap.stl.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>stl</span></div></a></li><li class="depth-2 branch"><a href="walkmap.superstructure.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>superstructure</span></div></a></li><li class="depth-2 branch"><a href="walkmap.svg.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>svg</span></div></a></li><li class="depth-2 branch"><a href="walkmap.tag.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>tag</span></div></a></li><li class="depth-2 branch"><a href="walkmap.utils.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>utils</span></div></a></li><li class="depth-2"><a href="walkmap.vertex.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>vertex</span></div></a></li></ul></div><div class="sidebar secondary"><h3><a href="#top"><span class="inner">Public Vars</span></a></h3><ul><li class="depth-1"><a href="walkmap.id.html#var-id"><div class="inner"><span>id</span></div></a></li></ul></div><div class="namespace-docs" id="content"><h1 class="anchor" id="top">walkmap.id</h1><div class="doc"><div class="markdown"><p>The namespace within which the privileged keyword <code>:walkmap.id/id</code> is defined.</p></div></div><div class="public anchor" id="var-id"><h3>id</h3><div class="usage"></div><div class="doc"><div class="markdown"><p>The magic id key walkmap uses, to distinguish it from all other uses of the unprotected keyword.</p></div></div><div class="src-link"><a href="https://github.com/simon-brooke/walkmap/blob/master/src/walkmap/id.clj#L4">view source</a></div></div></div></body></html>
|
3
docs/codox/walkmap.microworld.html
Normal file
3
docs/codox/walkmap.microworld.html
Normal file
File diff suppressed because one or more lines are too long
4
docs/codox/walkmap.ocean.html
Normal file
4
docs/codox/walkmap.ocean.html
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
3
docs/codox/walkmap.read-svg.html
Normal file
3
docs/codox/walkmap.read-svg.html
Normal file
File diff suppressed because one or more lines are too long
3
docs/codox/walkmap.routing.html
Normal file
3
docs/codox/walkmap.routing.html
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
21
docs/codox/walkmap.superstructure.html
Normal file
21
docs/codox/walkmap.superstructure.html
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
16
docs/codox/walkmap.tag.html
Normal file
16
docs/codox/walkmap.tag.html
Normal file
File diff suppressed because one or more lines are too long
5
docs/codox/walkmap.utils.html
Normal file
5
docs/codox/walkmap.utils.html
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
23
project.clj
23
project.clj
|
@ -4,13 +4,20 @@
|
|||
:doc/format :markdown}
|
||||
:output-path "docs/codox"
|
||||
:source-uri "https://github.com/simon-brooke/walkmap/blob/master/{filepath}#L{line}"}
|
||||
:dependencies [[org.clojure/clojure "1.8.0"]
|
||||
[org.clojure/math.numeric-tower "0.0.4"]
|
||||
[org.clojure/math.combinatorics "0.1.6"]
|
||||
[com.taoensso/timbre "4.10.0"]
|
||||
[dali "0.7.4"] ;; not currently used because performance issues.
|
||||
:dependencies [[org.clojure/algo.generic "1.0.0"]
|
||||
[org.clojure/core.memoize "1.1.266"]
|
||||
[org.clojure/clojure "1.11.2"]
|
||||
[org.clojure/data.zip "1.1.0"]
|
||||
[org.clojure/math.numeric-tower "0.1.0"]
|
||||
[org.clojure/math.combinatorics "0.3.0"]
|
||||
[com.taoensso/timbre "6.5.0"]
|
||||
;; [dali "1.0.2"] ;; not currently used because performance issues.
|
||||
[hiccup "1.0.5"]
|
||||
[macroz/search "0.3.0"]
|
||||
[me.raynes/fs "1.4.6"]
|
||||
[mw-engine "0.3.0-SNAPSHOT"]
|
||||
[net.mikera/core.matrix "0.63.0"]
|
||||
[net.mikera/vectorz-clj "0.48.0"]
|
||||
[smee/binary "0.5.5"]]
|
||||
:deploy-repositories [["releases" :clojars]
|
||||
["snapshots" :clojars]]
|
||||
|
@ -18,7 +25,9 @@
|
|||
:license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
|
||||
:url "https://www.eclipse.org/legal/epl-2.0/"}
|
||||
:plugins [[lein-cloverage "1.1.1"]
|
||||
[lein-codox "0.10.7"]]
|
||||
[lein-codox "0.10.7"]
|
||||
[org.clojars.benfb/lein-gorilla "0.7.0"]
|
||||
]
|
||||
:release-tasks [["vcs" "assert-committed"]
|
||||
["change" "version" "leiningen.release/bump-version" "release"]
|
||||
["vcs" "commit"]
|
||||
|
@ -29,5 +38,5 @@
|
|||
["uberjar"]
|
||||
["change" "version" "leiningen.release/bump-version"]
|
||||
["vcs" "commit"]]
|
||||
:repl-options {:init-ns walkmap.core}
|
||||
:repl-options {:init-ns cc.journeyman.walkmap.superstructure}
|
||||
:url "https://simon-brooke.github.io/walkmap/")
|
||||
|
|
1907820
resources/200x200/superstructure-pretty-20240410.edn
Normal file
1907820
resources/200x200/superstructure-pretty-20240410.edn
Normal file
File diff suppressed because it is too large
Load diff
1
resources/200x200/superstructure-raw-20240410.edn
Normal file
1
resources/200x200/superstructure-raw-20240410.edn
Normal file
File diff suppressed because one or more lines are too long
1
resources/iom/isle_of_man.edn
Normal file
1
resources/iom/isle_of_man.edn
Normal file
File diff suppressed because one or more lines are too long
1
resources/iom/isle_of_man.html
Normal file
1
resources/iom/isle_of_man.html
Normal file
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 6.1 KiB |
1
resources/iom/isle_of_man_75.edn
Normal file
1
resources/iom/isle_of_man_75.edn
Normal file
File diff suppressed because one or more lines are too long
1
resources/iom/isle_of_man_80.edn
Normal file
1
resources/iom/isle_of_man_80.edn
Normal file
File diff suppressed because one or more lines are too long
1
resources/iom/isle_of_man_80.html
Normal file
1
resources/iom/isle_of_man_80.html
Normal file
File diff suppressed because one or more lines are too long
1
resources/iom/isle_of_man_85.edn
Normal file
1
resources/iom/isle_of_man_85.edn
Normal file
File diff suppressed because one or more lines are too long
1
resources/iom/isle_of_man_85.html
Normal file
1
resources/iom/isle_of_man_85.html
Normal file
File diff suppressed because one or more lines are too long
1
resources/iom/isle_of_man_90.edn
Normal file
1
resources/iom/isle_of_man_90.edn
Normal file
File diff suppressed because one or more lines are too long
1
resources/iom/isle_of_man_90.html
Normal file
1
resources/iom/isle_of_man_90.html
Normal file
File diff suppressed because one or more lines are too long
273417
resources/iom/superstructure_20140412.edn
Normal file
273417
resources/iom/superstructure_20140412.edn
Normal file
File diff suppressed because it is too large
Load diff
Binary file not shown.
155
resources/rules/settlement_rules.txt
Normal file
155
resources/rules/settlement_rules.txt
Normal file
|
@ -0,0 +1,155 @@
|
|||
# Human settlement
|
||||
|
||||
;; This rule set attempts to model human settlement in a landscape. It models
|
||||
;; western European pre-history moderately well. Settlement first occurs as
|
||||
;; nomadic camps on coastal promentaries (cells with four or more neighbours
|
||||
;; that are water). This represents 'kitchen-midden' mesolithic settlement.
|
||||
;;
|
||||
;; As grassland becomes available near camps, pastoralists appear, and will
|
||||
;; follow their herds inland. When pastoralists have available fertile land,
|
||||
;; they will till the soil and plant crops, and in doing so will establish
|
||||
;; permanent settlements; this is approximately a neolithic stage.
|
||||
;;
|
||||
;; Where soil is fertile, settlements will cluster, and markets will appear.
|
||||
;; where there is sufficient settlement, the markets become permanent, and you
|
||||
;; have the appearance of towns. This takes us roughly into the bronze age.
|
||||
;;
|
||||
;; This is quite a complex ruleset, and runs quite slowly. However, it does
|
||||
;; model some significant things. Soil gains in fertility under woodland; deep
|
||||
;; loams and podzols build up over substantial time. Agriculture depletes
|
||||
;; fertility. So if forest has become well established before human settlement
|
||||
;; begins, a higher population (more crops) will eventually be sustainable,
|
||||
;; whereas if human population starts early the deep fertile soils will not
|
||||
;; establish and you will have more pastoralism, supporting fewer permanent
|
||||
;; settlements.
|
||||
|
||||
;; hack to speed up processing on the 'great britain and ireland' map
|
||||
if state is water then state should be water
|
||||
|
||||
;; nomads make their first significant camp near water because of fish and
|
||||
;; shellfish (kitchen-midden people)
|
||||
if state is in grassland or heath and more than 3 neighbours are water and generation is more than 20 then state should be camp
|
||||
|
||||
;; sooner or later nomads learn to keep flocks
|
||||
if state is in grassland or heath and some neighbours are camp then 1 chance in 2 state should be pasture
|
||||
|
||||
;; and more herds support more people
|
||||
if state is in grassland or heath and more than 2 neighbours are pasture then 1 chance in 3 state should be camp
|
||||
if state is pasture and more than 3 neighbours are pasture and fewer than 1 neighbours are camp and fewer than 1 neighbours within 2 are house then state should be camp
|
||||
|
||||
;; the idea of agriculture spreads
|
||||
if state is in grassland or heath and some neighbours within 2 are house then state should be pasture
|
||||
|
||||
;; nomads don't move on while the have crops growing. That would be silly!
|
||||
if state is camp and some neighbours are ploughland then state should be camp
|
||||
|
||||
;; Impoverished pasture can't be grazed permanently
|
||||
if state is pasture and fertility is less than 2 then 1 chance in 3 state should be heath
|
||||
|
||||
;; nomads move on
|
||||
if state is camp then 1 chance in 5 state should be waste
|
||||
|
||||
;; pasture that's too far from a house or camp will be abandoned
|
||||
if state is pasture and fewer than 1 neighbours within 3 are house and fewer than 1 neighbours within 2 are camp then state should be heath
|
||||
|
||||
;; markets spring up near settlements
|
||||
if state is in grassland or pasture and more than 1 neighbours are house then 1 chance in 10 state should be market
|
||||
|
||||
;; good fertile pasture close to settlement will be ploughed for crops
|
||||
if state is pasture and fertility is more than 10 and altitude is less than 100 and some neighbours are camp or some neighbours are house then state should be ploughland
|
||||
|
||||
if state is ploughland then state should be crop
|
||||
|
||||
;; after the crop is harvested, the land is allowed to lie fallow. But cropping
|
||||
;; depletes fertility.
|
||||
if state is crop then state should be grassland and fertility should be fertility - 1
|
||||
|
||||
;; if there's reliable food available, nomads build permanent settlements
|
||||
if state is in camp or abandoned and some neighbours are crop then state should be house
|
||||
if state is abandoned and some neighbours are pasture then state should be house
|
||||
;; people camp near to markets
|
||||
if state is in waste or grassland and some neighbours are market then state should be camp
|
||||
|
||||
;; a market in a settlement survives
|
||||
if state is market and some neighbours are inn then state should be market
|
||||
if state is market then state should be grassland
|
||||
|
||||
;; a house near a market in a settlement will become an inn
|
||||
if state is house and some neighbours are market and more than 1 neighbours are house then 1 chance in 5 state should be inn
|
||||
;; but it will need some local custom to survive
|
||||
if state is inn and fewer than 3 neighbours are house then state should be house
|
||||
|
||||
;; if there aren't enough resources houses should be abandoned
|
||||
;; resources from fishing
|
||||
if state is house and more than 2 neighbours are water then state should be house
|
||||
;; from farming
|
||||
if state is house and some neighbours are pasture then state should be house
|
||||
if state is house and some neighbours are ploughland then state should be house
|
||||
if state is house and some neighbours are crop then state should be house
|
||||
;; from the market
|
||||
if state is house and some neighbours are market then state should be house
|
||||
if state is house then 1 chance in 2 state should be abandoned
|
||||
if state is abandoned then 1 chance in 5 state should be waste
|
||||
|
||||
|
||||
## Vegetation rules
|
||||
;; rules which populate the world with plants
|
||||
|
||||
;; Occasionally, passing birds plant tree seeds into grassland
|
||||
|
||||
if state is grassland then 1 chance in 10 state should be heath
|
||||
|
||||
;; heath below the treeline grows gradually into forest
|
||||
|
||||
if state is heath and altitude is less than 120 then state should be scrub
|
||||
if state is scrub then 1 chance in 5 state should be forest
|
||||
|
||||
;; Forest on fertile land grows to climax
|
||||
|
||||
if state is forest and fertility is more than 5 and altitude is less than 70 then state should be climax
|
||||
|
||||
;; Climax forest occasionally catches fire (e.g. lightning strikes)
|
||||
|
||||
if state is climax then 1 chance in 500 state should be fire
|
||||
|
||||
;; Forest neighbouring fires is likely to catch fire. So are buildings.
|
||||
if state is in forest or climax or camp or house or inn and some neighbours are fire then 1 chance in 3 state should be fire
|
||||
|
||||
;; Climax forest near to settlement may be cleared for timber
|
||||
if state is in climax and more than 3 neighbours within 2 are house then state should be scrub
|
||||
|
||||
;; After fire we get waste
|
||||
|
||||
if state is fire then state should be waste
|
||||
|
||||
;; waste near settlement that is fertile becomes ploughland
|
||||
if state is waste and fertility is more than 10 and some neighbours are house or some neighbours are camp then state should be ploughland
|
||||
|
||||
;; And after waste we get pioneer species; if there's a woodland seed
|
||||
;; source, it's going to be heath, otherwise grassland.
|
||||
|
||||
if state is waste and some neighbours are scrub then state should be heath
|
||||
if state is waste and some neighbours are forest then state should be heath
|
||||
if state is waste and some neighbours are climax then state should be heath
|
||||
if state is waste then state should be grassland
|
||||
|
||||
|
||||
## Potential blockers
|
||||
|
||||
;; Forest increases soil fertility.
|
||||
if state is in forest or climax then fertility should be fertility + 1
|
||||
|
||||
## Initialisation rules
|
||||
|
||||
;; Rules which deal with state 'new' will waste less time if they're near the
|
||||
;; end of the file
|
||||
|
||||
;; below the waterline we have water.
|
||||
|
||||
if state is new and altitude is less than 10 then state should be water
|
||||
|
||||
;; above the snowline we have snow.
|
||||
if state is new and altitude is more than 200 then state should be snow
|
||||
|
||||
;; otherwise, we have grassland.
|
||||
if state is new then state should be grassland
|
185
src/cc/journeyman/walkmap/edge.clj
Normal file
185
src/cc/journeyman/walkmap/edge.clj
Normal file
|
@ -0,0 +1,185 @@
|
|||
(ns cc.journeyman.walkmap.edge
|
||||
"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 [clojure.math.numeric-tower :as m]
|
||||
[cc.journeyman.walkmap.vertex :refer [canonicalise check-vertex ensure2d ensure3d vertex vertex= vertex?]]))
|
||||
|
||||
(defn edge
|
||||
"Return an edge between vertices `v1` and `v2`."
|
||||
[v1 v2]
|
||||
{:kind :edge
|
||||
:walkmap.id/id (keyword (gensym "edge"))
|
||||
:start (check-vertex v1)
|
||||
:end (check-vertex v2)})
|
||||
|
||||
(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
|
||||
keys shall be a vertex."
|
||||
[o]
|
||||
(and
|
||||
(map? o)
|
||||
(vertex? (:start o))
|
||||
(vertex? (:end 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 centre
|
||||
"Return the vertex that represents the centre of this `edge`."
|
||||
[edge]
|
||||
(let [s (ensure3d (:start edge))
|
||||
e (ensure3d (:end edge))]
|
||||
(vertex
|
||||
(+ (:x s) (/ (- (:x e) (:x s)) 2))
|
||||
(+ (:y s) (/ (- (:y e) (:y s)) 2))
|
||||
(+ (:z s) (/ (- (:z e) (:z s)) 2)))))
|
||||
|
||||
(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')]
|
||||
(canonicalise
|
||||
(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."
|
||||
[& edges]
|
||||
(let [uvs (map unit-vector edges)]
|
||||
(every?
|
||||
#(vertex= % (first uvs))
|
||||
(rest uvs))))
|
||||
|
||||
(defn collinear?
|
||||
"True if edges `e1` and `e2` are collinear with one another."
|
||||
[e1 e2]
|
||||
(parallel?
|
||||
e1
|
||||
e2
|
||||
(if (vertex= (:start e1) (:start e2))
|
||||
{:start (:start e1) :end (:end e2)}
|
||||
{:start (:start e1) :end (:start e2)})))
|
||||
|
||||
(defn collinear2d?
|
||||
"True if the projections of edges `e1`, `e2` onto the x, y plane are
|
||||
collinear."
|
||||
[e1 e2]
|
||||
(collinear? {:start (ensure2d (:start e1)) :end (ensure2d (:end e1))}
|
||||
{:start (ensure2d (:start e2)) :end (ensure2d (:end e2))}))
|
||||
|
||||
(defn minimaxd
|
||||
"Apply function `f` to `coord` of the vertices at start and end of `edge`
|
||||
and return the result. Intended use case is `f` = `min` or `max`, `coord`
|
||||
is `:x`, `:y` or `:z`. No checks are made for sane arguments."
|
||||
[edge coord f]
|
||||
(apply f (list (coord (:start edge)) (coord (:end edge)))))
|
||||
|
||||
(defn on?
|
||||
"True if the vertex `v` is on the edge `e`."
|
||||
[e v]
|
||||
(let [p (ensure3d (:start e))
|
||||
q (ensure3d v)
|
||||
r (ensure3d (:end e))]
|
||||
(and
|
||||
(collinear? (edge p q) (edge q r))
|
||||
(<= (:x q) (max (:x p) (:x r)))
|
||||
(>= (:x q) (min (:x p) (:x r)))
|
||||
(<= (:y q) (max (:y p) (:y r)))
|
||||
(>= (:y q) (min (:y p) (:y r)))
|
||||
(<= (:z q) (max (:z p) (:z r)))
|
||||
(>= (:z q) (min (:z p) (:z r))))))
|
||||
|
||||
(defn on2d?
|
||||
"True if vertex `v` is on edge `e` when projected onto the x, y plane."
|
||||
[e v]
|
||||
(on? (edge (ensure2d (:start e)) (ensure2d (:end e))) v))
|
||||
|
||||
(defn overlaps2d?
|
||||
"True if the recangle in the x,y plane bisected by edge `e1` overlaps that
|
||||
bisected by edge `e2`. It is an error if either `e1` or `e2` is not an edge.
|
||||
|
||||
If `c1` is passed it should be the first coordinate of the plane of
|
||||
projection on which the overlap is sought (default: `:x`); similarly `c2`
|
||||
should be the second such coordinate (default: `:y`)."
|
||||
([e1 e2]
|
||||
(overlaps2d? e1 e2 :x :y))
|
||||
([e1 e2 c1 c2]
|
||||
(when (and (edge? e1) (edge? e2))
|
||||
(and
|
||||
(> (minimaxd e1 c1 max) (minimaxd e2 c1 min))
|
||||
(< (minimaxd e1 c1 min) (minimaxd e2 c1 max))
|
||||
(> (minimaxd e1 c2 max) (minimaxd e2 c2 min))
|
||||
(< (minimaxd e1 c2 min) (minimaxd e2 c2 max))))))
|
||||
|
||||
|
||||
(defn intersection2d
|
||||
"The probability of two lines intersecting in 3d space is low, and actually
|
||||
that is mostly not something we're interested in. We're interested in
|
||||
intersection in the `x,y` plane. This function returns a vertex representing
|
||||
a point vertically over the intersection of edges `e1`, `e2` in the `x,y`
|
||||
plane, whose `z` coordinate is
|
||||
|
||||
* 0 if both edges are 2d (i.e. have missing or zero `z` coordinates);
|
||||
* if one edge is 2d, then the point on the other edge over the intersection;
|
||||
* otherwise, the average of the z coordinates of the points on the two
|
||||
edges over the intersection.
|
||||
|
||||
If no such intersection exists, `nil` is returned.
|
||||
|
||||
It is an error, and an exception will be thrown, if either `e1` or `e2` is
|
||||
not an edge."
|
||||
([e1 e2]
|
||||
(intersection2d e1 e2 :x :y :z))
|
||||
([e1 e2 c1 c2 _c3]
|
||||
(if (and (edge? e1) (edge? e2))
|
||||
(when
|
||||
(overlaps2d? e1 e2) ;; relatively cheap check
|
||||
(if
|
||||
(collinear2d? e1 e2)
|
||||
;; any point within the overlap will do, but we'll pick the end of e1
|
||||
;; which is on e2
|
||||
(if (on2d? e2 (:start e1)) (:start e1) (:end e1))
|
||||
;; blatantly stolen from
|
||||
;; https://gist.github.com/cassiel/3e725b49670356a9b936
|
||||
(let [x1 (c1 (:start e1))
|
||||
x2 (c1 (:end e1))
|
||||
x3 (c1 (:start e2))
|
||||
x4 (c1 (:end e2))
|
||||
y1 (c2 (:start e1))
|
||||
y2 (c2 (:end e1))
|
||||
y3 (c2 (:start e2))
|
||||
y4 (c2 (:end e2))
|
||||
denom (- (* (- x1 x2) (- y3 y4))
|
||||
(* (- y1 y2) (- x3 x4)))
|
||||
x1y2-y1x2 (- (* x1 y2) (* y1 x2))
|
||||
x3y4-y3x4 (- (* x3 y4) (* y3 x4))
|
||||
px-num (- (* x1y2-y1x2 (- x3 x4))
|
||||
(* (- x1 x2) x3y4-y3x4))
|
||||
py-num (- (* x1y2-y1x2 (- y3 y4))
|
||||
(* (- y1 y2) x3y4-y3x4))
|
||||
result (when-not (zero? denom)
|
||||
(vertex (/ px-num denom) (/ py-num denom)))]
|
||||
(when (and result (on2d? e1 result) (on2d? e2 result)) result))))
|
||||
(throw (IllegalArgumentException.
|
||||
(str
|
||||
"Both `e1` and `e2` must be edges."
|
||||
(map #(or (:kind %) (type %)) [e1 e2])))))))
|
||||
|
8
src/cc/journeyman/walkmap/id.clj
Normal file
8
src/cc/journeyman/walkmap/id.clj
Normal file
|
@ -0,0 +1,8 @@
|
|||
(ns cc.journeyman.walkmap.id
|
||||
"The namespace within which the privileged keyword `:walkmap.id/id` is defined.")
|
||||
|
||||
(def ^:const id
|
||||
"The magic id key walkmap uses, to distinguish it from all other uses of
|
||||
the unprotected keyword."
|
||||
::id)
|
||||
|
75
src/cc/journeyman/walkmap/microworld.clj
Normal file
75
src/cc/journeyman/walkmap/microworld.clj
Normal file
|
@ -0,0 +1,75 @@
|
|||
(ns cc.journeyman.walkmap.microworld
|
||||
"An interface between walkmap and microworld, to allow use of microworld
|
||||
functionality to model things like rainfall, soil fertility, settlement
|
||||
and so on."
|
||||
(:require [cc.journeyman.walkmap.polygon :refer [rectangle]]
|
||||
[cc.journeyman.walkmap.superstructure :refer [store]]
|
||||
[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 IPersistentMap]
|
||||
[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
|
||||
in the cell by that vector."
|
||||
([cell]
|
||||
(cell->polygon cell (vertex 1 1 1)))
|
||||
([cell scale-vector]
|
||||
(tag
|
||||
(assoc
|
||||
(merge
|
||||
cell
|
||||
(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))]
|
||||
(rectangle
|
||||
(vertex s w z)
|
||||
(vertex n e z))))
|
||||
:walkmap.id/id
|
||||
(keyword (gensym "mw-cell")))
|
||||
(:state cell))))
|
||||
|
||||
(defn load-microworld-edn
|
||||
"While it would be possible to call MicroWorld functions directly from
|
||||
Walkmap, the fact is that running MicroWorld is so phenomenally
|
||||
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."
|
||||
([^String filename]
|
||||
(load-microworld-edn filename :mw))
|
||||
([^String filename ^Keyword map-kind]
|
||||
(load-microworld-edn filename map-kind nil))
|
||||
([^String filename ^Keyword mapkind ^IPersistentMap superstucture]
|
||||
(load-microworld-edn filename mapkind superstucture (vertex 1 1 1)))
|
||||
([^String filename ^Keyword map-kind ^IPersistentMap superstructure ^IPersistentMap _scale-vertex]
|
||||
(when-not
|
||||
(keyword? map-kind)
|
||||
(throw (IllegalArgumentException.
|
||||
(truncate
|
||||
(format "Must be a keyword: %s." (or map-kind "nil")) 80))))
|
||||
(let [mw (try
|
||||
(with-open [r (reader filename)]
|
||||
(edn/read (PushbackReader. r)))
|
||||
(catch RuntimeException e
|
||||
(error "Error parsing edn file '%s': %s\n"
|
||||
filename (.getMessage e))))
|
||||
polys (reduce
|
||||
concat
|
||||
(map (fn [row] (map cell->polygon row)) mw))]
|
||||
(if (map? superstructure)
|
||||
(reduce
|
||||
#(store %2 %1)
|
||||
superstructure
|
||||
polys)
|
||||
polys))))
|
||||
|
||||
|
||||
|
||||
|
50
src/cc/journeyman/walkmap/mw_stl.clj
Normal file
50
src/cc/journeyman/walkmap/mw_stl.clj
Normal file
|
@ -0,0 +1,50 @@
|
|||
(ns cc.journeyman.walkmap.mw-stl
|
||||
"Convert from Microworld to STL format"
|
||||
(:require [cc.journeyman.walkmap.ocean :refer [cull-ocean-facets]]
|
||||
[cc.journeyman.walkmap.polygon :refer [polygon]]
|
||||
[cc.journeyman.walkmap.vertex :refer [vertex]]
|
||||
[mw-engine.utils :refer [get-cell map-world]]))
|
||||
|
||||
(defn mean-altitude
|
||||
[cells]
|
||||
(let [c (remove nil? cells)]
|
||||
(if (= (count c) 1)
|
||||
(:altitude (first c))
|
||||
(/ (reduce + (map :altitude c)) (count c)))))
|
||||
|
||||
(defn cell->facets
|
||||
"Convert a cell into facets.
|
||||
|
||||
All facets in an STL file must be triangles, so each MicroWorld cell needs
|
||||
to be split into two. I am not at present seeing a rational rule for
|
||||
splitting cells, so at this stage I'm going to do it randomly to prevent
|
||||
parallel ridge artifacts appearing in the output.
|
||||
|
||||
This function is designed to be used with `mw-engint.utils/map-world`, q.v."
|
||||
[world cell]
|
||||
(let [;; bounds
|
||||
n (:y cell)
|
||||
e (:x cell)
|
||||
s (inc n)
|
||||
w (inc e)
|
||||
;; corner altitudes
|
||||
nea (mean-altitude [cell (get-cell world (dec n) (dec e))])
|
||||
nwa (mean-altitude [cell (get-cell world (dec n) (inc e))])
|
||||
swa (mean-altitude [cell (get-cell world (inc n) (inc e))])
|
||||
sea (mean-altitude [cell (get-cell world (inc n) (dec e))])]
|
||||
(if (rand-nth [true false])
|
||||
[(polygon (vertex n e nea) (vertex n w nwa) (vertex s e sea))
|
||||
(polygon (vertex n w nwa) (vertex s e sea) (vertex s w swa))]
|
||||
[(polygon (vertex n e nea) (vertex n w nwa) (vertex s w sea))
|
||||
(polygon (vertex n e nwa) (vertex s e sea) (vertex s w swa))])))
|
||||
|
||||
(defn mw->stl
|
||||
"Return an STL structure representing the topology of this MicroWorld world
|
||||
`mw`. If `title` is supplied use that as the title of the STL structure."
|
||||
([mw]
|
||||
(mw->stl mw nil))
|
||||
([mw title]
|
||||
(let [facets (cull-ocean-facets (flatten (map-world mw cell->facets)))
|
||||
stl {:facets facets
|
||||
:count (count facets)}]
|
||||
(if title (assoc stl :title title) stl))))
|
33
src/cc/journeyman/walkmap/ocean.clj
Normal file
33
src/cc/journeyman/walkmap/ocean.clj
Normal file
|
@ -0,0 +1,33 @@
|
|||
(ns cc.journeyman.walkmap.ocean
|
||||
"Deal with (specifically, at this stage, cull) ocean areas"
|
||||
(:require [cc.journeyman.walkmap.polygon :refer [triangle?]]
|
||||
[cc.journeyman.walkmap.stl :refer [stl?]]
|
||||
[cc.journeyman.walkmap.utils :refer [=ish]]))
|
||||
|
||||
(def ^:dynamic *sea-level*
|
||||
"The sea level on heightmaps we're currently handling. If characters are to
|
||||
be able to swin in the sea, we must model the sea bottom, so we need
|
||||
heightmaps which cover at least the continental shelf. However, the sea
|
||||
bottom is not walkable territory and can be culled from walkmaps.
|
||||
|
||||
**Note** must be a floating point number. `(= 0 0.0)` returns `false`!"
|
||||
0.0)
|
||||
|
||||
(defn ocean?
|
||||
"Of a `facet`, is the altitude of every vertice equal to `*sea-level*`?"
|
||||
[facet]
|
||||
(every?
|
||||
#(=ish % *sea-level*)
|
||||
(map :z (:vertices facet))))
|
||||
|
||||
(defn cull-ocean-facets
|
||||
"Ye cannae walk on water. Remove all facets from this `stl` structure which
|
||||
are at sea level."
|
||||
[stl-or-seq]
|
||||
(cond
|
||||
(and (seq? stl-or-seq)
|
||||
(every? triangle? stl-or-seq)) (remove ocean? stl-or-seq)
|
||||
(stl? stl-or-seq)(assoc stl-or-seq :facets
|
||||
(cull-ocean-facets (:facets stl-or-seq)))
|
||||
:else (throw (IllegalArgumentException.
|
||||
"Expected STL structure, or sequence of triangles."))))
|
91
src/cc/journeyman/walkmap/path.clj
Normal file
91
src/cc/journeyman/walkmap/path.clj
Normal file
|
@ -0,0 +1,91 @@
|
|||
(ns cc.journeyman.walkmap.path
|
||||
"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 [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?
|
||||
"True if `o` satisfies the conditions for a path. A path shall be a map
|
||||
having the key `:vertices`, whose value shall be a sequence of vertices as
|
||||
defined in `walkmap.vertex`."
|
||||
[o]
|
||||
(let
|
||||
[v (:vertices o)]
|
||||
(and
|
||||
(seq? v)
|
||||
(> (count v) 1)
|
||||
(every? vertex? v)
|
||||
(:walkmap.id/id o)
|
||||
(or (nil? (:kind o)) (= (:kind o) :path)))))
|
||||
|
||||
(defn path
|
||||
"Return a path constructed from these `vertices`."
|
||||
[& vertices]
|
||||
(if
|
||||
(> (count (check-vertices vertices)) 1)
|
||||
{:vertices vertices :walkmap.id/id (keyword (gensym "path")) :kind :path}
|
||||
(throw (IllegalArgumentException. "Path must have more than one vertex."))))
|
||||
|
||||
(defmacro check-path
|
||||
"If `o` is not a path, throw an `IllegalArgumentException` with an
|
||||
appropriate message; otherwise, returns `o`. Macro, so exception is thrown
|
||||
from the calling function."
|
||||
[o]
|
||||
`(check-kind-type ~o path? :path))
|
||||
|
||||
(defmacro check-paths
|
||||
"If `o` is not a sequence of paths, throw an `IllegalArgumentException` with an
|
||||
appropriate message; otherwise, returns `o`. Macro, so exception is thrown
|
||||
from the calling function."
|
||||
[o]
|
||||
`(check-kind-type-seq ~o path? :path))
|
||||
|
||||
(defn polygon->path
|
||||
"If `o` is a polygon, return an equivalent path. What's different about
|
||||
a path is that in polygons there is an implicit edge between the first
|
||||
vertex and the last. In paths, there isn't, so we need to add that
|
||||
edge explicitly.
|
||||
|
||||
If `o` is not a polygon, will throw an exception."
|
||||
[o]
|
||||
;; this is breaking, but I have NO IDEA why!
|
||||
;; (check-polygon o polygon? :polygon)
|
||||
(assoc (dissoc o :vertices)
|
||||
:kind :path
|
||||
;; `concat` rather than `conj` because order matters.
|
||||
:vertices (concat (:vertices o) (list (first (:vertices 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
|
||||
;; 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) (first (rest o)))
|
||||
(path->edges (rest o))))
|
||||
(path? o) (path->edges (:vertices o))
|
||||
(polygon? o) (path->edges (polygon->path 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]
|
||||
(reduce + (map e/length (path->edges (check-path path)))))
|
178
src/cc/journeyman/walkmap/polygon.clj
Normal file
178
src/cc/journeyman/walkmap/polygon.clj
Normal file
|
@ -0,0 +1,178 @@
|
|||
(ns cc.journeyman.walkmap.polygon
|
||||
"Essentially the specification for things we shall consider to be polygons."
|
||||
(:require [clojure.string :as s]
|
||||
[cc.journeyman.walkmap.edge :as e]
|
||||
[cc.journeyman.walkmap.tag :as t]
|
||||
[cc.journeyman.walkmap.utils :refer [check-kind-type
|
||||
check-kind-type-seq
|
||||
kind-type
|
||||
not-yet-implemented]]
|
||||
[cc.journeyman.walkmap.vertex :refer [check-vertex check-vertices vertex vertex?]]))
|
||||
|
||||
(defn polygon?
|
||||
"True if `o` satisfies the conditions for a polygon. A polygon shall be a
|
||||
map which has a value for the key `:vertices`, where that value is a sequence
|
||||
of vertices."
|
||||
[o]
|
||||
(let
|
||||
[v (:vertices o)]
|
||||
(and
|
||||
(coll? v)
|
||||
(> (count v) 2)
|
||||
(every? vertex? v)
|
||||
(:walkmap.id/id o)
|
||||
(or (nil? (:kind o)) (= (:kind o) :polygon)))))
|
||||
|
||||
(defmacro check-polygon
|
||||
"If `o` is not a polygon, throw an `IllegalArgumentException` with an
|
||||
appropriate message; otherwise, returns `o`. Macro, so exception is thrown
|
||||
from the calling function."
|
||||
[o]
|
||||
`(check-kind-type ~o polygon? :polygon))
|
||||
|
||||
(defmacro check-polygons
|
||||
"If `o` is not a sequence of polygons, throw an `IllegalArgumentException` with an
|
||||
appropriate message; otherwise, returns `o`. Macro, so exception is thrown
|
||||
from the calling function."
|
||||
[o]
|
||||
`(check-kind-type-seq ~o polygon? :polygon))
|
||||
|
||||
(defn triangle?
|
||||
"True if `o` satisfies the conditions for a triangle. A triangle shall be a
|
||||
polygon with exactly three vertices."
|
||||
[o]
|
||||
(and
|
||||
(coll? o)
|
||||
(#{:polygon :triangle} (kind-type o))
|
||||
(= (count (:vertices o)) 3)))
|
||||
|
||||
(defmacro check-triangle
|
||||
"If `o` is not a triangle, throw an `IllegalArgumentException` with an
|
||||
appropriate message; otherwise, returns `o`. Macro, so exception is thrown
|
||||
from the calling function."
|
||||
[o]
|
||||
`(check-kind-type ~o triangle? :triangle))
|
||||
|
||||
(def recognised-polygon-types #{:triangle
|
||||
:quadrilateral
|
||||
:pentagon
|
||||
:hexagon
|
||||
:heptagon
|
||||
:octogon
|
||||
:polygon})
|
||||
|
||||
(defn- poly-kind
|
||||
"Return a keyword representing the kind of polygon which has this many
|
||||
`vertices`."
|
||||
[vertices]
|
||||
(case (count vertices)
|
||||
3 :triangle
|
||||
4 :quadrilateral
|
||||
5 :pentagon
|
||||
6 :hexagon
|
||||
7 :heptagon
|
||||
8 :octogon
|
||||
;;else
|
||||
:polygon))
|
||||
|
||||
(defn polygon
|
||||
"Return a polygon constructed from these `vertices`."
|
||||
[& vertices]
|
||||
(if
|
||||
(> (count vertices) 2)
|
||||
{:vertices (check-vertices vertices)
|
||||
:walkmap.id/id (keyword (gensym "poly"))
|
||||
:kind (poly-kind vertices)}
|
||||
(throw (IllegalArgumentException.
|
||||
"A polygon must have at least 3 vertices."))))
|
||||
|
||||
(defn rectangle
|
||||
"Return a rectangle, with edges aligned east-west and north-south, whose
|
||||
south-west corner is the vertex `vsw` and whose north-east corner is the
|
||||
vertex `vne`."
|
||||
[vsw vne]
|
||||
;; we can actually create any rectangle in the xy plane based on two opposite
|
||||
;; corners, but the maths are a bit to advanced for me today. TODO: do it!
|
||||
(let [vnw (vertex (:x (check-vertex vsw))
|
||||
(:y (check-vertex vne))
|
||||
(/ (reduce + (map #(or (:z %) 0) [vsw vne])) 2))
|
||||
vse (vertex (:x vne)
|
||||
(:y vsw)
|
||||
(/ (reduce + (map #(or (:z %) 0) [vsw vne])) 2))
|
||||
height-order (sort-by :z [vsw vne])]
|
||||
(t/tag
|
||||
(assoc
|
||||
(polygon vsw vnw vne vse)
|
||||
:gradient
|
||||
(e/unit-vector (e/edge (first height-order) (last height-order)))
|
||||
:centre
|
||||
(vertex (+ (:x vsw) (/ (- (:x vne) (:x vsw)) 2))
|
||||
(+ (:y vsw) (/ (- (:y vne) (:y vsw)) 2))
|
||||
(:z vse)))
|
||||
:rectangle)))
|
||||
|
||||
;; (rectangle (vertex 1 2 3) (vertex 7 9 4))
|
||||
|
||||
(defn gradient
|
||||
"Return a polygon like `triangle` but with a key `:gradient` whose value is a
|
||||
unit vector representing the gradient across `triangle`."
|
||||
[triangle]
|
||||
(let [order (sort #(max (:z %1) (:z %2))
|
||||
(:vertices (check-triangle triangle)))
|
||||
highest (first order)
|
||||
lowest (last order)]
|
||||
(assoc triangle :gradient (e/unit-vector (e/edge lowest highest)))))
|
||||
|
||||
(defn triangle-centre
|
||||
"Return a canonicalised `facet` (i.e. a triangular polygon) with an added
|
||||
key `:centre` whose value represents the centre of this facet in 3
|
||||
dimensions. This only works for triangles, so is here not in
|
||||
`walkmap.polygon`. It is an error (although no exception is currently
|
||||
thrown) if the object past is not a triangular polygon."
|
||||
[facet]
|
||||
(let [vs (:vertices (check-triangle facet))
|
||||
v1 (first vs)
|
||||
opposite (e/edge (nth vs 1) (nth vs 2))
|
||||
oc (e/centre opposite)]
|
||||
(assoc
|
||||
facet
|
||||
:centre
|
||||
(vertex
|
||||
(+ (:x v1) (* (- (:x oc) (:x v1)) 2/3))
|
||||
(+ (:y v1) (* (- (:y oc) (:y v1)) 2/3))
|
||||
(+ (:z v1) (* (- (:z oc) (:z v1)) 2/3))))))
|
||||
|
||||
(defn centre
|
||||
[poly]
|
||||
(case (count (:vertices (check-polygon poly)))
|
||||
3 (triangle-centre poly)
|
||||
;; else
|
||||
(throw
|
||||
(UnsupportedOperationException.
|
||||
"The general case of centre for polygons is not yet implemented."))))
|
||||
|
||||
(defmacro on2dtriangle?
|
||||
"Is the projection of this `vertex` on the x, y plane within the
|
||||
projection of this triangle on that plane?"
|
||||
[vertex poly]
|
||||
`(not-yet-implemented "on2d? for triangles."))
|
||||
|
||||
(defn on2drectangle?
|
||||
"Is the projection of this `vertex` on the x, y plane within the
|
||||
projection of this rectangle on that plane?"
|
||||
[vertex rectangle]
|
||||
(let [xo (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))))))
|
||||
|
||||
(defmacro on2d?
|
||||
"Is the projection of this `vertex` on the x, y plane within the
|
||||
projection of this polygon `poly` on that plane?"
|
||||
[vertex poly]
|
||||
`(cond
|
||||
(rectangle? ~poly) (on2drectangle? ~vertex ~poly)
|
||||
(triangle? ~poly) (on2dtriangle? ~vertex ~poly)
|
||||
:else
|
||||
(not-yet-implemented "general case of on2d? for polygons.")))
|
95
src/cc/journeyman/walkmap/read_svg.clj
Normal file
95
src/cc/journeyman/walkmap/read_svg.clj
Normal file
|
@ -0,0 +1,95 @@
|
|||
(ns cc.journeyman.walkmap.read-svg
|
||||
"Utility functions for scalable vector graphics (SVG) into walkmap
|
||||
structures."
|
||||
(:require [clojure.java.io :as io]
|
||||
[clojure.string :as s]
|
||||
[clojure.xml :as x]
|
||||
[cc.journeyman.walkmap.path :refer [path]]
|
||||
[cc.journeyman.walkmap.tag :refer [tag]]
|
||||
[cc.journeyman.walkmap.vertex :refer [vertex vertex?]]))
|
||||
|
||||
(defn upper-case?
|
||||
[s]
|
||||
(every? #(Character/isUpperCase %) s))
|
||||
|
||||
(defn match->vertex
|
||||
[match-vector x y]
|
||||
(when-not (empty? match-vector)
|
||||
(let [command (nth match-vector 1)
|
||||
xcoord (read-string (nth match-vector 2))
|
||||
ycoord (read-string (nth match-vector 3))
|
||||
;; upper case command letters mean the coordinates that follow are
|
||||
;; absolute; lower case, relative.
|
||||
x' (if (upper-case? command) xcoord (+ x xcoord))
|
||||
y' (if (upper-case? command) ycoord (+ y ycoord))]
|
||||
(case (s/lower-case command)
|
||||
("m" "l") {:vertex (vertex x' y') :x x' :y y'}
|
||||
nil))))
|
||||
|
||||
(defn command-string->vertices
|
||||
"Return the destination of each successive line (`l`, `L`) and move (`m`, `M`)
|
||||
command in this string `s`, expected to be an SVG path command string."
|
||||
[s]
|
||||
(let [cmd-matcher ;; matches a 'command' in the string: a letter followed by
|
||||
;;spaces and numbers
|
||||
(re-matcher #"[a-zA-Z][^a-zA-Z]*" s)
|
||||
seg-pattern ;; matches a command which initiates a move of the current
|
||||
;; position.
|
||||
#"([a-zA-Z]) +([-+]?[0-9]*\.?[0-9]+) +([-+]?[0-9]*\.?[0-9]+) +"]
|
||||
(loop [match (re-find cmd-matcher)
|
||||
result []
|
||||
x 0
|
||||
y 0]
|
||||
(if-not match
|
||||
(filter vertex? result)
|
||||
(let [m (match->vertex (re-find seg-pattern match) x y)]
|
||||
(recur (re-find cmd-matcher) ;loop with 2 new arguments
|
||||
(conj result (:vertex m))
|
||||
(or (:x m) x)
|
||||
(or (:y m) y)))))))
|
||||
|
||||
(defn path-elt->path
|
||||
"Given the SVG path element `elt`, return a walkmap path structure
|
||||
representing the line (`l`, `L`) and move (`m`, `M`) commands in
|
||||
that path."
|
||||
[elt]
|
||||
(if (= (:tag elt) :path)
|
||||
(let [vs (command-string->vertices (-> elt :attrs :d))
|
||||
p (when-not (empty? vs) (apply path vs))]
|
||||
(if (and p (-> elt :attrs :class))
|
||||
(tag p (map keyword (s/split (-> elt :attrs :class) #" ")))
|
||||
p))
|
||||
(throw (IllegalArgumentException.
|
||||
(str "Must be an SVG `path` element: " elt)))))
|
||||
|
||||
(defn progeny
|
||||
"Return all the nodes in the XML structure below this `elt` which match
|
||||
this `predicate`."
|
||||
;; the name `descendants` is bound in `clojure.core` for something quite
|
||||
;; different, and I chose not to rebind it.
|
||||
[elt predicate]
|
||||
(if
|
||||
(apply predicate (list elt))
|
||||
(list elt)
|
||||
(reduce
|
||||
concat
|
||||
(remove
|
||||
empty?
|
||||
(map
|
||||
#(progeny % predicate)
|
||||
(:content elt))))))
|
||||
|
||||
(defn read-svg
|
||||
;; I tried to get this working with all the clever zip stuff in
|
||||
;; `clojure.zip`, `clojure.data.zip`, and so on. It would probably have
|
||||
;; been more elegant, but it kept crashing out of heap space on even
|
||||
;; quite small XML files. So I've implemented my own solution.
|
||||
([file-name]
|
||||
(read-svg file-name nil))
|
||||
([file-name _map-kind]
|
||||
(let [xml (x/parse (io/file file-name))
|
||||
paths (progeny xml #(= (:tag %) :path))]
|
||||
(remove nil? (map path-elt->path paths)))))
|
||||
|
||||
;; (read-svg "resources/iom/manual_roads.svg")
|
||||
|
210
src/cc/journeyman/walkmap/routing.clj
Normal file
210
src/cc/journeyman/walkmap/routing.clj
Normal file
|
@ -0,0 +1,210 @@
|
|||
(ns cc.journeyman.walkmap.routing
|
||||
"Finding optimal routes to traverse a map."
|
||||
(:require [clojure.math.numeric-tower :refer [expt]]
|
||||
[clojure.set :refer [intersection]]
|
||||
[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 :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.
|
||||
|
||||
;; Reading list:
|
||||
;;
|
||||
;; https://en.wikipedia.org/wiki/A*_search_algorithm
|
||||
;; https://www.redblobgames.com/pathfinding/a-star/introduction.html
|
||||
;; https://faculty.nps.edu/ncrowe/opmpaper2.htm
|
||||
;;
|
||||
;; See https://simon-brooke.github.io/the-great-game/codox/Pathmaking.html
|
||||
|
||||
(def ^:dynamic *gradient-exponent*
|
||||
"The exponent to be applied to `(inc (:z (unit-vector from to)))`
|
||||
of a path segment to calculate the gradient-related part of the
|
||||
cost of traversal. Dynamic, because we will want to tune this."
|
||||
2)
|
||||
|
||||
(def ^:dynamic *traversals-exponent*
|
||||
"The (expected to be negative) exponent to be applied to the number
|
||||
of traversals of a path to compute the road bonus. Paths more travelled by
|
||||
should have larger bonuses, but not dramatically so - so the increase in
|
||||
bonus needs to scale significantly less than linearly with the number
|
||||
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)
|
||||
(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))))
|
||||
|
||||
(defn path-traversal-cost
|
||||
[path s]
|
||||
(vertices-traversal-cost (:vertices (p/check-path path)) s))
|
||||
|
||||
(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]
|
||||
(debug (format "barriers-crossed called with vertices\n\tfrom: %s\n\tto: %s" from to))
|
||||
;; TODO: implement
|
||||
'())
|
||||
|
||||
(defn crossing-penalty
|
||||
"TODO: should return the cost of crossing this `barrier`, initially mainly
|
||||
a watercourse, on the axis from vertex `from` to vertex `to`. in the context
|
||||
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]
|
||||
(debug (format "crossing-penalty called with arguments\n\tbarrier: %s\n\tfrom: %s\n\tto: %s" barrier from to))
|
||||
;; TODO: implement
|
||||
0)
|
||||
|
||||
(defn gradient-cost
|
||||
"Compute the per-unit-distance cost of traversing this `edge`."
|
||||
[edge]
|
||||
(let [g (:z (e/unit-vector edge))]
|
||||
(if (pos? g)
|
||||
(expt (inc g) *gradient-exponent*)
|
||||
1)))
|
||||
|
||||
;; (gradient-cost (e/edge (v/vertex 0 0 0) (v/vertex 0 1 0)))
|
||||
;; (gradient-cost (e/edge (v/vertex 0 0 0) (v/vertex 0 2 0)))
|
||||
;; (gradient-cost (e/edge (v/vertex 0 0 0) (v/vertex 0 1 1)))
|
||||
;; (gradient-cost (e/edge (v/vertex 0 0 0) (v/vertex 0 2 1)))
|
||||
;; (gradient-cost (e/edge (v/vertex 0 0 0) (v/vertex 0 1 0.0001)))
|
||||
|
||||
(defn best-road
|
||||
"Find the best traversable path which links the vertices `from` and `to`
|
||||
in this superstructure `s`, or nil if there are none."
|
||||
[from to s]
|
||||
(let [f (fn [v] (set (s/touching v p/path? s)))]
|
||||
(first
|
||||
(sort-by
|
||||
;;; I... chose the path more travelled by.
|
||||
#(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`,
|
||||
`to`, in the context of the superstructure `s`. Obviously there only is
|
||||
such a bonus if there actually is an existing thoroughfare to use. Road
|
||||
bonuses scale with some fractional exponent of the number of traversals
|
||||
which have been made of the road segment in question."
|
||||
[from to s]
|
||||
(let [best (best-road from to s)]
|
||||
(when (:traversals best)
|
||||
(expt (:traversals best) *traversals-exponent*))))
|
||||
|
||||
(defn traversal-cost
|
||||
"Return the traversal cost of the edge represented by the vertices `from`,
|
||||
`to`, in the context of the superstructure `s`. It is legitimate to pass
|
||||
`nil` as the `to` argument, in which case the result will be zero, in order
|
||||
to allow `reduce` to be used to compute total path costs."
|
||||
[from to s]
|
||||
(if (nil? to)
|
||||
0
|
||||
(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)))))
|
||||
|
||||
;; (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}
|
||||
;; {:x 7.578125, :y -10.703125, :kind :vertex, :walkmap.id/id :vert_7-578125_-10-703125}
|
||||
;; {:x 7.578125, :y 0, :kind :vertex, :walkmap.id/id :vert_7-578125_0}
|
||||
;; {:x 2.171875, :y -0.765625, :kind :vertex, :walkmap.id/id :vert_2-171875_-0-765625}
|
||||
;; {:x 6.8125, :y -0.765625, :kind :vertex, :walkmap.id/id :vert_6-8125_-0-765625}))
|
||||
;; (v/check-vertices p)
|
||||
;; (def p' (p/path p))
|
||||
|
||||
;; (traversal-cost (first p) (nth p 1) {})
|
||||
;; (vertices-traversal-cost p {})
|
||||
;; (path-traversal-cost (p/path p))
|
||||
|
||||
(defn extend-frontier
|
||||
"Return a sequence like `frontier` with all of these `candidates` which are
|
||||
not already members either of `frontier` or of `rejects` appended. Assumes
|
||||
candidates are traversable."
|
||||
([frontier candidates]
|
||||
(extend-frontier frontier candidates nil))
|
||||
([frontier candidates rejects]
|
||||
(if
|
||||
(empty? frontier)
|
||||
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))
|
||||
;; (extend-frontier '(1 2 3 4 5) '())
|
||||
;; (extend-frontier '(1 2 3 4 5) nil)
|
||||
;; (extend-frontier nil '(1 2 3 4 5))
|
||||
|
||||
(def ^:dynamic *route-goal*
|
||||
"The goal of the route currently sought."
|
||||
nil)
|
||||
|
||||
(defn find-traversable-facet
|
||||
"Return the nearest traversable walkmap facet within `search-radius` of
|
||||
`target`, or throw an exception if none is found."
|
||||
[target search-radius s]
|
||||
(let [r (s/nearest s target traversable? search-radius)]
|
||||
(when-not r (throw
|
||||
(Exception.
|
||||
(format "Unable to find walkable facet within %s of %s"
|
||||
search-radius
|
||||
target))))
|
||||
r))
|
||||
|
||||
(defn route
|
||||
;; architectural problem: needs to return not the route, but a modified
|
||||
;; superstructure with the new route stored in it.
|
||||
([from to s]
|
||||
(route from to s traversal-cost 5))
|
||||
([from to s cost-fn search-radius]
|
||||
(let [from' (find-traversable-facet from search-radius s)
|
||||
to' (find-traversable-facet to search-radius s)]
|
||||
(a* from'
|
||||
#(v/vertex= % (:centre to')) ;; goal?-fn - 'have we got there yet?'
|
||||
#(cost-fn %1 %2 s) ;; distance-fn - what is the distance/cost
|
||||
;; between these vertices?
|
||||
#(e/length (e/edge (:centre %) to))
|
||||
;; heuristic: how far to the end goal
|
||||
#(s/neighbours % traversable? s)
|
||||
;; neighbours-fn - return the traversable
|
||||
;; neighbours of the current facet
|
||||
(int (* search-radius (e/length (e/edge from to))))
|
||||
;; how long a path we'll accept
|
||||
))))
|
253
src/cc/journeyman/walkmap/stl.clj
Normal file
253
src/cc/journeyman/walkmap/stl.clj
Normal file
|
@ -0,0 +1,253 @@
|
|||
(ns cc.journeyman.walkmap.stl
|
||||
"Utility functions dealing with stereolithography (STL) files. Not a stable API yet!"
|
||||
(:require [cc.journeyman.walkmap.ocean :refer [ocean?]]
|
||||
[cc.journeyman.walkmap.polygon :refer [centre gradient triangle?]]
|
||||
[cc.journeyman.walkmap.superstructure :refer [store]]
|
||||
[cc.journeyman.walkmap.tag :refer [tag]]
|
||||
[cc.journeyman.walkmap.utils :refer [kind-type truncate]]
|
||||
[cc.journeyman.walkmap.vertex :as v :refer [vertex]]
|
||||
[clojure.core.matrix :refer [set-current-implementation sub]]
|
||||
[clojure.java.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]))
|
||||
|
||||
;; We want to use the `[vectorz](https://github.com/mikera/vectorz-clj)`
|
||||
;; implementation of core.matrix.
|
||||
(set-current-implementation :vectorz)
|
||||
|
||||
(defn stl?
|
||||
"True if `o` is recogniseable as an STL structure. An STL structure must
|
||||
have a key `:facets`, whose value must be a sequence of polygons; and
|
||||
may have a key `:header` whose value should be a string, and/or a key
|
||||
`:count`, whose value should be a positive integer.
|
||||
|
||||
If `verify-count?` is passed and is not `false`, verify that the value of
|
||||
the `:count` header is equal to the number of facets."
|
||||
([o]
|
||||
(stl? o false))
|
||||
([o verify-count?]
|
||||
(and
|
||||
(map? o)
|
||||
(:facets o)
|
||||
(every? triangle? (:facets o))
|
||||
(if (:header o) (string? (:header o)) true)
|
||||
(if (:count o) (integer? (:count o)) true)
|
||||
(or (nil? (:kind o)) (= (:kind o) :stl))
|
||||
(if verify-count? (= (:count o) (count (:facets o))) true))))
|
||||
|
||||
(def vect
|
||||
"A codec for vectors within a binary STL file."
|
||||
(b/ordered-map
|
||||
:x :float-le
|
||||
:y :float-le
|
||||
:z :float-le))
|
||||
|
||||
(def facet
|
||||
"A codec for a facet (triangle) within a binary STL file."
|
||||
(b/ordered-map
|
||||
:normal vect
|
||||
:vertices [vect vect vect]
|
||||
:abc :ushort-le))
|
||||
|
||||
(def binary-stl
|
||||
"A codec for binary STL files"
|
||||
(b/ordered-map
|
||||
:header (b/string "ISO-8859-1" :length 80) ;; for the time being we neither know nor care what's in this.
|
||||
:count :uint-le
|
||||
:facets (b/repeated facet)))
|
||||
|
||||
(defn vertex->array
|
||||
[v]
|
||||
[(:x v) (:y v) (or (:z v) 0)])
|
||||
|
||||
(defn surface-normal
|
||||
"From https://www.khronos.org/opengl/wiki/Calculating_a_Surface_Normal
|
||||
```
|
||||
Begin Function CalculateSurfaceNormal (Input Triangle) Returns Vector
|
||||
|
||||
Set Vector U to (Triangle.p2 minus Triangle.p1)
|
||||
Set Vector V to (Triangle.p3 minus Triangle.p1)
|
||||
|
||||
Set Normal.x to (multiply U.y by V.z) minus (multiply U.z by V.y)
|
||||
Set Normal.y to (multiply U.z by V.x) minus (multiply U.x by V.z)
|
||||
Set Normal.z to (multiply U.x by V.y) minus (multiply U.y by V.x)
|
||||
|
||||
Returning Normal
|
||||
|
||||
End Function
|
||||
```"
|
||||
[triangle]
|
||||
(if (triangle? triangle)
|
||||
(let
|
||||
[vertices (:vertices triangle)
|
||||
v1 (vertex->array (nth vertices 0))
|
||||
u (sub (vertex->array (nth vertices 1)) v1)
|
||||
v (sub (vertex->array (nth vertices 2)) v1)
|
||||
x (- (* (nth u 1)(nth v 2)) (* (nth u 2) (nth v 1)))
|
||||
y (- (* (nth u 2) (nth v 0)) (* (nth u 0) (nth v 2)))
|
||||
z (- (* (nth u 0) (nth v 1)) (* (nth u 1 (nth v 0))))]
|
||||
(debug (format "Calculating normal for triangle %s" triangle))
|
||||
(vertex x y z))
|
||||
(throw (IllegalArgumentException.
|
||||
(format "Expected :triangle, found %s" (kind-type triangle))))))
|
||||
|
||||
(defn ensure-normal
|
||||
"Ensure this `triangle` has a normal vector/"
|
||||
[triangle]
|
||||
(if (triangle? triangle)
|
||||
(if (:normal triangle) triangle
|
||||
(assoc triangle :normal (surface-normal triangle)))
|
||||
(throw (IllegalArgumentException.
|
||||
(format "Expected :triangle, found %s" (kind-type triangle))))))
|
||||
|
||||
(defn canonicalise
|
||||
"Objects read in from STL won't have all the keys/values we need them to have.
|
||||
`o` may be a map (representing a facet or a vertex), or a sequence of such maps;
|
||||
if it isn't recognised it is at present just returned unchanged. `map-kind`, if
|
||||
passed, must be a keyword indicating the value represented by the `z` axis
|
||||
(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 ^Keyword map-kind]
|
||||
(canonicalise o map-kind (v/vertex 1 1 1)))
|
||||
([o ^Keyword map-kind scale-vertex]
|
||||
(when-not
|
||||
(keyword? map-kind)
|
||||
(throw (IllegalArgumentException.
|
||||
(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?`
|
||||
(:facets o) (assoc o
|
||||
:kind :stl
|
||||
:walkmap.id/id (or (:walkmap.id/id o)
|
||||
(keyword (gensym "stl")))
|
||||
:facets (canonicalise (:facets o) map-kind))
|
||||
;; if it's a triangle it's almost a facet. All it needs now is a normal
|
||||
;; vector
|
||||
(triangle? o) (ensure-normal o)
|
||||
;; if it has :vertices it's a polygon, but it may not yet conform to
|
||||
;; `polygon?`
|
||||
(:vertices o) (let [f (gradient
|
||||
(centre
|
||||
(tag
|
||||
(assoc o
|
||||
:walkmap.id/id (or
|
||||
(:walkmap.id/id o)
|
||||
(keyword (gensym "poly")))
|
||||
:kind :polygon
|
||||
:vertices (canonicalise
|
||||
(:vertices o)
|
||||
map-kind))
|
||||
:facet map-kind)))]
|
||||
(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 (v/vertex? scale-vertex)
|
||||
(v/vertex* c scale-vertex)
|
||||
c))
|
||||
;; shouldn't happen
|
||||
:else o)))
|
||||
|
||||
(defn decode-binary-stl
|
||||
"Parse a binary STL file from this `filename` and return an STL structure
|
||||
representing its contents. `map-kind`, if passed, must be a keyword
|
||||
or sequence of keywords indicating the semantic value represented by the `z`
|
||||
axis (defaults to `:height`).
|
||||
|
||||
If `superstructure` is supplied and is a map, the generated STL structure
|
||||
will be stored in that superstructure, which will be returned.
|
||||
|
||||
If `scale-vertex` is supplied, it must be a three dimensional vertex (i.e.
|
||||
the `:z` key must have a numeric value) representing the amount by which
|
||||
each of the vertices read from the STL will be scaled.
|
||||
|
||||
It is an error, and an exception will be thrown, if `map-kind` is not a
|
||||
keyword or sequence of keywords.
|
||||
|
||||
**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."
|
||||
([^String filename]
|
||||
(decode-binary-stl filename :height))
|
||||
([^String filename ^Keyword map-kind]
|
||||
(decode-binary-stl filename map-kind nil))
|
||||
([^String filename ^Keyword mapkind superstucture]
|
||||
(decode-binary-stl filename mapkind superstucture (v/vertex 1 1 1)))
|
||||
([^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)
|
||||
(store stl superstructure)
|
||||
stl))))
|
||||
|
||||
(defn- vect->str [prefix v]
|
||||
(str prefix " " (:x v) " " (:y v) " " (:z v) "\n"))
|
||||
|
||||
(defn- facet2str [tri]
|
||||
(str
|
||||
(vect->str "facet normal" (:normal tri))
|
||||
"outer loop\n"
|
||||
(s/join
|
||||
(map
|
||||
#(vect->str "vertex" %)
|
||||
(:vertices tri)))
|
||||
"endloop\nendfacet\n"))
|
||||
|
||||
(defn stl->ascii
|
||||
"Return as a string an ASCII rendering of the `stl` structure."
|
||||
([stl]
|
||||
(stl->ascii stl "unknown"))
|
||||
([stl solidname]
|
||||
(str
|
||||
"solid "
|
||||
(or solidname
|
||||
(when (:header stl)(s/trim (:header stl))))
|
||||
"\n"
|
||||
(s/join
|
||||
(map
|
||||
facet2str
|
||||
(:facets stl)))
|
||||
"endsolid "
|
||||
solidname
|
||||
"\n")))
|
||||
|
||||
(defn write-ascii-stl
|
||||
"Write an `stl` structure as read by `decode-binary-stl` to this
|
||||
`filename` as ASCII encoded STL."
|
||||
([filename stl]
|
||||
(let [b (base-name filename true)]
|
||||
(write-ascii-stl
|
||||
filename stl
|
||||
(subs b 0 (or (s/index-of b ".") (count b))))))
|
||||
([filename stl solidname]
|
||||
(debug (format "Writing STL to '%s'; solid name is %s" filename solidname))
|
||||
(spit
|
||||
filename
|
||||
(stl->ascii stl solidname))))
|
||||
|
||||
(defn binary-stl-to-ascii
|
||||
"Convert the binary STL file indicated by `in-filename`, and write it to
|
||||
`out-filename`, if specified; otherwise, to a file with the same basename
|
||||
as `in-filename` but the extension `.ascii.stl`."
|
||||
([in-filename]
|
||||
(let [[_ ext] (split-ext in-filename)]
|
||||
(binary-stl-to-ascii
|
||||
in-filename
|
||||
(str
|
||||
(subs
|
||||
in-filename
|
||||
0
|
||||
(or
|
||||
(s/last-index-of in-filename ".")
|
||||
(count in-filename)))
|
||||
".ascii"
|
||||
ext))))
|
||||
([in-filename out-filename]
|
||||
(write-ascii-stl out-filename (decode-binary-stl in-filename))))
|
277
src/cc/journeyman/walkmap/superstructure.clj
Normal file
277
src/cc/journeyman/walkmap/superstructure.clj
Normal file
|
@ -0,0 +1,277 @@
|
|||
(ns cc.journeyman.walkmap.superstructure
|
||||
"single indexing structure for walkmap objects"
|
||||
(: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.utils :as u]
|
||||
[cc.journeyman.walkmap.vertex :as v])
|
||||
(:import [clojure.lang IFn IPersistentMap]))
|
||||
|
||||
;; 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,
|
||||
;; 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.
|
||||
;;
|
||||
;; The question then is, when we want to do things with those objects, do we
|
||||
;; exteract a copy with its dependent structures fixed back up (reification),
|
||||
;; or do we indirect through the superstructure every time we want to access
|
||||
;; them? In a sense, the copy in the superstructure is the 'one true copy',
|
||||
;; but it may become very difficult then to have one true copy of the
|
||||
;; superstructure - unless we replace the superstructure altogether with a
|
||||
;; database, which may be the Right Thing To Do.
|
||||
|
||||
(def vertex-index ::vertex-index)
|
||||
|
||||
(defn vertices
|
||||
"If `o` is an object with vertices, return those vertices, else nil."
|
||||
[o]
|
||||
(when (map? o)
|
||||
(reduce
|
||||
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)
|
||||
;; (p/path? o) (:vertices o))
|
||||
;; )
|
||||
|
||||
(defn index-vertex
|
||||
"Return a superstructure like `s` in which object `o` is indexed by vertex
|
||||
`v`. It is an error (and an exception may be thrown) if
|
||||
|
||||
1. `s` is not a map;
|
||||
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."
|
||||
[^IPersistentMap s ^IPersistentMap o ^IPersistentMap v]
|
||||
(if-not (v/vertex? o)
|
||||
(if (:walkmap.id/id o)
|
||||
(if (v/vertex? v)
|
||||
(let [vi (or (::vertex-index s) {})
|
||||
current (or (vi (:walkmap.id/id v)) {})]
|
||||
;; deep-merge doesn't merge sets, only maps; so at this
|
||||
;; stage we need to build a map.
|
||||
(assoc vi (:walkmap.id/id v) (assoc current (:walkmap.id/id o) (:walkmap.id/id v))))
|
||||
(throw (IllegalArgumentException. "Not a vertex: " v)))
|
||||
(throw (IllegalArgumentException. (u/truncate (str "No `:walkmap.id/id` value: " o) 80))))
|
||||
;; it shouldn't actually be an error to try to index a vertex, but it
|
||||
;; also isn't useful to do so, so I'd be inclined to ignore it.
|
||||
(::vertex-index s)))
|
||||
|
||||
(defn index-vertices
|
||||
"Return a superstructure like `s` in which object `o` is indexed by its
|
||||
vertices. It is an error (and an exception may be thrown) if
|
||||
|
||||
1. `s` is not a map;
|
||||
2. `o` is not a map;
|
||||
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)))}))
|
||||
|
||||
(defn in-retrieve
|
||||
"Internal guts of `retrieve`, q.v. `x` can be anything; `s` must be a
|
||||
walkmap superstructure. TODO: recursive, quite likely to blow the fragile
|
||||
Clojure stack. Probably better to do this with `walk`, but I don't yet
|
||||
understand that."
|
||||
[x s]
|
||||
(cond
|
||||
;; if it's a keyword identifying something in s, retrieve that something.
|
||||
(keyword? x) (if (s x)
|
||||
(in-retrieve (s x) s)
|
||||
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)))
|
||||
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))
|
||||
(set? x) x ;; TODO: should I search in sets for objects when storing?
|
||||
(coll? x) (map #(in-retrieve % s) x)
|
||||
:else x))
|
||||
|
||||
(defn retrieve
|
||||
"Retrieve the canonical representation of the object with this `id` from the
|
||||
superstructure `s`."
|
||||
[id s]
|
||||
(in-retrieve (id s) s))
|
||||
|
||||
(defn in-store-find-objects
|
||||
"Return an id -> object map of every object within `o`. Internal to
|
||||
`in-store`, q.v. Use at your own peril."
|
||||
([o]
|
||||
(in-store-find-objects o {}))
|
||||
([o s]
|
||||
(when (map? o) (l/debug "Finding objects in:" o))
|
||||
(cond
|
||||
(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))
|
||||
(coll? o) (reduce merge s (map #(in-store-find-objects % s) o))
|
||||
:else s)))
|
||||
|
||||
(defn in-store-replace-with-keys
|
||||
"Return a copy of `o` in which each reified walkmap object within `o` has
|
||||
been replaced with the `:walkmap.id/id` of that object. Internal to
|
||||
`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)))
|
||||
|
||||
;; (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)))
|
||||
|
||||
(defn store
|
||||
"Return a superstructure like `s` with object `o` added. If only one
|
||||
argument is supplied it will be assumed to represent `o` and a new
|
||||
superstructure will be returned.
|
||||
|
||||
It is an error (and an exception may be thrown) if
|
||||
|
||||
1. `s` is not a map;
|
||||
2. `o` is not a recognisable walkmap object"
|
||||
([^IPersistentMap o]
|
||||
(store o {}))
|
||||
([^IPersistentMap o ^IPersistentMap s]
|
||||
(when-not (:walkmap.id/id o)
|
||||
(throw
|
||||
(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)))))
|
||||
(assoc
|
||||
(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
|
||||
`minv` and `maxv`. Every coordinate in `minv` must have a lower value than
|
||||
the equivalent coordinate in `maxv`. If `d2?` is supplied and not false,
|
||||
search only in the x,y projection.
|
||||
|
||||
**NOTE THAT** this depends on the fact that vertices do not currently
|
||||
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."
|
||||
([^IPersistentMap s ^IPersistentMap minv ^IPersistentMap 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))))))
|
||||
|
||||
(defn nearest
|
||||
"Search superstructure `s` for the nearest object matching `filter-fn` to
|
||||
the `target` vertex. Searches only with `radius` (slight misnomer, area
|
||||
actually searched is a cube). Returns one object, or `nil` if no matching
|
||||
object found.
|
||||
|
||||
WARNING: currently only returns objects which have a defined `:centre`
|
||||
(but most of the significant objects we have do)."
|
||||
([^IPersistentMap s ^IPersistentMap target ^Number radius]
|
||||
(nearest s target :centre radius))
|
||||
([^IPersistentMap s ^IPersistentMap 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
|
||||
(+ (: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
|
||||
(sort-by
|
||||
#(length (edge target (:centre %)))
|
||||
(filter
|
||||
filter-fn
|
||||
(filter
|
||||
:centre
|
||||
(pmap #(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)
|
||||
;; get all the vertex ids within radius of the target
|
||||
(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`."
|
||||
([^IPersistentMap vertex ^IPersistentMap s]
|
||||
(map
|
||||
#(retrieve % s)
|
||||
(set (-> s :vertex-index (:walkmap.id/id (v/check-vertex vertex)) keys))))
|
||||
([^IPersistentMap vertex ^IFn filter-fn ^IPersistentMap s]
|
||||
(filter
|
||||
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."
|
||||
([^IPersistentMap target ^IPersistentMap s]
|
||||
(neighbours target identity s))
|
||||
([^IPersistentMap target ^IFn filter-fn ^IPersistentMap s]
|
||||
(remove
|
||||
#(= 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."
|
||||
([^IPersistentMap target ^IPersistentMap s]
|
||||
(neighbour-ids target identity s))
|
||||
([^IPersistentMap target ^IFn filter-fn ^IPersistentMap s]
|
||||
(pmap :walkmap.id/id (neighbours target filter-fn s))))
|
106
src/cc/journeyman/walkmap/svg.clj
Normal file
106
src/cc/journeyman/walkmap/svg.clj
Normal file
|
@ -0,0 +1,106 @@
|
|||
(ns cc.journeyman.walkmap.svg
|
||||
"Utility functions for writing stereolithography (STL) files (and possibly,
|
||||
later, other geometry files of interest to us) as scalable vector graphics
|
||||
(SVG)."
|
||||
(:require [clojure.string :as s]
|
||||
[dali.io :as neatly-folded-clock]
|
||||
[hiccup.core :refer [html]]
|
||||
[taoensso.timbre :refer [info]]
|
||||
[cc.journeyman.walkmap.ocean :refer [cull-ocean-facets]]
|
||||
[cc.journeyman.walkmap.stl :refer [decode-binary-stl]]))
|
||||
|
||||
(def ^:dynamic *preferred-svg-render*
|
||||
"Mainly for debugging dali; switch SVG renderer to use. Expected values:
|
||||
`:dali`, `:hiccup`."
|
||||
:dali)
|
||||
|
||||
(defn- facet->svg-poly
|
||||
[facet]
|
||||
[:polygon
|
||||
{:points (s/join " " (map #(str (:x %) "," (:y %)) (:vertices facet)))}])
|
||||
|
||||
(defn- dali-facet->svg-poly
|
||||
[facet]
|
||||
(vec
|
||||
(cons
|
||||
:polygon
|
||||
(map #(vec (list (:x %) (:y %))) (:vertices facet)))))
|
||||
|
||||
(defn dali-stl->svg
|
||||
"Format this `stl` as SVG for the `dali` renderer on a page with these
|
||||
bounds."
|
||||
[stl minx maxx miny maxy]
|
||||
[:dali/page
|
||||
{:xmlns "http://www.w3.org/2000/svg"
|
||||
:version "1.2"
|
||||
:width (- maxx minx)
|
||||
:height (- maxy miny)
|
||||
:viewBox (s/join " " (map str [minx miny maxx maxy]))}
|
||||
(vec
|
||||
(cons
|
||||
:g
|
||||
(map
|
||||
dali-facet->svg-poly
|
||||
(:facets stl))))])
|
||||
|
||||
(defn hiccup-stl->svg
|
||||
"Format this `stl` as SVG for the `hiccup` renderer on a page with these
|
||||
bounds."
|
||||
[stl minx maxx miny maxy]
|
||||
[:svg
|
||||
{:xmlns "http://www.w3.org/2000/svg"
|
||||
:version "1.2"
|
||||
:width (- maxx minx)
|
||||
:height (- maxy miny)
|
||||
:viewBox (s/join " " (map str [minx miny maxx maxy]))}
|
||||
(vec
|
||||
(cons
|
||||
:g
|
||||
(map
|
||||
facet->svg-poly
|
||||
(:facets stl))))])
|
||||
|
||||
(defn stl->svg
|
||||
"Convert this in-memory `stl` structure, as read by `decode-binary-stl`, into
|
||||
an in-memory hiccup representation of SVG structure, and return it."
|
||||
[stl]
|
||||
(let [minx (reduce
|
||||
min
|
||||
(map
|
||||
#(reduce min (map :x (:vertices %)))
|
||||
(:facets stl)))
|
||||
maxx (reduce
|
||||
max
|
||||
(map
|
||||
#(reduce max (map :x (:vertices %)))
|
||||
(:facets stl)))
|
||||
miny (reduce
|
||||
min
|
||||
(map
|
||||
#(reduce min (map :y (:vertices %)))
|
||||
(:facets stl)))
|
||||
maxy (reduce
|
||||
max
|
||||
(map
|
||||
#(reduce max (map :y (:vertices %)))
|
||||
(:facets stl)))]
|
||||
(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)
|
||||
(throw (Exception. "Unexpected renderer value: " *preferred-svg-render*)))))
|
||||
|
||||
(defn binary-stl-file->svg
|
||||
"Given only an `in-filename`, parse the indicated file, expected to be
|
||||
binary STL, and return an equivalent SVG structure. Given both `in-filename`
|
||||
and `out-filename`, as side-effect write the SVG to the indicated output file."
|
||||
([in-filename]
|
||||
(stl->svg (cull-ocean-facets (decode-binary-stl in-filename))))
|
||||
([in-filename out-filename]
|
||||
(let [s (binary-stl-file->svg in-filename)]
|
||||
(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))
|
||||
(throw (Exception. "Unexpected renderer value: " *preferred-svg-render*)))
|
||||
s)))
|
60
src/cc/journeyman/walkmap/tag.clj
Normal file
60
src/cc/journeyman/walkmap/tag.clj
Normal file
|
@ -0,0 +1,60 @@
|
|||
(ns cc.journeyman.walkmap.tag
|
||||
"Code for tagging, untagging, and finding tags on objects. Note the use of
|
||||
the namespaced keyword, `:walkmap.tag/tags`, denoted in this file `::tags`.
|
||||
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]])
|
||||
(:import [clojure.lang IPersistentMap]))
|
||||
|
||||
(defn tagged?
|
||||
"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."
|
||||
[^IPersistentMap object & tags]
|
||||
(let [tags' (flatten tags)]
|
||||
(when-not (every? keyword? tags')
|
||||
(throw (IllegalArgumentException.
|
||||
(str "Must be keywords: " (map kind-type tags')))))
|
||||
(let [ot (::tags object)]
|
||||
(and
|
||||
(set? ot)
|
||||
(every? ot tags')))))
|
||||
|
||||
(defn tag
|
||||
"Return an object like this `object` but with these `tags` added to its tags,
|
||||
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 or sequence of keywords.
|
||||
|
||||
It's legal to include sequences of keywords in `tags`, so that users can do
|
||||
useful things like `(tag obj (map keyword some-strings))`."
|
||||
[^IPersistentMap object & tags]
|
||||
(l/debug "Tagging" (kind-type object) "with" tags)
|
||||
(let [tags' (flatten tags)]
|
||||
(when-not (every? keyword? tags')
|
||||
(throw (IllegalArgumentException.
|
||||
(str "Must be keywords: " (map kind-type tags')))))
|
||||
(assoc object ::tags (union (set tags') (::tags 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. 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 or sequence of keywords."
|
||||
[^IPersistentMap object & tags]
|
||||
(let [tags' (flatten tags)]
|
||||
(when-not (every? keyword? tags')
|
||||
(throw (IllegalArgumentException.
|
||||
(str "Must be keywords: " (map kind-type tags')))))
|
||||
(update-in object [:walkmap.tag/tags] difference (set tags'))))
|
19
src/cc/journeyman/walkmap/test_all.clj
Normal file
19
src/cc/journeyman/walkmap/test_all.clj
Normal file
|
@ -0,0 +1,19 @@
|
|||
(ns cc.journeyman.walkmap.test-all
|
||||
(:require [cc.journeyman.walkmap.mw-stl :refer [mw->stl]]
|
||||
[cc.journeyman.walkmap.stl :refer [write-ascii-stl]]
|
||||
[mw-engine.world :refer [world?]]))
|
||||
|
||||
(defn run-test
|
||||
([^String name]
|
||||
(run-test (str name ".edn") (str name ".stl") name))
|
||||
([^String mw-name ^String stl-name ^String title]
|
||||
(let [mw (read-string (slurp mw-name))]
|
||||
(if (world? mw)
|
||||
(write-ascii-stl
|
||||
stl-name
|
||||
(mw->stl mw)
|
||||
title)
|
||||
(throw (IllegalArgumentException.
|
||||
(format
|
||||
"Content of file '%s' was not recognised as a valid world"
|
||||
mw-name)))))))
|
119
src/cc/journeyman/walkmap/utils.clj
Normal file
119
src/cc/journeyman/walkmap/utils.clj
Normal file
|
@ -0,0 +1,119 @@
|
|||
(ns cc.journeyman.walkmap.utils
|
||||
"Miscellaneous utility functions."
|
||||
(: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.
|
||||
[& vals]
|
||||
(if (every? map? 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
|
||||
"If string `s` is more than `n` characters long, return the first `n`
|
||||
characters; otherwise, return `s`."
|
||||
[s n]
|
||||
(if (and (string? s) (number? n) (> (count s) n))
|
||||
(subs s 0 n)
|
||||
s))
|
||||
|
||||
(defn kind-type
|
||||
"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
|
||||
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)."
|
||||
([^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)))
|
||||
([^Number n1 ^Number n2 ^Number tolerance]
|
||||
(if (and (number? n1) (number? n2))
|
||||
(< (m/abs (- n1 n2)) tolerance)
|
||||
(= n1 n2))))
|
||||
|
||||
(defmacro check-kind-type
|
||||
"If `object` is not of kind-type `expected`, throws an
|
||||
IllegalArgumentException with an appropriate message; otherwise, returns
|
||||
`object`. If `checkfn` is supplied, it should be a function which tests
|
||||
whether the object is of the expected kind-type.
|
||||
|
||||
Macro, so that the exception is thrown from the calling function."
|
||||
([object expected]
|
||||
`(if-not (= (kind-type ~object) ~expected)
|
||||
(throw
|
||||
(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)])))
|
||||
~object)))
|
||||
|
||||
(defmacro check-kind-type-seq
|
||||
"If some item on sequence `s` is not of the `expected` kind-type, throws an
|
||||
IllegalArgumentException with an appropriate message; otherwise, returns
|
||||
`object`. If `checkfn` is supplied, it should be a function which tests
|
||||
whether the object is of the expected kind-type.
|
||||
|
||||
Macro, so that the exception is thrown from the calling function."
|
||||
([s expected]
|
||||
`(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 checkfn expected]
|
||||
`(if-not (every? #(~checkfn %) ~s)
|
||||
(throw
|
||||
(IllegalArgumentException.
|
||||
(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)."
|
||||
[source]
|
||||
(try
|
||||
(with-open [r (io/reader source)]
|
||||
(edn/read (java.io.PushbackReader. r)))
|
||||
(catch java.io.IOException e
|
||||
(printf "Couldn't open '%s': %s\n" source (.getMessage e)))
|
||||
(catch RuntimeException e
|
||||
(printf "Error parsing edn file '%s': %s\n" source (.getMessage e)))))
|
||||
|
||||
(defn not-yet-implemented
|
||||
[message]
|
||||
(throw
|
||||
(UnsupportedOperationException.
|
||||
(str "Not yet implemented: " message))))
|
152
src/cc/journeyman/walkmap/vertex.clj
Normal file
152
src/cc/journeyman/walkmap/vertex.clj
Normal file
|
@ -0,0 +1,152 @@
|
|||
(ns cc.journeyman.walkmap.vertex
|
||||
"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`."
|
||||
(:require [clojure.math.numeric-tower :as m]
|
||||
[clojure.string :as s]
|
||||
[taoensso.timbre :as l]
|
||||
[cc.journeyman.walkmap.utils :refer
|
||||
[=ish check-kind-type check-kind-type-seq kind-type truncate]]))
|
||||
|
||||
(defn vertex-key
|
||||
"Making sure we get the same key everytime we key a vertex with the same
|
||||
coordinates. `o` must have numeric values for `:x`, `:y`, and optionally
|
||||
`:z`; it is an error and an exception will be thrown if `o` does not
|
||||
conform to this specification.
|
||||
|
||||
**Note:** these keys can be quite long. No apology is made: it is required
|
||||
that the same key can *never* refer to two different locations in space."
|
||||
[o]
|
||||
(keyword
|
||||
(s/replace
|
||||
(cond
|
||||
(and (:x o) (:y o) (:z o))
|
||||
(str "vert_" (:x o) "_" (:y o) "_" (:z o))
|
||||
(and (:x o) (:y o))
|
||||
(str "vert_" (:x o) "_" (:y o))
|
||||
:else
|
||||
(throw (IllegalArgumentException.
|
||||
(truncate (str "Not a vertex: " (or o "nil")) 80))))
|
||||
#"\p{Punct}"
|
||||
"_")))
|
||||
|
||||
(defn vertex?
|
||||
"True if `o` satisfies the conditions for a vertex. That is, essentially,
|
||||
that it must rerpresent a two- or three- dimensional vector. A vertex is
|
||||
shall be a map having at least the keys `:x` and `:y`, where the value of
|
||||
those keys is a number. If the key `:z` is also present, its value must also
|
||||
be a number.
|
||||
|
||||
The name `vector?` was not used as that would clash with a function of that
|
||||
name in `clojure.core` whose semantics are entirely different."
|
||||
[o]
|
||||
(and
|
||||
(map? o)
|
||||
(:walkmap.id/id o)
|
||||
(number? (:x o))
|
||||
(number? (:y o))
|
||||
(or (nil? (:z o)) (number? (:z o)))
|
||||
(or (nil? (:kind o)) (= (:kind o) :vertex))))
|
||||
|
||||
(defmacro check-vertex
|
||||
"If `o` is not a vertex, throw an `IllegalArgumentException` with an
|
||||
appropriate message; otherwise, returns `o`. Macro, so exception is thrown
|
||||
from the calling function."
|
||||
[o]
|
||||
`(check-kind-type ~o vertex? :vertex))
|
||||
|
||||
(defmacro check-vertices
|
||||
"If `o` is not a sequence of vertices, throw an `IllegalArgumentException` with an
|
||||
appropriate message; otherwise, returns `o`. Macro, so exception is thrown
|
||||
from the calling function."
|
||||
[o]
|
||||
`(check-kind-type-seq ~o vertex? :vertex))
|
||||
|
||||
(defn vertex=
|
||||
"True if vertices `v1`, `v2` represent the same vertex."
|
||||
[v1 v2]
|
||||
(check-vertex v1)
|
||||
(check-vertex v2)
|
||||
(every?
|
||||
#(=ish (% v1) (% v2))
|
||||
[:x :y :z]))
|
||||
|
||||
(defn vertex*
|
||||
"Return a vertex like `v1`, but with each of its coordinates multiplied
|
||||
by the equivalent vertex in `v2`. It is an error, and an exception will
|
||||
be thrown, if either `v1` or `v2` is not a vertex."
|
||||
[v1 v2]
|
||||
(let [f (fn [v1 v2 coord]
|
||||
(* (or (coord v1) 0)
|
||||
;; one here is deliberate!
|
||||
(or (coord v2) 1)))]
|
||||
(assoc v1 :x (f (check-vertex v1) (check-vertex v2) :x)
|
||||
:y (f v1 v2 :y)
|
||||
:z (f v1 v2 :z))))
|
||||
|
||||
(defn vertex
|
||||
"Make a vertex with this `x`, `y` and (if provided) `z` values. Returns a map
|
||||
with those values, plus a unique `:walkmap.id/id` value, and `:kind` set to `:vertex`.
|
||||
It's not necessary to use this function to create a vertex, but the `:walkmap.id/id`
|
||||
must be present and must be unique."
|
||||
([x y]
|
||||
(let [v {:x x :y y :kind :vertex}]
|
||||
(assoc v :walkmap.id/id (vertex-key v))))
|
||||
([x y z]
|
||||
(let [v {:x x :y y :z z :kind :vertex}]
|
||||
(assoc v :walkmap.id/id (vertex-key v)))))
|
||||
|
||||
(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]
|
||||
(if
|
||||
(and
|
||||
(map? o)
|
||||
(number? (:x o))
|
||||
(number? (:y o))
|
||||
(or (nil? (:z o)) (number? (:z o))))
|
||||
(assoc o :kind :vertex :walkmap.id/id (vertex-key o))
|
||||
(throw
|
||||
(IllegalArgumentException.
|
||||
(truncate
|
||||
(str "Not a proto-vertex: must have numeric `:x` and `:y`: "
|
||||
(or o "nil"))
|
||||
80)))))
|
||||
|
||||
(def ensure3d
|
||||
"Given a vertex `o`, if `o` has a `:z` value, just return `o`; otherwise
|
||||
return a vertex like `o` but having this `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."
|
||||
(memoize
|
||||
(fn
|
||||
([o]
|
||||
(ensure3d o 0.0))
|
||||
([o dflt]
|
||||
(if (:z (check-vertex o))
|
||||
o
|
||||
(assoc o :z dflt))))))
|
||||
|
||||
(def ensure2d
|
||||
"If `o` is a vertex, set its `:z` value to zero; else throw an exception."
|
||||
(memoize
|
||||
(fn [o]
|
||||
(assoc (check-vertex o) :z 0.0))))
|
||||
|
||||
(defn within-box?
|
||||
"True if `target` is within the box defined by `minv` and `maxv`. All
|
||||
arguments must be vertices; additionally, both `minv` and `maxv` must
|
||||
have `:z` coordinates."
|
||||
[target minv maxv]
|
||||
(do
|
||||
(check-vertices [target minv maxv])
|
||||
(every?
|
||||
true?
|
||||
(map
|
||||
#(if (% target)
|
||||
(<= (% minv) (% target) (% maxv))
|
||||
true)
|
||||
[:x :y :z]))))
|
|
@ -1,52 +0,0 @@
|
|||
# -*- encoding: utf-8 -*-
|
||||
# From https://github.com/IsseiMori/binary-stl-toASCII/blob/master/BinaryToASCII.py
|
||||
# Included here to sanity check.
|
||||
import struct
|
||||
|
||||
infile = open('../the-great-game/resources/maps/heightmap.stl') #import file
|
||||
out = open('ASCII.stl', 'w') #export file
|
||||
|
||||
data = infile.read()
|
||||
|
||||
|
||||
out.write("solid ")
|
||||
|
||||
for x in xrange(0,80):
|
||||
if not ord(data[x]) == 0:
|
||||
out.write(struct.unpack('c', data[x])[0])
|
||||
else:
|
||||
pass
|
||||
out.write("\n")
|
||||
|
||||
number = data[80] + data[81] + data[82] + data[83]
|
||||
faces = struct.unpack('I',number)[0]
|
||||
|
||||
for x in range(0,faces):
|
||||
out.write("facet normal ")
|
||||
|
||||
xc = data[84+x*50] + data[85+x*50] + data[86+x*50] + data[87+x*50]
|
||||
yc = data[88+x*50] + data[89+x*50] + data[90+x*50] + data[91+x*50]
|
||||
zc = data[92+x*50] + data[93+x*50] + data[94+x*50] + data[95+x*50]
|
||||
|
||||
out.write(str(struct.unpack('f',xc)[0]) + " ")
|
||||
out.write(str(struct.unpack('f',yc)[0]) + " ")
|
||||
out.write(str(struct.unpack('f',zc)[0]) + "\n")
|
||||
|
||||
out.write("outer loop\n")
|
||||
|
||||
for y in range(1,4):
|
||||
out.write("vertex ")
|
||||
|
||||
xc = data[84+y*12+x*50] + data[85+y*12+x*50] + data[86+y*12+x*50] + data[87+y*12+x*50]
|
||||
yc = data[88+y*12+x*50] + data[89+y*12+x*50] + data[90+y*12+x*50] + data[91+y*12+x*50]
|
||||
zc = data[92+y*12+x*50] + data[93+y*12+x*50] + data[94+y*12+x*50] + data[95+y*12+x*50]
|
||||
|
||||
out.write(str(struct.unpack('f',xc)[0]) + " ")
|
||||
out.write(str(struct.unpack('f',yc)[0]) + " ")
|
||||
out.write(str(struct.unpack('f',zc)[0]) + "\n")
|
||||
|
||||
out.write("endloop\n")
|
||||
out.write("endfacet\n")
|
||||
|
||||
out.close()
|
||||
print "end"
|
|
@ -1,44 +0,0 @@
|
|||
(ns walkmap.core
|
||||
"At this stage, primarily 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]
|
||||
[hiccup.core :refer [html]]
|
||||
[me.raynes.fs :as fs]
|
||||
[taoensso.timbre :as l :refer [info error spy]]
|
||||
[walkmap.stl :refer [decode-binary-stl]]
|
||||
[walkmap.svg :refer [stl->svg]]))
|
||||
|
||||
(def ^:dynamic *sea-level*
|
||||
"The sea level on heightmaps we're currently handling. If characters are to
|
||||
be able to swin in the sea, we must model the sea bottom, so we need
|
||||
heightmaps which cover at least the continental shelf. However, the sea
|
||||
bottom is not walkable territory and can be culled from walkmaps.
|
||||
|
||||
**Note** must be a floating point number. `(= 0 0.0)` returns `false`!"
|
||||
0.0)
|
||||
|
||||
(defn ocean?
|
||||
"Of a `facet`, is the altitude of every vertice equal to `*sea-level*`?"
|
||||
[facet]
|
||||
(every?
|
||||
#(= % *sea-level*)
|
||||
(map :z (:vertices facet))))
|
||||
|
||||
(defn cull-ocean-facets
|
||||
"Ye cannae walk on water. Remove all facets from this `stl` structure which
|
||||
are at sea level."
|
||||
[stl]
|
||||
(assoc stl :facets (remove ocean? (:facets stl))))
|
||||
|
||||
(defn binary-stl-file->svg
|
||||
"Given only an `in-filename`, parse the indicated file, expected to be
|
||||
binary STL, and return an equivalent SVG structure. Given both `in-filename`
|
||||
and `out-filename`, as side-effect write the SVG to the indicated output file."
|
||||
([in-filename]
|
||||
(stl->svg (cull-ocean-facets (decode-binary-stl in-filename))))
|
||||
([in-filename out-filename]
|
||||
(let [s (binary-stl-file->svg in-filename)]
|
||||
(spit out-filename (html s))
|
||||
s)))
|
||||
|
|
@ -1,82 +0,0 @@
|
|||
(ns walkmap.edge
|
||||
"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 [clojure.math.numeric-tower :as m]
|
||||
[walkmap.path :refer [path? polygon->path]]
|
||||
[walkmap.polygon :refer [polygon?]]
|
||||
[walkmap.vertex :refer [ensure3d vertex?]]))
|
||||
|
||||
(defn edge?
|
||||
"True if `o` satisfies the conditions for a path. A path shall be a map
|
||||
having the keys `:start` and `:end`, such that the values of each of those
|
||||
keys shall be a vertex."
|
||||
[o]
|
||||
(and
|
||||
(map? o)
|
||||
(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."
|
||||
[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))))
|
||||
|
||||
(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)}))
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
(ns walkmap.geometry
|
||||
(:require [clojure.math.combinatorics :as combo]
|
||||
[clojure.math.numeric-tower :as m]
|
||||
[walkmap.edge :as e]
|
||||
[walkmap.path :refer [path? polygon->path]]
|
||||
[walkmap.polygon :refer [polygon?]]
|
||||
[walkmap.vertex :as v]))
|
||||
|
||||
(defn on?
|
||||
"True if the vertex `v` is on the edge `e`."
|
||||
[e v]
|
||||
(let [p (v/ensure3d (:start e))
|
||||
q (v/ensure3d v)
|
||||
r (v/ensure3d (:end e))]
|
||||
(and
|
||||
(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)))
|
||||
(>= (:y q) (min (:y p) (:y r)))
|
||||
(<= (:z q) (max (:z p) (:z r)))
|
||||
(>= (:z q) (min (:z p) (:z r))))))
|
||||
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
(ns walkmap.path
|
||||
"Essentially the specification for things we shall consider to be path."
|
||||
(:require [walkmap.polygon :refer [polygon?]]
|
||||
[walkmap.vertex :refer [vertex?]]))
|
||||
|
||||
(defn path?
|
||||
"True if `o` satisfies the conditions for a path. A path shall be a map
|
||||
having the key `:nodes`, whose value shall be a sequence of vertices as
|
||||
defined in `walkmap.vertex`."
|
||||
[o]
|
||||
(let
|
||||
[v (:nodes o)]
|
||||
(and
|
||||
(seq? v)
|
||||
(> (count v) 2)
|
||||
(every? vertex? v))))
|
||||
|
||||
(defn polygon->path
|
||||
"If `o` is a polygon, return an equivalent path. What's different about
|
||||
a path is that in polygons there is an implicit edge between the first
|
||||
vertex and the last. In paths, there isn't, so we need to add that
|
||||
edge explicitly.
|
||||
|
||||
If `o` is not a polygon, will throw an exception."
|
||||
[o]
|
||||
(if
|
||||
(polygon? o)
|
||||
(assoc (dissoc o :vertices) :nodes (concat (:vertices o) (list (first (:vertices o)))))
|
||||
(throw (Exception. "Not a polygon!"))))
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
(ns walkmap.polygon
|
||||
"Essentially the specification for things we shall consider to be polygons."
|
||||
(:require [walkmap.vertex :refer [vertex?]]))
|
||||
|
||||
(defn polygon?
|
||||
"True if `o` satisfies the conditions for a polygon. A polygon shall be a
|
||||
map which has a value for the key `:vertices`, where that value is a sequence
|
||||
of vertices."
|
||||
[o]
|
||||
(let
|
||||
[v (:vertices o)]
|
||||
(and
|
||||
(seq? v)
|
||||
(> (count v) 2)
|
||||
(every? vertex? v))))
|
||||
|
||||
|
|
@ -1,126 +0,0 @@
|
|||
(ns 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 :refer [info error spy]]
|
||||
[walkmap.polygon :refer [polygon?]])
|
||||
(:import org.clojars.smee.binary.core.BinaryIO
|
||||
java.io.DataInput))
|
||||
|
||||
(defn stl?
|
||||
"True if `o` is recogniseable as an STL structure. An STL structure must
|
||||
have a key `:facets`, whose value must be a sequence of polygons; and
|
||||
may have a key `:header` whose value should be a string, and/or a key
|
||||
`:count`, whose value should be a positive integer.
|
||||
|
||||
If `verify-count?` is passed and is not `false`, verify that the value of
|
||||
the `:count` header is equal to the number of facets."
|
||||
([o]
|
||||
(stl? o false))
|
||||
([o verify-count?]
|
||||
(and
|
||||
(map? o)
|
||||
(:facets o)
|
||||
(every? polygon? (:facets o))
|
||||
(if (:header o) (string? (:header o)) true)
|
||||
(if (:count o) (integer? (:count o)) true)
|
||||
(if verify-count? (= (:count o) (count (:facets o))) true))))
|
||||
|
||||
(def vect
|
||||
"A codec for vectors within a binary STL file."
|
||||
(b/ordered-map
|
||||
:x :float-le
|
||||
:y :float-le
|
||||
:z :float-le))
|
||||
|
||||
(def facet
|
||||
"A codec for a facet (triangle) within a binary STL file."
|
||||
(b/ordered-map
|
||||
:normal vect
|
||||
:vertices [vect vect vect]
|
||||
:abc :ushort-le))
|
||||
|
||||
(def binary-stl
|
||||
"A codec for binary STL files"
|
||||
(b/ordered-map
|
||||
:header (b/string "ISO-8859-1" :length 80) ;; for the time being we neither know nor care what's in this.
|
||||
:count :uint-le
|
||||
:facets (b/repeated facet)))
|
||||
|
||||
(defn decode-binary-stl
|
||||
"Parse a binary STL file from this `filename` and return an STL structure
|
||||
representing its contents.
|
||||
|
||||
**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]
|
||||
(let [in (io/input-stream filename)]
|
||||
(b/decode binary-stl in)))
|
||||
|
||||
(defn- vect->str [prefix v]
|
||||
(str prefix " " (:x v) " " (:y v) " " (:z v) "\n"))
|
||||
|
||||
(defn- facet2str [tri]
|
||||
(str
|
||||
(vect->str "facet normal" (:normal tri))
|
||||
"outer loop\n"
|
||||
(apply str
|
||||
(map
|
||||
#(vect->str "vertex" %)
|
||||
(:vertices tri)))
|
||||
"endloop\nendfacet\n"))
|
||||
|
||||
(defn stl->ascii
|
||||
"Return as a string an ASCII rendering of the `stl` structure."
|
||||
([stl]
|
||||
(stl->ascii stl "unknown"))
|
||||
([stl solidname]
|
||||
(str
|
||||
"solid "
|
||||
solidname
|
||||
(s/trim (:header stl))
|
||||
"\n"
|
||||
(apply
|
||||
str
|
||||
(map
|
||||
facet2str
|
||||
(:facets stl)))
|
||||
"endsolid "
|
||||
solidname
|
||||
"\n")))
|
||||
|
||||
(defn write-ascii-stl
|
||||
"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)]
|
||||
(write-ascii-stl
|
||||
filename stl
|
||||
(subs b 0 (or (s/index-of b ".") (count b))))))
|
||||
([filename stl solidname]
|
||||
(l/debug "Solid name is " solidname)
|
||||
(spit
|
||||
filename
|
||||
(stl->ascii stl solidname))))
|
||||
|
||||
(defn binary-stl-to-ascii
|
||||
"Convert the binary STL file indicated by `in-filename`, and write it to
|
||||
`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)]
|
||||
(binary-stl-to-ascii
|
||||
in-filename
|
||||
(str
|
||||
(subs
|
||||
in-filename
|
||||
0
|
||||
(or
|
||||
(s/last-index-of in-filename ".")
|
||||
(count in-filename)))
|
||||
".ascii"
|
||||
ext))))
|
||||
([in-filename out-filename]
|
||||
(write-ascii-stl out-filename (decode-binary-stl in-filename))))
|
|
@ -1,50 +0,0 @@
|
|||
(ns walkmap.svg
|
||||
"Utility functions for writing stereolithography (STL) files (and possibly,
|
||||
later, other geometry files of interest to us) as scalable vector graphics
|
||||
(SVG)."
|
||||
(:require [clojure.string :as s]
|
||||
[taoensso.timbre :as l :refer [info error spy]]
|
||||
[walkmap.polygon :refer [polygon?]]
|
||||
[walkmap.vertex :refer [vertex?]]))
|
||||
|
||||
(defn- facet->svg-poly
|
||||
[facet]
|
||||
[:polygon
|
||||
{:points (s/join " " (map #(str (:x %) "," (:y %)) (:vertices facet)))}])
|
||||
|
||||
(defn stl->svg
|
||||
"Convert this in-memory `stl` structure, as read by `decode-binary-stl`, into
|
||||
an in-memory hiccup representation of SVG structure, and return it."
|
||||
[stl]
|
||||
(let [minx (reduce
|
||||
min
|
||||
(map
|
||||
#(reduce min (map :x (:vertices %)))
|
||||
(:facets stl)))
|
||||
maxx (reduce
|
||||
max
|
||||
(map
|
||||
#(reduce max (map :x (:vertices %)))
|
||||
(:facets stl)))
|
||||
miny (reduce
|
||||
min
|
||||
(map
|
||||
#(reduce min (map :y (:vertices %)))
|
||||
(:facets stl)))
|
||||
maxy (reduce
|
||||
max
|
||||
(map
|
||||
#(reduce max (map :y (:vertices %)))
|
||||
(:facets stl)))]
|
||||
[:svg
|
||||
{:xmlns "http://www.w3.org/2000/svg"
|
||||
:version "1.2"
|
||||
:width (- maxx minx)
|
||||
:height (- maxy miny)
|
||||
:viewBox (s/join " " (map str [minx miny maxx maxy]))}
|
||||
(vec
|
||||
(cons
|
||||
:g
|
||||
(map
|
||||
facet->svg-poly
|
||||
(:facets stl))))]))
|
|
@ -1,43 +0,0 @@
|
|||
(ns walkmap.vertex
|
||||
"Essentially the specification for things we shall consider to be vertices.")
|
||||
|
||||
(defn vertex?
|
||||
"True if `o` satisfies the conditions for a vertex. That is, essentially,
|
||||
that it must rerpresent a two- or three- dimensional vector. A vertex is
|
||||
shall be a map having at least the keys `:x` and `:y`, where the value of
|
||||
those keys is a number. If the key `:z` is also present, its value must also
|
||||
be a number.
|
||||
|
||||
The name `vector?` was not used as that would clash with a function of that
|
||||
name in `clojure.core` whose semantics are entirely different."
|
||||
[o]
|
||||
(and
|
||||
(map? o)
|
||||
(number? (:x o))
|
||||
(number? (:y o))
|
||||
(or (nil? (:z o)) (number? (:z o)))))
|
||||
|
||||
(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."
|
||||
(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))))))
|
||||
|
||||
(def ensure2d
|
||||
"If `o` is a vertex, set its `:z` value to zero; else throw an exception."
|
||||
(memoize
|
||||
(fn [o]
|
||||
(if
|
||||
(vertex? o)
|
||||
(assoc o :z 0.0)
|
||||
(throw (Exception. "Not a vertex!"))))))
|
124
test/cc/journeyman/walkmap/edge_test.clj
Normal file
124
test/cc/journeyman/walkmap/edge_test.clj
Normal file
|
@ -0,0 +1,124 @@
|
|||
(ns cc.journeyman.walkmap.edge-test
|
||||
(:require [clojure.math.numeric-tower :as m]
|
||||
[clojure.test :refer :all]
|
||||
[cc.journeyman.walkmap.edge :refer [collinear? collinear2d? edge
|
||||
edge? intersection2d length
|
||||
minimaxd parallel? overlaps2d?
|
||||
unit-vector]]
|
||||
[cc.journeyman.walkmap.vertex :refer [vertex vertex=]]))
|
||||
|
||||
(deftest edge-test
|
||||
(testing "identification of edges."
|
||||
(is (edge? {:start (vertex 0.0 0.0 0.0)
|
||||
:end (vertex 3 4 0.0)}) "It is.")
|
||||
(is (not (edge? {:start {:y 0.0 :z 0.0 :walkmap.id/id 'foo}
|
||||
:end {:x 3 :y 4 :z 0.0 :walkmap.id/id 'bar}})) "Start lacks :x key")
|
||||
(is (not (edge? {:start {:x nil :y 0.0 :z 0.0 :walkmap.id/id 'foo}
|
||||
:end {:x 3 :y 4 :z 0.0 :walkmap.id/id 'bar}})) "Start lacks :x value")
|
||||
(is (not (edge? {:begin {:x nil :y 0.0 :z 0.0 :walkmap.id/id 'foo}
|
||||
:end {:x 3 :y 4 :z 0.0 :walkmap.id/id 'bar}})) "Lacks start key")
|
||||
(is (not (edge? {:start {:x nil :y 0.0 :z 0.0 :walkmap.id/id 'foo}
|
||||
:finish {:x 3 :y 4 :z 0.0 :walkmap.id/id 'bar}})) "Lacks end key")
|
||||
(is (not (edge? {:start {:x "zero" :y 0.0 :z 0.0 :walkmap.id/id 'foo}
|
||||
:end {:x 3 :y 4 :z 0.0 :walkmap.id/id 'bar}})) "Value of x in start is not a number")
|
||||
(is (false? (edge? "I am not an edge")) "Edge mustbe a map.")))
|
||||
|
||||
(deftest collinear-test
|
||||
(testing "collinearity"
|
||||
(is (collinear? {:start {:x 0.0 :y 0.0 :z 0.0 :walkmap.id/id 'foo} :end {:x 3.0 :y 4.0 :z 0.0 :walkmap.id/id 'bar}}
|
||||
{:start {:x 3.0 :y 4.0 :z 0.0 :walkmap.id/id 'foo} :end {:x 9.0 :y 12.0 :z 0.0 :walkmap.id/id 'bar}})
|
||||
"Should be")
|
||||
(is (not
|
||||
(collinear? {:start {:x 0.0 :y 0.0 :z 0.0 :walkmap.id/id 'foo} :end {:x 3 :y 4 :z 0.0 :walkmap.id/id 'bar}}
|
||||
{:start {:x 1.0 :y 2.0 :z 3.5 :walkmap.id/id 'foo} :end {:x 4.0 :y 6.0 :z 3.5 :walkmap.id/id 'bar}}))
|
||||
"Should not be!")
|
||||
(is (collinear? {:start {:x 0.0 :y 0.0 :z 0.0 :walkmap.id/id 'foo} :end {:x 3.0 :y 4.0 :z 0.0 :walkmap.id/id 'bar}}
|
||||
{:start {:x 0.0 :y 0.0 :z 0.0 :walkmap.id/id 'foo} :end {:x 9.0 :y 12.0 :z 0.0 :walkmap.id/id 'bar}})
|
||||
"Edge case: same start location")
|
||||
(is (collinear? {:start {:x 0.0 :y 0.0 :z 0.0 :walkmap.id/id 'foo} :end {:x 9.0 :y 12.0 :z 0.0 :walkmap.id/id 'bar}}
|
||||
{:start {:x 3.0 :y 4.0 :z 0.0 :walkmap.id/id 'foo} :end {:x 9.0 :y 12.0 :z 0.0 :walkmap.id/id 'bar}})
|
||||
"Edge case: same end location")
|
||||
))
|
||||
|
||||
(deftest collinear2d-test
|
||||
(testing "Collinearity when projected onto the x,y plane."
|
||||
(is (collinear2d? (edge (vertex 1.0 1.0) (vertex 5.0 5.0))
|
||||
(edge (vertex 4.0 4.0) (vertex 6.0 6.0)))
|
||||
"Collinear, overlapping.")
|
||||
(is (collinear2d? (edge (vertex 1.0 1.0 0.0) (vertex 5.0 5.0 5.0))
|
||||
(edge (vertex 4.0 4.0 79.3) (vertex 6.0 6.0 0.2)))
|
||||
"Separated in the z axis, but collinear in x, y.")))
|
||||
|
||||
(deftest construction-test
|
||||
(testing "Construction of edges."
|
||||
(is (edge? (edge (vertex 1.0 2.0 3.0) (vertex 4.0 8.0 12.0)))
|
||||
"If both arguments are vertices, we should get an edge")
|
||||
(is (thrown? IllegalArgumentException (edge "Not a vertex" (vertex 1 2)))
|
||||
"If first argument is not a vertex, we should get an exception.")
|
||||
(is (thrown? IllegalArgumentException (edge (vertex 1 2) "Not a vertex"))
|
||||
"If second argument is not a vertex, we should get an exception.")))
|
||||
|
||||
(deftest intersection2d-test
|
||||
(testing "intersection of two edges projected onto the x,y plane."
|
||||
(is (thrown? IllegalArgumentException
|
||||
(intersection2d
|
||||
(edge (vertex 1.0 1.0) (vertex 5.0 5.0))
|
||||
"This is not an edge"))
|
||||
"Not an edge (second arg) -> exception.")
|
||||
(is (thrown? IllegalArgumentException
|
||||
(intersection2d
|
||||
"This is not an edge"
|
||||
(edge (vertex 1.0 1.0) (vertex 5.0 5.0))))
|
||||
"Not an edge (first arg) -> exception.")
|
||||
(is (nil? (intersection2d (edge (vertex 1.0 1.0) (vertex 5.0 5.0))
|
||||
(edge (vertex 1.0 2.0) (vertex 5.0 6.0))))
|
||||
"Parallel but not intersecting.")
|
||||
(is (:x (intersection2d (edge (vertex 1.0 1.0) (vertex 5.0 5.0))
|
||||
(edge (vertex 4.0 4.0) (vertex 6.0 6.0)))
|
||||
5.0)
|
||||
"Collinear, overlapping, should choose the overlapping end of the first edge.")
|
||||
(is (= (:x (intersection2d (edge (vertex 1.0 1.0) (vertex 5.0 5.0))
|
||||
(edge (vertex 1.0 5.0) (vertex 5.0 1.0))))
|
||||
3.0)
|
||||
"Crossing, should intersect at 3.0, 3.0: x coord.")
|
||||
(is (= (:y (intersection2d (edge (vertex 1.0 1.0) (vertex 5.0 5.0))
|
||||
(edge (vertex 1.0 5.0) (vertex 5.0 1.0))))
|
||||
3.0)
|
||||
"Crossing, should intersect at 3.0, 3.0: y coord.")
|
||||
(is (= (:y (intersection2d (edge (vertex 1.0 1.0 0.0) (vertex 5.0 5.0 0.0))
|
||||
(edge (vertex 1.0 5.0 999) (vertex 5.0 1.0 379))))
|
||||
3.0)
|
||||
"Crossing, presence of z coordinate should make no difference")))
|
||||
|
||||
(deftest length-test
|
||||
(testing "length of an edge"
|
||||
(is (= (length {:start {:x 0.0 :y 0.0 :z 0.0 :walkmap.id/id 'foo} :end {:x 3.0 :y 4.0 :z 0.0 :walkmap.id/id 'bar}}) 5.0))))
|
||||
|
||||
(deftest minimaxd-test
|
||||
(testing "finding minimum and maximum coordinates of edges."
|
||||
(is (= (minimaxd (edge (vertex 1.0 2.0 3.0) (vertex 4.0 8.0 12.0)) :x min) 1.0))
|
||||
(is (= (minimaxd (edge (vertex 1.0 2.0 3.0) (vertex 4.0 8.0 12.0)) :y max) 8.0))))
|
||||
|
||||
(deftest parallel-test
|
||||
(testing "parallelism"
|
||||
(is (parallel? (edge (vertex 0.0 0.0 0.0) (vertex 3 4 0.0))
|
||||
(edge (vertex 1.0 2.0 3.5) (vertex 4.0 6.0 3.5)))
|
||||
"Should be")
|
||||
(is (not
|
||||
(parallel? (edge (vertex 0.0 0.0 0.0) (vertex 3 4 0.0))
|
||||
(edge (vertex 1.0 2.0 3.5) (vertex 4.0 6.0 3.49))))
|
||||
"Should not be!")))
|
||||
|
||||
(deftest overlaps2d-test
|
||||
(testing "whether two edges are in the same area of the x,y plane."
|
||||
(is (false? (overlaps2d? (edge (vertex 1 1) (vertex 4 4)) (edge (vertex 5 5) (vertex 8 8)))))
|
||||
(is (overlaps2d? (edge (vertex 1 1) (vertex 4 4)) (edge (vertex 4 4) (vertex 1 1))))))
|
||||
|
||||
(deftest unit-vector-test
|
||||
(testing "deriving the unit vector"
|
||||
(is (vertex=
|
||||
(unit-vector (edge (vertex 0.0 0.0 0.0) (vertex 3 4 0.0)))
|
||||
(vertex 0.6 0.8 0.0)))
|
||||
(is (vertex=
|
||||
(unit-vector (edge (vertex 1.0 2.0 3.5) (vertex 4.0 6.0 3.5)))
|
||||
(vertex 0.6 0.8 0.0)))))
|
53
test/cc/journeyman/walkmap/ocean_test.clj
Normal file
53
test/cc/journeyman/walkmap/ocean_test.clj
Normal file
|
@ -0,0 +1,53 @@
|
|||
(ns cc.journeyman.walkmap.ocean-test
|
||||
(:require [clojure.test :refer :all]
|
||||
[cc.journeyman.walkmap.ocean :refer [*sea-level* cull-ocean-facets ocean?]]
|
||||
[cc.journeyman.walkmap.polygon :refer [polygon]]
|
||||
[cc.journeyman.walkmap.vertex :refer [vertex vertex=]]))
|
||||
|
||||
(deftest ocean-tests
|
||||
(testing "Identification of polygons at sea level"
|
||||
(is (ocean? (polygon (vertex 0 0 0) (vertex 0 1 0) (vertex 1 0 0)))
|
||||
"All `:z` coordinates are zero, and default binding for `*sea-level*`
|
||||
=> ocean.")
|
||||
(is (false? (ocean? (polygon (vertex 0 0 1) (vertex 0 1 0) (vertex 1 0 0))))
|
||||
"Not all `:z` coordinates are zero, and default binding for `*sea-level*`
|
||||
=> not ocean.")
|
||||
(is (false? (ocean? (polygon (vertex 0 0 5) (vertex 0 1 5) (vertex 1 0 5))))
|
||||
"Not all `:z` coordinates are five, and default binding for `*sea-level*`
|
||||
=> not ocean.")
|
||||
(binding [*sea-level* 5]
|
||||
(is (false? (ocean? (polygon (vertex 0 0 0) (vertex 0 1 0) (vertex 1 0 0))))
|
||||
"All `:z` coordinates are zero, and `*sea-level*` rebound to five
|
||||
=> not ocean.")
|
||||
(is (false? (ocean? (polygon (vertex 0 0 1) (vertex 0 1 0) (vertex 1 0 0))))
|
||||
"Not all `:z` coordinates are zero, and `*sea-level*` rebound to five
|
||||
=> not ocean.")
|
||||
(is (ocean? (polygon (vertex 0 0 5) (vertex 0 1 5) (vertex 1 0 5)))
|
||||
"Not all `:z` coordinates are five, and `*sea-level*` rebound to five
|
||||
=> ocean."))))
|
||||
|
||||
(deftest cull-ocean-facets-tests
|
||||
(testing "Culling of ocean facets (not currently used)."
|
||||
(let [stl {:facets [(polygon (vertex 0 0 0) (vertex 0 1 0) (vertex 1 0 0))
|
||||
(polygon (vertex 0 0 1) (vertex 0 1 0) (vertex 1 0 0))
|
||||
(polygon (vertex 0 0 5) (vertex 0 1 5) (vertex 1 0 5))]}
|
||||
expected {:facets
|
||||
[(polygon (vertex 0 0 1) (vertex 0 1 0) (vertex 1 0 0))
|
||||
(polygon (vertex 0 0 5) (vertex 0 1 5) (vertex 1 0 5))]}
|
||||
actual (cull-ocean-facets stl)]
|
||||
(map
|
||||
#(is (vertex= (nth (:facets expected) %) (nth (:facets actual) %))
|
||||
(str "Facet " % " did not match."))
|
||||
(range (max (count (:facets expected)) (count (:facets actual))))))
|
||||
(binding [*sea-level* 5]
|
||||
(let [stl {:facets [(polygon (vertex 0 0 0) (vertex 0 1 0) (vertex 1 0 0))
|
||||
(polygon (vertex 0 0 1) (vertex 0 1 0) (vertex 1 0 0))
|
||||
(polygon (vertex 0 0 5) (vertex 0 1 5) (vertex 1 0 5))]}
|
||||
expected {:facets
|
||||
[(polygon (vertex 0 0 0) (vertex 0 1 0) (vertex 1 0 0))
|
||||
(polygon (vertex 0 0 1) (vertex 0 1 0) (vertex 1 0 0))]}
|
||||
actual (cull-ocean-facets stl)]
|
||||
(map
|
||||
#(is (vertex= (nth (:facets expected) %) (nth (:facets actual) %))
|
||||
(str "Facet " % " did not match."))
|
||||
(range (max (count (:facets expected)) (count (:facets actual)))))))))
|
113
test/cc/journeyman/walkmap/path_test.clj
Normal file
113
test/cc/journeyman/walkmap/path_test.clj
Normal file
|
@ -0,0 +1,113 @@
|
|||
(ns cc.journeyman.walkmap.path-test
|
||||
(:require [clojure.test :refer :all]
|
||||
[cc.journeyman.walkmap.edge :refer [edge?]]
|
||||
[cc.journeyman.walkmap.path :refer [check-path check-paths
|
||||
length path path? path->edges
|
||||
polygon->path]]
|
||||
[cc.journeyman.walkmap.polygon :refer [polygon]]
|
||||
[cc.journeyman.walkmap.utils :refer [kind-type]]
|
||||
[cc.journeyman.walkmap.vertex :refer [vertex vertex=]]))
|
||||
|
||||
(deftest path-tests
|
||||
(testing "Path instantiation"
|
||||
(is (= (kind-type (path (vertex 0 0 0) (vertex 1 1 1))) :path)
|
||||
"Paths should be identified as paths.")
|
||||
(is (path? (path (vertex 0 0 0) (vertex 1 1 1)))
|
||||
"Paths should test as paths.")
|
||||
(is (check-path (path (vertex 0 0 0) (vertex 1 1 1)))
|
||||
"No exception should be thrown when checking a valid path.")
|
||||
(is (thrown?
|
||||
IllegalArgumentException
|
||||
(check-path
|
||||
(update-in
|
||||
(path (vertex 0 0 0) (vertex 1 1 1))
|
||||
[:vertices]
|
||||
conj
|
||||
"Not a vertex")))
|
||||
"Checking an invalid path should throw an exception.")
|
||||
(is (thrown?
|
||||
IllegalArgumentException
|
||||
(path (vertex 0 0 0)))
|
||||
"Too short.")
|
||||
(is (thrown?
|
||||
IllegalArgumentException
|
||||
(path (vertex 0 0 0) (vertex 1 1 1) "Not a vertex"))
|
||||
"Non-vertex included.")
|
||||
(is (thrown?
|
||||
IllegalArgumentException
|
||||
(path (vertex 0 0 0) (vertex 1 1 1) "Not a vertex."))
|
||||
"Passing something which is not a vertex when constructing a path whould
|
||||
cause an exception to be thrown.")))
|
||||
|
||||
(deftest conversion-tests
|
||||
(testing "Converting polygons to paths"
|
||||
(let [poly (polygon (vertex 0 0 0) (vertex 1 0 0) (vertex 1 1 0) (vertex 0 1 0))
|
||||
p (polygon->path poly)]
|
||||
(is (path? p) "Should be a path.")
|
||||
(is (vertex= (first (:vertices p)) (last (:vertices p)))
|
||||
"First and last vertices of the generated path should be equal to
|
||||
one another.")
|
||||
(is (= (count (:vertices p)) (inc (count (:vertices poly))))
|
||||
"The generated path should have one more vertex than the polygon.")
|
||||
(map
|
||||
#(is (vertex= (nth (:vertices poly) %) (nth (:vertices p) %))
|
||||
(str "Vertex " % " from each set of vertices should be the same."))
|
||||
(range (count (:vertices poly))))))
|
||||
(testing "Converting polygons and paths to edges."
|
||||
(let [poly (polygon (vertex 0 0 0) (vertex 1 0 0) (vertex 1 1 0) (vertex 0 1 0))
|
||||
edges (path->edges poly)]
|
||||
(is (every? edge? edges)
|
||||
"Every returned edge should be an edge.")
|
||||
(is (= (count (:vertices poly)) (count edges))
|
||||
"There should be the same number of edges as the vertices of the polygon")
|
||||
(doall
|
||||
(map
|
||||
#(is
|
||||
(vertex= (nth (:vertices poly) %) (:start (nth edges %)))
|
||||
(str
|
||||
"Each edge should start from the same place as the corresponding
|
||||
vertex: " %))
|
||||
(range (count (:vertices poly)))))
|
||||
(doall
|
||||
(map
|
||||
#(is
|
||||
(vertex= (nth (:vertices poly) (mod (inc %) (count (:vertices poly))))
|
||||
(:end (nth edges %)))
|
||||
(str
|
||||
"Each edge should end at the same place as the subsequent
|
||||
vertex: " %))
|
||||
(range (count (:vertices poly))))))
|
||||
(is (thrown? IllegalArgumentException
|
||||
(path->edges "Not a legal argument.")))))
|
||||
|
||||
(deftest check-paths-tests
|
||||
(testing "Checking multiple paths."
|
||||
(is (thrown? IllegalArgumentException
|
||||
(check-paths [(path (vertex 0 0 0)
|
||||
(vertex 1 0 0)
|
||||
(vertex 1 1 0)
|
||||
(vertex 0 1 0)
|
||||
(vertex 0 0 0))
|
||||
(path (vertex 0 0 1)
|
||||
(vertex 1 0 1)
|
||||
(vertex 1 1 1)
|
||||
(vertex 0 1 1)
|
||||
(vertex 0 0 1))
|
||||
(vertex 0 0 0)]))
|
||||
"Not all elements are paths")
|
||||
(is (check-paths [(path (vertex 0 0 0)
|
||||
(vertex 1 0 0)
|
||||
(vertex 1 1 0)
|
||||
(vertex 0 1 0)
|
||||
(vertex 0 0 0))
|
||||
(path (vertex 0 0 1)
|
||||
(vertex 1 0 1)
|
||||
(vertex 1 1 1)
|
||||
(vertex 0 1 1)
|
||||
(vertex 0 0 1))])
|
||||
"All elements are paths")))
|
||||
|
||||
(deftest length-tests
|
||||
(testing "length of paths"
|
||||
(let [p (path (vertex 0 0 0) (vertex 1 0 0) (vertex 1 1 0) (vertex 0 1 0) (vertex 0 0 0))]
|
||||
(is (= (length p) 4) "By inspection."))))
|
85
test/cc/journeyman/walkmap/polygon_test.clj
Normal file
85
test/cc/journeyman/walkmap/polygon_test.clj
Normal file
|
@ -0,0 +1,85 @@
|
|||
(ns cc.journeyman.walkmap.polygon-test
|
||||
(:require [clojure.test :refer :all]
|
||||
;; [clojure.algo.generic.math-functions :as m]
|
||||
;; [cc.journeyman.walkmap.edge :refer [edge?]]
|
||||
;; [cc.journeyman.walkmap.path :refer :all]
|
||||
[cc.journeyman.walkmap.polygon :refer [centre check-polygon
|
||||
check-polygons
|
||||
check-triangle gradient
|
||||
polygon polygon?
|
||||
triangle?]]
|
||||
[cc.journeyman.walkmap.utils :refer [kind-type]]
|
||||
[cc.journeyman.walkmap.vertex :refer [vertex vertex? vertex=]])
|
||||
)
|
||||
|
||||
(deftest polygon-tests
|
||||
(testing "Constructing polygons"
|
||||
(let [square (polygon (vertex 0 0 0) (vertex 1 0 0)
|
||||
(vertex 1 1 0) (vertex 0 1 0))
|
||||
triangle (polygon (vertex 0 0 0) (vertex 0 3 0)
|
||||
(vertex 4 0 0))]
|
||||
(is (= (kind-type square) :polygon)
|
||||
"Square should have `:kind` = `:polygon`.")
|
||||
(is (= (kind-type triangle) :polygon)
|
||||
"Triangle should have `:kind` = `:polygon`.")
|
||||
(is (polygon? square) "Square should be a polygon.")
|
||||
(is (polygon? triangle) "Triangle should be a polygon.")
|
||||
(is (false? (triangle? square)) "Square is not a triangle.")
|
||||
(is (triangle? triangle) "Triangle is a triangle.")
|
||||
(is (check-polygon square) "No exception should be thrown.")
|
||||
(is (check-polygon triangle) "No exception should be thrown.")
|
||||
(is (check-triangle triangle) "No exception should be thrown.")
|
||||
(is (check-polygons [square triangle])
|
||||
"No exception should be thrown.")
|
||||
(is (thrown?
|
||||
IllegalArgumentException
|
||||
(check-polygon "Not a polygon")) "Not a polygon")
|
||||
(is (thrown?
|
||||
IllegalArgumentException
|
||||
(check-polygons [square triangle "Not a polygon"]))
|
||||
"One value is not a polygon.")
|
||||
(is (thrown?
|
||||
IllegalArgumentException (check-triangle square))
|
||||
"Not a triangle.")
|
||||
(is (thrown?
|
||||
IllegalArgumentException (polygon (vertex 0 0 0) (vertex 1 0 0)))
|
||||
"Too few vertices.")
|
||||
(is (thrown?
|
||||
IllegalArgumentException (polygon (vertex 0 0 0) (vertex 1 0 0)
|
||||
(vertex 1 1 0) "Not a vertex"
|
||||
(vertex 0 1 0)))
|
||||
"Non-vertex included.")
|
||||
)
|
||||
))
|
||||
|
||||
(deftest gradient-tests
|
||||
(testing "Finding the gradient across a triangle."
|
||||
(let [tri (polygon (vertex 0 0 1) (vertex 1 0 0) (vertex 1 1 0.5))
|
||||
gra (gradient tri)]
|
||||
(is (nil? (:gradient tri)) "Basic trangle should not have a gradient.")
|
||||
(is (vertex? (:gradient gra))
|
||||
"After passing through gradient function, it should have a gradient.")
|
||||
;; TODO: I need to check that the gradient is being computed correclt,
|
||||
;; but my brain isn't up to the trigonometry just now.
|
||||
)))
|
||||
|
||||
(deftest centre-tests
|
||||
(testing "Finding the centres of polygons."
|
||||
(let [square (polygon (vertex 0 0 0) (vertex 1 0 0)
|
||||
(vertex 1 1 0) (vertex 0 1 0))
|
||||
triangle (polygon (vertex 0 0 0) (vertex 0 3 0)
|
||||
(vertex 4 0 0))
|
||||
centred (centre triangle)]
|
||||
(is (vertex= (:centre centred) (vertex 1.3333333 1.0 0.0))
|
||||
"By inspection (check this maths!).")
|
||||
(is (thrown?
|
||||
UnsupportedOperationException
|
||||
(centre square))
|
||||
"We can't yet find the centre of a quadrilateral, but we should be
|
||||
able to do so, so it isn't an illegal argument, it just doesn't
|
||||
work.")
|
||||
(is (thrown?
|
||||
IllegalArgumentException
|
||||
(centre "Not a polygon"))
|
||||
"Anything else that isn't a polygon, though, is an illegal argument."))))
|
||||
|
96
test/cc/journeyman/walkmap/stl_test.clj
Normal file
96
test/cc/journeyman/walkmap/stl_test.clj
Normal file
|
@ -0,0 +1,96 @@
|
|||
(ns cc.journeyman.walkmap.stl-test
|
||||
(:require [clojure.test :refer :all]
|
||||
[cc.journeyman.walkmap.stl :refer [canonicalise stl?]]
|
||||
[cc.journeyman.walkmap.polygon :refer [polygon?]]
|
||||
[cc.journeyman.walkmap.vertex :refer [vertex?]]))
|
||||
|
||||
(deftest canonicalise-test
|
||||
(testing "Canonicalisation of objects read from STL: vertices."
|
||||
(is (vertex? (canonicalise {:x 3.0, :y 1.0, :z 1.0}))
|
||||
"Vertex: should have an `:walkmap.id/id` and `:kind` = `:vertex`.")
|
||||
(is (= (:x (canonicalise {:x 3.0, :y 1.0, :z 1.0})) 3.0)
|
||||
"`:x` value should be unchanged.")
|
||||
(is (= (:y (canonicalise {:x 3.0, :y 1.0, :z 1.0})) 1.0)
|
||||
"`:y` value should be unchanged.")
|
||||
(is (= (:z (canonicalise {:x 3.0, :y 1.0, :z 1.0})) 1.0)
|
||||
"`:z` value should be unchanged.")
|
||||
(is (every?
|
||||
vertex?
|
||||
(canonicalise [{:x 3.0, :y 1.0, :z 1.0}
|
||||
{:x 2.0, :y 3.0, :z 1.0}
|
||||
{:x 0.0, :y 0.0, :z 1.0}]))
|
||||
"Vertices: should recurse."))
|
||||
(testing "Canonicalisation of objects read from STL: facets/polygons."
|
||||
(let [p {:normal {:x -0.0, :y 0.0, :z 1.0},
|
||||
:vertices [{:x 3.0, :y 1.0, :z 1.0}
|
||||
{:x 2.0, :y 3.0, :z 1.0}
|
||||
{:x 0.0, :y 0.0, :z 1.0}],
|
||||
:abc 0}
|
||||
p' (canonicalise p)]
|
||||
(is (polygon? p')
|
||||
"Polygon: should have an `:walkmap.id/id` and `:kind` = `:polygon`.")
|
||||
(is (= (count (:vertices p)) (count (:vertices p')))
|
||||
"Number of vertices should not change")
|
||||
(map
|
||||
#(is (= (map % (:vertices p))(map % (:vertices p')))
|
||||
(str "Order of vertices should not change: " %))
|
||||
[:x :y :z]))
|
||||
(is (every?
|
||||
polygon?
|
||||
(canonicalise
|
||||
[{:normal {:x -0.0, :y 0.0, :z 1.0},
|
||||
:vertices [{:x 3.0, :y 1.0, :z 1.0}
|
||||
{:x 2.0, :y 3.0, :z 1.0}
|
||||
{:x 0.0, :y 0.0, :z 1.0}],
|
||||
:abc 0}
|
||||
{:normal {:x 0.0, :y 0.0, :z 1.0},
|
||||
:vertices [{:x 10.0, :y 4.0, :z 1.0}
|
||||
{:x 22.0, :y 3.0, :z 1.0}
|
||||
{:x 13.0, :y 5.0, :z 1.0}],
|
||||
:abc 0}
|
||||
{:normal {:x 0.0, :y 0.0, :z 1.0},
|
||||
:vertices [{:x 26.0, :y 46.0, :z 1.0}
|
||||
{:x 29.0, :y 49.0, :z 1.0}
|
||||
{:x 31.0, :y 61.0, :z 1.0}],
|
||||
:abc 0}
|
||||
{:normal {:x -0.0, :y 0.0, :z 1.0},
|
||||
:vertices [{:x 16.0, :y 33.0, :z 1.0}
|
||||
{:x 15.0, :y 35.0, :z 1.0}
|
||||
{:x 13.0, :y 32.0, :z 1.0}],
|
||||
:abc 0}
|
||||
{:normal {:x 0.0, :y 0.0, :z 1.0},
|
||||
:vertices [{:x 81.0, :y 0.0, :z 1.0}
|
||||
{:x 54.0, :y 27.0, :z 1.0}
|
||||
{:x 51.0, :y 20.0, :z 1.0}],
|
||||
:abc 0}]))
|
||||
"Facets/polygons: should recurse."))
|
||||
(testing "Canonicalisation of entire STL structure."
|
||||
(let [stl {:header "Dummy test STL",
|
||||
:count 5,
|
||||
:facets [{:normal {:x -0.0, :y 0.0, :z 1.0},
|
||||
:vertices [{:x 3.0, :y 1.0, :z 1.0}
|
||||
{:x 2.0, :y 3.0, :z 1.0}
|
||||
{:x 0.0, :y 0.0, :z 1.0}],
|
||||
:abc 0}
|
||||
{:normal {:x 0.0, :y 0.0, :z 1.0},
|
||||
:vertices [{:x 10.0, :y 4.0, :z 1.0}
|
||||
{:x 22.0, :y 3.0, :z 1.0}
|
||||
{:x 13.0, :y 5.0, :z 1.0}],
|
||||
:abc 0}
|
||||
{:normal {:x 0.0, :y 0.0, :z 1.0},
|
||||
:vertices [{:x 26.0, :y 46.0, :z 1.0}
|
||||
{:x 29.0, :y 49.0, :z 1.0}
|
||||
{:x 31.0, :y 61.0, :z 1.0}],
|
||||
:abc 0}
|
||||
{:normal {:x -0.0, :y 0.0, :z 1.0},
|
||||
:vertices [{:x 16.0, :y 33.0, :z 1.0}
|
||||
{:x 15.0, :y 35.0, :z 1.0}
|
||||
{:x 13.0, :y 32.0, :z 1.0}],
|
||||
:abc 0}
|
||||
{:normal {:x 0.0, :y 0.0, :z 1.0},
|
||||
:vertices [{:x 81.0, :y 0.0, :z 1.0}
|
||||
{:x 54.0, :y 27.0, :z 1.0}
|
||||
{:x 51.0, :y 20.0, :z 1.0}],
|
||||
:abc 0}]}
|
||||
stl' (canonicalise stl)]
|
||||
(is (stl? stl') "Stl: should have an `:walkmap.id/id` and `:kind` = `:stl`."))))
|
135
test/cc/journeyman/walkmap/superstructure_test.clj
Normal file
135
test/cc/journeyman/walkmap/superstructure_test.clj
Normal file
|
@ -0,0 +1,135 @@
|
|||
(ns cc.journeyman.walkmap.superstructure-test
|
||||
(:require [clojure.set :refer [subset?]]
|
||||
[clojure.test :refer :all]
|
||||
[cc.journeyman.walkmap.path :as p]
|
||||
[cc.journeyman.walkmap.polygon :as q]
|
||||
[cc.journeyman.walkmap.superstructure :refer [retrieve store vertex-index]]
|
||||
[cc.journeyman.walkmap.tag :as t]
|
||||
[cc.journeyman.walkmap.utils :as u]
|
||||
[cc.journeyman.walkmap.vertex :as v]))
|
||||
|
||||
(deftest store-test
|
||||
(testing "Object storage"
|
||||
(let [p (p/path
|
||||
(v/vertex (rand) (rand) (rand))
|
||||
(v/vertex (rand) (rand) (rand))
|
||||
(v/vertex (rand) (rand) (rand))
|
||||
(v/vertex (rand) (rand) (rand)))
|
||||
id (:walkmap.id/id p)
|
||||
s (store p)
|
||||
r (id s)]
|
||||
(is (= (:walkmap.id/id r) id)
|
||||
"A representation should be stored in `s` keyed by `id`, and the id of that representation should be `id`.")
|
||||
(is (= (:kind r) (:kind p))
|
||||
"The representation should have the same value for `:kind`.")
|
||||
(is (= (count (:vertices p)) (count (:vertices r)))
|
||||
"The representation of `p` in `s` should have the same number of vertices as `p`.")
|
||||
(is (every? v/vertex? (:vertices p))
|
||||
"Every vertex of `p` should be a vertex.")
|
||||
(is (every? keyword? (:vertices r))
|
||||
"Every vertex of the representation of `p` in `s` should be a keyword.")
|
||||
(is (every? v/vertex? (map #(s %) (:vertices r)))
|
||||
"The value in `s` of every vertex of the representation of `p` in `s`
|
||||
should be a vertex.")
|
||||
(is (subset? (set (:vertices r)) (set (keys (vertex-index s))))
|
||||
"All the keys which are vertices of the representation of `p` in `s`
|
||||
should be present as keys in the vertex-index of `s`.")
|
||||
(is (every?
|
||||
#(s (% id))
|
||||
(map #(set (keys (% (vertex-index s)))) (:vertices r)))
|
||||
"The value in the vertex-index in `s` for each keyword in the
|
||||
vertexes of the representation of `p` in `s` should include,
|
||||
as a key, the `id` of `p`."))))
|
||||
|
||||
(deftest retrieve-test
|
||||
(testing "Object retrieval"
|
||||
;; the value of `s` here is hand-typed; think of it as a specification
|
||||
(let [s {:path1 {:walkmap.id/id :path1
|
||||
:kind :path
|
||||
:vertices '(:vert_0_0_0
|
||||
:vert_0_0_1
|
||||
:vert_1_0_0)}
|
||||
:vert_0_0_0 {:walkmap.id/id :vert_0_0_0
|
||||
:kind :vertex
|
||||
:x 0
|
||||
:y 0
|
||||
:z 0}
|
||||
:vert_0_0_1 {:walkmap.id/id :vert_0_0_1
|
||||
:kind :vertex
|
||||
:x 0
|
||||
:y 0
|
||||
:z 1}
|
||||
:vert_1_0_0 {:walkmap.id/id :vert_1_0_0
|
||||
:kind :vertex
|
||||
:x 1
|
||||
:y 0
|
||||
:z 0}
|
||||
:walkmap.superstructure/vertex-index {:vert_0_0_0 {:path1 :vert_0_0_0}
|
||||
:vert_0_0_1 {:path1 :vert_0_0_1}
|
||||
:vert_1_0_0 {:path1 :vert_1_0_0}}}
|
||||
expected {:kind :path,
|
||||
:vertices
|
||||
'({:kind :vertex, :x 0, :y 0, :z 0, :walkmap.id/id :vert_0_0_0}
|
||||
{:kind :vertex, :x 0, :y 0, :z 1, :walkmap.id/id :vert_0_0_1}
|
||||
{:kind :vertex, :x 1, :y 0, :z 0, :walkmap.id/id :vert_1_0_0}),
|
||||
:walkmap.id/id :path1}]
|
||||
(is (= (retrieve :path1 s) expected)
|
||||
"The object reconstructed from the superstructure."))))
|
||||
|
||||
(deftest round-trip-test
|
||||
(testing "Roundtripping an object through the superstructure."
|
||||
(let [p (p/path
|
||||
(v/vertex (rand) (rand) (rand))
|
||||
(v/vertex (rand) (rand) (rand))
|
||||
(v/vertex (rand) (rand) (rand))
|
||||
(v/vertex (rand) (rand) (rand)))
|
||||
id (:walkmap.id/id p)
|
||||
s (store p)
|
||||
r (retrieve id s)]
|
||||
(is (= p r) "As it was, so it shall be."))))
|
||||
|
||||
(deftest multi-object-round-trip-test
|
||||
(testing "Roundtripping two different objects through a superstructure."
|
||||
(let [p (p/path
|
||||
(v/vertex (rand) (rand) (rand))
|
||||
(v/vertex (rand) (rand) (rand))
|
||||
(v/vertex (rand) (rand) (rand))
|
||||
(v/vertex (rand) (rand) (rand)))
|
||||
q (p/path
|
||||
(v/vertex (rand) (rand) (rand))
|
||||
(v/vertex (rand) (rand) (rand))
|
||||
(v/vertex (rand) (rand) (rand))
|
||||
(v/vertex (rand) (rand) (rand)))
|
||||
pid (:walkmap.id/id p)
|
||||
qid (:walkmap.id/id q)
|
||||
s (store q (store p))
|
||||
rp (retrieve pid s)
|
||||
rq (retrieve qid s)]
|
||||
(is (= p rp) "As `p` was, so it shall be.")
|
||||
(is (= q rq) "As `q` was, so it shall be.")
|
||||
(is (not= pid qid)
|
||||
"It is not possible that the ids should be equal, since they are
|
||||
gensymmed")
|
||||
(is (not= rp rq)
|
||||
"It is not possible that the paths should be equal, since at
|
||||
minimum, their ids are gensymmed."))))
|
||||
|
||||
(deftest store-retrieve-edit-store-test
|
||||
(testing "After editing a retrieved object and storing it again, a further
|
||||
retrieve should return the new version."
|
||||
(let [p (p/path
|
||||
(v/vertex (rand) (rand) (rand))
|
||||
(v/vertex (rand) (rand) (rand))
|
||||
(v/vertex (rand) (rand) (rand))
|
||||
(v/vertex (rand) (rand) (rand)))
|
||||
id (:walkmap.id/id p)
|
||||
o (store p)
|
||||
r (retrieve id o)
|
||||
p' (t/tag
|
||||
(assoc r :vertices
|
||||
(conj (:vertices id) (v/vertex (rand) (rand) (rand))))
|
||||
:edited)
|
||||
o' (store p' o)
|
||||
r' (retrieve id o')]
|
||||
(is (not= r r') "The value referenced by `id` should have changed.")
|
||||
(is (= r' p') "The value referenced by `id` in `o'` should be equal to `p'`."))))
|
54
test/cc/journeyman/walkmap/tag_test.clj
Normal file
54
test/cc/journeyman/walkmap/tag_test.clj
Normal file
|
@ -0,0 +1,54 @@
|
|||
(ns cc.journeyman.walkmap.tag-test
|
||||
(:require [clojure.test :refer :all]
|
||||
[cc.journeyman.walkmap.tag :refer [tag tagged? tags untag]]))
|
||||
|
||||
(deftest tag-tests
|
||||
(testing "Tagging"
|
||||
(is (set? (:walkmap.tag/tags (tag {:kind :test-obj} :foo :bar :ban :froboz)))
|
||||
"The value of `:walkmap.tag/tags` should be a set.")
|
||||
(is (= (count (:walkmap.tag/tags (tag {:kind :test-obj} :foo :bar :ban :froboz))) 4)
|
||||
"All the tags passed should be added.")
|
||||
(is (:walkmap.tag/tags (tag {:kind :test-obj} :foo :bar :ban :froboz) :ban)
|
||||
"`:ban` should be present in the set, and, as it is a set, it
|
||||
should be valid to apply it to a keyword.")
|
||||
(is (not ((:walkmap.tag/tags (tag {:kind :test-obj} :foo :bar :ban :froboz)) :cornflakes))
|
||||
"`:cornflakes should not be present.")
|
||||
(is (true? (tagged? (tag {:kind :test-obj} :foo :bar :ban :froboz) :bar))
|
||||
"`tagged?` should return an explicit `true`, not any other value.")
|
||||
(is (tagged? (tag {:kind :test-obj} :foo :bar :ban :froboz) :bar :froboz)
|
||||
"We should be able to test for the presence of more than one tag")
|
||||
(is (false? (tagged? {:kind :test-obj} :foo))
|
||||
"A missing `:walkmap.tag/tags` should not cause an error.")
|
||||
(is (= (tagged? (tag {:kind :test-obj} :foo :bar :ban :froboz) :bar :cornflakes) false)
|
||||
"If any of the queried tags is missing, false should be returned")
|
||||
(is (tagged? (tag (tag {:kind :test-obj} :foo) :bar) :foo :bar)
|
||||
"We should be able to add tags to an already tagged object")
|
||||
(is (false? (tagged? (tag {:kind :test-obj} :foo :bar) :cornflakes))
|
||||
"`tagged?` should return an explicit `false` if a queried tag is missing.")
|
||||
(is (= (tags (tag {:kind :test-obj} :foo)) #{:foo})
|
||||
"`tags` should return the tags on the object, if any.")
|
||||
(is (every? nil? (map #(tags %) [1 :one "one" [:one] {:one 1}]))
|
||||
"Things which don't have tags don't have tags, and that's not a problem.")
|
||||
(let [object (tag {:kind :test-obj} :foo :bar :ban :froboz)]
|
||||
(is (= (untag object :cornflakes) object)
|
||||
"Removing a missing tag should have no effect.")
|
||||
(is (tagged? (untag object :foo) :bar :ban :froboz)
|
||||
"All tags not explicitly removed should still be present.")
|
||||
(is (false? (tagged? (untag object :bar) :bar))
|
||||
"But the tag which has been removed should be removed."))
|
||||
(is (thrown? IllegalArgumentException (tag [] :foo))
|
||||
"An exception should be thrown if `object` is not a map: `tag`.")
|
||||
(is (thrown? IllegalArgumentException (tagged? [] :foo))
|
||||
"An exception should be thrown if `object` is not a map: `tagged?`.")
|
||||
(is (thrown? IllegalArgumentException (untag [] :foo))
|
||||
"An exception should be thrown if `object` is not a map: `untag`.")
|
||||
(is (thrown? IllegalArgumentException (tag {:kind :test-obj} :foo "bar" :ban))
|
||||
"An exception should be thrown if any of `tags` is not a keyword: `tag`.")
|
||||
(is (thrown? IllegalArgumentException (tagged? {:kind :test-obj} :foo "bar" :ban))
|
||||
"An exception should be thrown if any of `tags` is not a keyword: `tagged?`.")
|
||||
(is (thrown? IllegalArgumentException (untag {:kind :test-obj} :foo "bar" :ban))
|
||||
"An exception should be thrown if any of `tags` is not a keywordp: `untag`.")
|
||||
(let [o (tag {:kind :test-obj} :foo '(:bar :ban) :froboz)]
|
||||
(is (tagged? o :ban :bar :foo :froboz)
|
||||
"It's now allowed to include lists of tags in the arg list for `tag`."))))
|
||||
|
100
test/cc/journeyman/walkmap/utils_test.clj
Normal file
100
test/cc/journeyman/walkmap/utils_test.clj
Normal file
|
@ -0,0 +1,100 @@
|
|||
(ns cc.journeyman.walkmap.utils-test
|
||||
(:require [clojure.test :refer :all]
|
||||
[cc.journeyman.walkmap.utils :refer [=ish check-kind-type check-kind-type-seq kind-type truncate]]
|
||||
[cc.journeyman.walkmap.vertex :refer [vertex vertex?]]))
|
||||
|
||||
(deftest =ish-tests
|
||||
(testing "Rough equality"
|
||||
(is (=ish 5.00000001 5.00000002) "Close enough.")
|
||||
(is (=ish 5 5) "Perfect.")
|
||||
(is (not (=ish 5.01 5.02)) "Not close enough.")
|
||||
(is (=ish 22/7 3.142857) "We hope so!")
|
||||
(is (=ish 0 0.0) "Tricky conrer case!")
|
||||
(is (=ish :foo :foo) "Fails over to plain old equals for non-numbers.")
|
||||
(is (=ish 6 5 10000) "If tolerance is wide enough, anything can be equal.")
|
||||
(is (not (=ish "hello" "goodbye" 10000)) "Well, except non-numbers, of course.")))
|
||||
|
||||
(deftest truncate-tests
|
||||
(testing "String truncation"
|
||||
(is (= (truncate "The quick brown fox jumped over the lazy dog" 19)
|
||||
"The quick brown fox")
|
||||
"If it's a sting, and longer than the desired length, it should be
|
||||
truncated.")
|
||||
(is (= (truncate "The quick brown fox jumped over the lazy dog" 100)
|
||||
"The quick brown fox jumped over the lazy dog")
|
||||
"If it's a sting, and shorter than the desired length, it should not be
|
||||
truncated.")
|
||||
(is (= (truncate :the-quick-brown-fox 10) :the-quick-brown-fox)
|
||||
"If it's not a string, it should not be truncated, regardless.")))
|
||||
|
||||
|
||||
(deftest kind-type-tests
|
||||
(testing "Type identification."
|
||||
(is (= (kind-type {:kind :test}) :test)
|
||||
"Maps with a value for `:kind` return that as their kind.")
|
||||
(is (= (kind-type {:dnik :test}) clojure.lang.PersistentArrayMap)
|
||||
"Maps with no value for `:kind` are just maps.")
|
||||
(is (= (kind-type nil) "nil")
|
||||
"As a special case, the kind of `nil` is the string \"nil\".")
|
||||
(is (= (kind-type "Fred") java.lang.String)
|
||||
"The kind-type of anything else is just its Java class.")))
|
||||
|
||||
(deftest check-kind-type-tests
|
||||
(testing "Exception thrown if kind not as expected."
|
||||
(let [v {:kind :test}]
|
||||
(is (= (check-kind-type v :test) v)
|
||||
"If the check passes, the object is returned."))
|
||||
(let [v "test"]
|
||||
(is (= (check-kind-type v java.lang.String) v)
|
||||
"If the check passes, the object is returned."))
|
||||
(let [v "test"]
|
||||
(is (= (check-kind-type v string? java.lang.String) v)
|
||||
"If the check passes, the object is returned."))
|
||||
(let [v (vertex 1 1 1)]
|
||||
(is (= (check-kind-type v :vertex) v)
|
||||
"If the check passes, the object is returned."))
|
||||
(let [v (vertex 1 1 1)]
|
||||
(is (= (check-kind-type v vertex? :vertex) v)
|
||||
"If the check passes, the object is returned."))
|
||||
(let [v "test"]
|
||||
(is (thrown? IllegalArgumentException
|
||||
(check-kind-type v :test))
|
||||
"If the check doesn't pass, an exception is thrown."))
|
||||
(let [v {:kind :test}]
|
||||
(is (thrown? IllegalArgumentException
|
||||
(check-kind-type v vertex? :vertex))
|
||||
"If the check doesn't pass, an exception is thrown."))))
|
||||
|
||||
(deftest check-kind-type-seq-tests
|
||||
(testing "Exception thrown if kind not as expected: sequence variant."
|
||||
(let [v [{:kind :test} {:kind :test}]]
|
||||
(is (= (check-kind-type-seq v :test) v)
|
||||
"If the check passes, the object is returned."))
|
||||
(let [v (list "another" "test")]
|
||||
(is (= (check-kind-type-seq v java.lang.String) v)
|
||||
"If the check passes, the object is returned."))
|
||||
(let [v ["more" "test" "strings"]]
|
||||
(is (= (check-kind-type-seq v string? java.lang.String) v)
|
||||
"If the check passes, the object is returned."))
|
||||
(let [v (list (vertex 1 1 1) (vertex 2 2 2) (vertex 3 3 3))]
|
||||
(is (= (check-kind-type-seq v :vertex) v)
|
||||
"If the check passes, the object is returned."))
|
||||
(let [v (list (vertex 1 1 1))]
|
||||
(is (= (check-kind-type-seq v vertex? :vertex) v)
|
||||
"If the check passes, the object is returned."))
|
||||
(let [v :test]
|
||||
(is (thrown? IllegalArgumentException
|
||||
(check-kind-type-seq v :test))
|
||||
"If the arg isn't a sequence, an exception is thrown."))
|
||||
(let [v (list (vertex 1 1 1) "test" (vertex 3 3 3))]
|
||||
(is (thrown? IllegalArgumentException
|
||||
(check-kind-type-seq v :test))
|
||||
"If the check doesn't pass for any item, an exception is thrown."))
|
||||
(let [v (list (vertex 1 1 1) (vertex 2 2 2) "test")]
|
||||
(is (thrown? IllegalArgumentException
|
||||
(check-kind-type-seq v vertex? :vertex))
|
||||
"If the check doesn't pass, an exception is thrown."))))
|
||||
|
||||
|
||||
|
||||
|
148
test/cc/journeyman/walkmap/vertex_test.clj
Normal file
148
test/cc/journeyman/walkmap/vertex_test.clj
Normal file
|
@ -0,0 +1,148 @@
|
|||
(ns cc.journeyman.walkmap.vertex-test
|
||||
(:require [clojure.test :refer :all]
|
||||
[cc.journeyman.walkmap.utils :refer [=ish kind-type]]
|
||||
[cc.journeyman.walkmap.vertex :refer [canonicalise ensure3d vertex
|
||||
vertex= vertex* vertex?
|
||||
within-box?]]))
|
||||
|
||||
(deftest vertex-equal-tests
|
||||
(testing "Equality of vertices"
|
||||
(is (vertex= (vertex 0 0 0) (vertex 0 0 0))
|
||||
"should be equal")
|
||||
(is (vertex= (vertex 0 0 0) (vertex 0.0000001 0 0))
|
||||
"differences less than one part in a million should be ignored")
|
||||
(is (false? (vertex= (vertex 0 0 0) (vertex 0 0 1)))
|
||||
"should not be equal")
|
||||
(is (thrown? IllegalArgumentException
|
||||
(vertex= (vertex 0 0 0) "Not a vertex"))
|
||||
"Exception should be thrown: not a vertex.")))
|
||||
|
||||
(deftest vertex-multiply-tests
|
||||
(testing "multiplication of vertices"
|
||||
(let [v (vertex (rand) (rand) (rand))
|
||||
u (vertex 1 1 1)
|
||||
v' (vertex* v u)]
|
||||
(is (vertex= v v')
|
||||
"Multiplication by {:x 1 :y 1 :z 1} should not change the vertex"))
|
||||
(let [v (vertex 0.333333 0.25 0.2)
|
||||
d (vertex 3 4 5)
|
||||
v' (vertex* v d)
|
||||
expected (vertex 1 1 1)]
|
||||
(is (vertex= expected v')
|
||||
"Multiplication by values other than {:x 1 :y 1 :z 1} should change
|
||||
the vertex"))
|
||||
(let [v (vertex 0.3333333 0.25 0.2)
|
||||
d (vertex 3 4)
|
||||
v' (vertex* v d)
|
||||
expected (vertex 1 1 0.2)]
|
||||
(is (vertex= expected v')
|
||||
"Multiplication by a 2D vertex should not change `:z`"))
|
||||
(let [v (vertex 0.3333333 0.25)
|
||||
d (vertex 3 4)
|
||||
v' (vertex* v d)
|
||||
expected (vertex 1 1 0)]
|
||||
(is (=ish 0 (:z v'))
|
||||
"Multiplication of a 2D vertex should result in `:z` = zero"))
|
||||
(is (thrown? IllegalArgumentException
|
||||
(vertex* 3 (vertex 0 0 0)))
|
||||
"Exception should be thrown: not a vertex (1st arg).")
|
||||
(is (thrown? IllegalArgumentException
|
||||
(vertex* (vertex 0 0 0) "Not a vertex"))
|
||||
"Exception should be thrown: not a vertex (2nd arg).")))
|
||||
|
||||
(deftest canonicalise-tests
|
||||
(testing "Canonicalisation of vertices."
|
||||
(is (thrown? IllegalArgumentException
|
||||
(canonicalise {:x "3" :y 4}))
|
||||
"Exception should be thrown: not a number (`:x` coord).")
|
||||
(is (thrown? IllegalArgumentException
|
||||
(canonicalise {:x 3 :y :Jam}))
|
||||
"Exception should be thrown: not a number (`:y` coord).")
|
||||
(is (thrown? IllegalArgumentException
|
||||
(canonicalise {:x 3 :y :4 :z {:foo "bar"}}))
|
||||
"Exception should be thrown: not a number (`:z` coord).")
|
||||
(let [v (canonicalise {:x 3 :y 4})]
|
||||
(is
|
||||
(= (:walkmap.id/id v)
|
||||
(keyword (str "vert_" (:x v) "_" (:y v))))
|
||||
"Vertex ids should match the expected pattern.")
|
||||
(is (= (kind-type v) :vertex)
|
||||
"A canonicalised 2d vertex should have the kind `:vertex`.")
|
||||
(is (vertex? v)
|
||||
"A canonicalised 2d vertex should be recognisable as a vertex."))
|
||||
(let [v (canonicalise {:x 3 :y 4 :z 5})]
|
||||
(is
|
||||
(= (:walkmap.id/id v)
|
||||
(keyword (str "vert_" (:x v) "_" (:y v) "_" (:z v))))
|
||||
"Vertex ids should match the expected pattern.")
|
||||
(is (= (kind-type v) :vertex)
|
||||
"A canonicalised 3d vertex should have the kind `:vertex`.")
|
||||
(is (vertex? v)
|
||||
"A canonicalised 3d vertex should be recognisable as a vertex."))))
|
||||
|
||||
(deftest ensure3d-tests
|
||||
(testing "Coercing vertices to three dimensions"
|
||||
(let [v (vertex 2 3)
|
||||
v' (ensure3d v)]
|
||||
(is (zero? (:z v'))
|
||||
"If not already 3d, and no `dflt` arg specified, `:z` should be zero."))
|
||||
(let [v (vertex 2 3)
|
||||
v' (ensure3d v 5)]
|
||||
(is (= (:z v') 5)
|
||||
"If not already 3d, and `dflt` arg specified, `:z` should be
|
||||
equal to `dflt`."))
|
||||
(let [v (vertex 2 3 4)
|
||||
v' (ensure3d v 5)]
|
||||
(is (= v v')
|
||||
"If already 3d, should be unchanged."))))
|
||||
|
||||
(deftest within-box-tests
|
||||
(testing "Checking whether a vertex is within a specified region: 2d."
|
||||
(is (within-box? (vertex 2 2) (vertex 1 1) (vertex 3 3)) "Should be.")
|
||||
(is (within-box? (vertex 1 3) (vertex 1 1) (vertex 3 3)) "Should be.")
|
||||
(is (false? (within-box? (vertex 0 2) (vertex 1 1) (vertex 3 3)))
|
||||
"Outside west")
|
||||
(is (false? (within-box? (vertex 5 2) (vertex 1 1) (vertex 3 3)))
|
||||
"Outside east")
|
||||
(is (false? (within-box? (vertex 2 0) (vertex 1 1) (vertex 3 3)))
|
||||
"Outside south")
|
||||
(is (false? (within-box? (vertex 2 5) (vertex 1 1) (vertex 3 3)))
|
||||
"Outside north")
|
||||
(is (false? (within-box? (vertex 2 3.000001) (vertex 1 1) (vertex 3 3)))
|
||||
"Very slightly outside north"))
|
||||
(testing "Checking whether a vertex is within a specified region: 3d."
|
||||
(is (within-box?
|
||||
(vertex 2 2 2) (vertex 1 1 1) (vertex 3 3 3)) "Should be.")
|
||||
(is (within-box?
|
||||
(vertex 1 3 3) (vertex 1 1 1) (vertex 3 3 3)) "Should be.")
|
||||
(is (false?
|
||||
(within-box? (vertex 0 2 2) (vertex 1 1 1) (vertex 3 3 3)))
|
||||
"Outside west")
|
||||
(is (false?
|
||||
(within-box? (vertex 5 2 2) (vertex 1 1 1) (vertex 3 3 3)))
|
||||
"Outside east")
|
||||
(is (false?
|
||||
(within-box? (vertex 2 0 2) (vertex 1 1 1) (vertex 3 3 3)))
|
||||
"Outside south")
|
||||
(is (false?
|
||||
(within-box? (vertex 2 5 2) (vertex 1 1 1) (vertex 3 3 3)))
|
||||
"Outside north")
|
||||
(is (false?
|
||||
(within-box? (vertex 2 0 2) (vertex 1 1 1) (vertex 3 3 3)))
|
||||
"Outside south")
|
||||
(is (false?
|
||||
(within-box? (vertex 2 2 0) (vertex 1 1 1) (vertex 3 3 3)))
|
||||
"Outside down")
|
||||
(is (false?
|
||||
(within-box? (vertex 2 2 5) (vertex 1 1 1) (vertex 3 3 3)))
|
||||
"Outside up"))
|
||||
(testing "Bad arguments."
|
||||
(is (thrown? IllegalArgumentException
|
||||
(within-box? :fred (vertex 1 1 1) (vertex 3 3 3)))
|
||||
"Not a vertex: `target`.")
|
||||
(is (thrown? IllegalArgumentException
|
||||
(within-box? (vertex 2 2 2) :ginny (vertex 3 3 3)))
|
||||
"Not a vertex: `minv`.")
|
||||
(is (thrown? IllegalArgumentException
|
||||
(within-box? (vertex 2 2 2) (vertex 1 1 1) :henry))
|
||||
"Not a vertex: `maxv`.")))
|
|
@ -1,7 +0,0 @@
|
|||
(ns walkmap.core-test
|
||||
(:require [clojure.test :refer :all]
|
||||
[walkmap.core :refer :all]))
|
||||
|
||||
;; (deftest a-test
|
||||
;; (testing "FIXME, I fail."
|
||||
;; (is (= 0 1))))
|
|
@ -1,46 +0,0 @@
|
|||
(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!")))
|
Loading…
Reference in a new issue