#3: Massive changes, superstructure now kind-of works
More unit tests now, and they all pass. More work needed, but this is very promising.
This commit is contained in:
parent
f49a7495db
commit
f616992191
33 changed files with 1768 additions and 672 deletions
|
|
@ -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"
|
||||
|
|
@ -13,7 +13,15 @@
|
|||
(and
|
||||
(seq? v)
|
||||
(> (count v) 2)
|
||||
(every? vertex? v))))
|
||||
(every? vertex? v)
|
||||
(or (nil? (:kind o)) (= (:kind o) :path)))))
|
||||
|
||||
(defn make-path
|
||||
[nodes]
|
||||
(if
|
||||
(every? vertex? nodes)
|
||||
{:nodes nodes :id (keyword (gensym "path")) :kind :path}
|
||||
(throw (Exception. "Each item on path must be a vertex."))))
|
||||
|
||||
(defn polygon->path
|
||||
"If `o` is a polygon, return an equivalent path. What's different about
|
||||
|
|
@ -25,6 +33,6 @@
|
|||
[o]
|
||||
(if
|
||||
(polygon? o)
|
||||
(assoc (dissoc o :vertices) :nodes (concat (:vertices o) (list (first (:vertices o)))))
|
||||
(assoc (dissoc o :vertices) :kind :path :nodes (concat (:vertices o) (list (first (:vertices o)))))
|
||||
(throw (Exception. "Not a polygon!"))))
|
||||
|
||||
|
|
|
|||
|
|
@ -10,8 +10,9 @@
|
|||
(let
|
||||
[v (:vertices o)]
|
||||
(and
|
||||
(seq? v)
|
||||
(coll? v)
|
||||
(> (count v) 2)
|
||||
(every? vertex? v))))
|
||||
(every? vertex? v)
|
||||
(or (nil? (:kind o)) (= (:kind o) :polygon)))))
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@
|
|||
[me.raynes.fs :as fs]
|
||||
[org.clojars.smee.binary.core :as b]
|
||||
[taoensso.timbre :as l :refer [info error spy]]
|
||||
[walkmap.polygon :refer [polygon?]])
|
||||
[walkmap.polygon :refer [polygon?]]
|
||||
[walkmap.vertex :refer [vertex-key]])
|
||||
(:import org.clojars.smee.binary.core.BinaryIO
|
||||
java.io.DataInput))
|
||||
|
||||
|
|
@ -26,6 +27,7 @@
|
|||
(every? polygon? (: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
|
||||
|
|
@ -49,6 +51,26 @@
|
|||
:count :uint-le
|
||||
:facets (b/repeated facet)))
|
||||
|
||||
(defn canonicalise
|
||||
"Objects read in from STL won't have all the keys/values we need them to have."
|
||||
[o]
|
||||
(cond
|
||||
(and (coll? o) (not (map? o))) (map canonicalise o)
|
||||
;; if it has :facets it's an STL structure, but it doesn't yet conform to `stl?`
|
||||
(:facets o) (assoc o
|
||||
:kind :stl
|
||||
:id (or (:id o) (keyword (gensym "stl")))
|
||||
:facets (canonicalise (:facets o)))
|
||||
;; if it has :vertices it's a polygon, but it doesn't yet conform to `polygon?`
|
||||
(:vertices o) (assoc o
|
||||
:id (or (:id o) (keyword (gensym "poly")))
|
||||
:kind :polygon
|
||||
:vertices (canonicalise (:vertices o)))
|
||||
;; if it has a value for :x it's a vertex, but it doesn't yet conform to `vertex?`
|
||||
(:x o) (assoc o :kind :vertex :id (or (:id o) (vertex-key o)))
|
||||
;; 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.
|
||||
|
|
@ -57,7 +79,7 @@
|
|||
data, if it is not this will run but will return garbage."
|
||||
[filename]
|
||||
(let [in (io/input-stream filename)]
|
||||
(b/decode binary-stl in)))
|
||||
(canonicalise (b/decode binary-stl in))))
|
||||
|
||||
(defn- vect->str [prefix v]
|
||||
(str prefix " " (:x v) " " (:y v) " " (:z v) "\n"))
|
||||
|
|
|
|||
76
src/walkmap/superstructure.clj
Normal file
76
src/walkmap/superstructure.clj
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
(ns walkmap.superstructure
|
||||
"single indexing structure for walkmap objects"
|
||||
(:require [walkmap.path :as p]
|
||||
[walkmap.polygon :as q]
|
||||
[walkmap.stl :as s]
|
||||
[walkmap.utils :as u]
|
||||
[walkmap.vertex :as v]))
|
||||
|
||||
(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 `:id`;
|
||||
4. `v` is not a vertex."
|
||||
;; two copies of the same vertex are not identical enough to one another
|
||||
;; to be used as keys in a map. So our vertices need to have ids, and we need
|
||||
;; to key the vertex-index by vertex ids.
|
||||
;; TODO: BUT WE CANNOT USE GENSYMED ids, because two vertices with the same
|
||||
;; vertices must have the same id!
|
||||
[s o v]
|
||||
(if-not (v/vertex? o)
|
||||
(if (:id o)
|
||||
(if (v/vertex? v)
|
||||
(let [vi (or (:vertex-index s) {})
|
||||
current (or (vi (:id v)) {})]
|
||||
;; deep-merge doesn't merge sets, only maps; so at this
|
||||
;; stage we need to build a map.
|
||||
(assoc vi (:id v) (assoc current (:id o) (:id v))))
|
||||
(throw (Exception. "Not a vertex: " v)))
|
||||
(throw (Exception. (subs (str "No `:id` value: " o) 0 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 `:id`."
|
||||
[s o]
|
||||
(assoc
|
||||
s
|
||||
:vertex-index
|
||||
(reduce
|
||||
u/deep-merge
|
||||
(map
|
||||
#(index-vertex s o %)
|
||||
(u/vertices o)))))
|
||||
|
||||
(defn add-to-superstructure
|
||||
"Return a superstructure like `s` with object `o` added. If `o` is a collection,
|
||||
return a superstructure like `s` with each element of `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 map, or a sequence of maps."
|
||||
([o]
|
||||
(add-to-superstructure {} o))
|
||||
([s o]
|
||||
(cond
|
||||
(map? o) (let [o' (if (:id o) o (assoc o :id (keyword (gensym "obj"))))]
|
||||
(index-vertices (assoc s (:id o') o') o'))
|
||||
(coll? o) (reduce u/deep-merge (map #(add-to-superstructure s %) o))
|
||||
(nil? o) o
|
||||
:else
|
||||
(throw (Exception. (str "Don't know how to index " (or (type o) "nil")))))))
|
||||
|
||||
(:vertex-index (add-to-superstructure (:facets (s/decode-binary-stl "resources/isle_of_man.stl"))))
|
||||
(s/decode-binary-stl "resources/isle_of_man.stl")
|
||||
26
src/walkmap/utils.clj
Normal file
26
src/walkmap/utils.clj
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
(ns walkmap.utils
|
||||
"Miscellaneous utility functions."
|
||||
(:require [walkmap.path :as p]
|
||||
[walkmap.polygon :as q]
|
||||
[walkmap.vertex :as v]))
|
||||
|
||||
(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)
|
||||
(apply merge-with deep-merge vals)
|
||||
(last vals)))
|
||||
|
||||
(defn vertices
|
||||
"If `o` is an object with vertices, return those vertices, else nil."
|
||||
;; TODO: it's possibly a design mistake that I'm currently distinguishing
|
||||
;; between polygons and paths on the basis that one has `:vertices` and
|
||||
;; the other has `:nodes`. Possibly it would be better to have a key
|
||||
;; `:closed` which was `true` for polygons, `false` (or missing) for
|
||||
;; paths.
|
||||
[o]
|
||||
(cond
|
||||
(v/vertex? o) (list o)
|
||||
(q/polygon? o) (:vertices o)
|
||||
(p/path? o) (:nodes o)))
|
||||
|
|
@ -1,6 +1,16 @@
|
|||
(ns walkmap.vertex
|
||||
"Essentially the specification for things we shall consider to be vertices.")
|
||||
|
||||
(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`."
|
||||
[o]
|
||||
(cond
|
||||
(and (:x o) (:y o) (:z o)) (keyword (str "vert{" (:x o) "|" (:y o) "|" (:z o) "}"))
|
||||
(and (:x o) (:y o)) (keyword (str "vert{" (:x o) "|" (:y o) "}"))
|
||||
:else (throw (Exception. "Not a vertex."))))
|
||||
|
||||
(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
|
||||
|
|
@ -13,9 +23,22 @@
|
|||
[o]
|
||||
(and
|
||||
(map? o)
|
||||
(:id o)
|
||||
(number? (:x o))
|
||||
(number? (:y o))
|
||||
(or (nil? (:z o)) (number? (:z o)))))
|
||||
(or (nil? (:z o)) (number? (:z o)))
|
||||
(or (nil? (:kind o)) (= (:kind o) :vertex))))
|
||||
|
||||
(defn make-vertex
|
||||
"Make a vertex with this `x`, `y` and (if provided) `z` values. Returns a map
|
||||
with those values, plus a unique `:id` value, and `:kind` set to `:vertex`.
|
||||
It's not necessary to use this function to create a vertex, but the `:id`
|
||||
must be present and must be unique."
|
||||
([x y]
|
||||
(let [v {:x x :y y :kind :vertex}]
|
||||
(assoc v :id (vertex-key v))))
|
||||
([x y z]
|
||||
(assoc (make-vertex x y) :z z)))
|
||||
|
||||
(def ensure3d
|
||||
"Given a vertex `o`, if `o` has a `:z` value, just return `o`; otherwise
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue