Skip to content

Commit

Permalink
[voxel] update SVO & isosurface ns to current core API, add example
Browse files Browse the repository at this point in the history
  • Loading branch information
postspectacular committed Mar 25, 2015
1 parent d380b18 commit f732d61
Show file tree
Hide file tree
Showing 5 changed files with 466 additions and 232 deletions.
65 changes: 65 additions & 0 deletions geom-voxel/src/examples.org
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#+SETUPFILE: setup.org

* geom.voxels examples

#+BEGIN_SRC clojure :tangle ../babel/examples/example01.clj :mkdirp yes :padline no
(ns example01
(:require
[thi.ng.geom.core :as g]
[thi.ng.geom.core.vector :refer [vec3]]
[thi.ng.geom.voxel.svo :as svo]
[thi.ng.geom.voxel.isosurface :as iso]
[thi.ng.geom.mesh.io :as mio]
[thi.ng.common.math.core :as m]
[clojure.java.io :as io]))

(def res (double 1/8))
(def wall 0.25)

(defn gyroid ^double [s t p]
"Evaluates gyroid function at scaled point `p`."
(let [[x y z] (g/* p s)]
(- (m/abs
(+ (* (Math/cos x) (Math/sin z))
(* (Math/cos y) (Math/sin x))
(* (Math/cos z) (Math/sin y))))
t)))

(def delete-voxels #(first (svo/delete-at % %2)))

(defn voxel-box
[tree op flt range]
(->> (for [x range y range, z range] (vec3 x y z))
(filter flt)
(svo/apply-voxels op tree)))

(time
(def v
(reduce
(fn [tree [op r]] (voxel-box tree op identity r))
(svo/voxeltree 32 res)
[[svo/set-at (range 10 20 res)] [svo/set-at (range 15 25 res)]])))

(time
(def v2
(reduce
(fn [tree [op r]] (voxel-box tree op identity r))
v [[delete-voxels (range (+ 10 wall) (- 20 wall) res)]
[delete-voxels (range (+ 15 wall) (- 25 wall) res)]])))

(time
(def v3
(reduce
(fn [tree [op r]]
(voxel-box tree op #(m/in-range? 0.0 10.0 (gyroid 1.0 1.2 %)) r))
v2 [[svo/set-at (range (+ 10 wall) (- 20 wall) res)]
[svo/set-at (range (+ 15 wall) (- 25 wall) res)]])))

(time
(def v4
(reduce
(fn [tree [f rx ry rz]] (svo/apply-voxels f tree (for [x rx y ry z rz] (vec3 x y z))))
v3 [[delete-voxels (range 9 26 res) (range 9 26 res) (range 18 26 res)]])))

(time (with-open [o (io/output-stream "foo.stl")] (mio/write-stl o (g/tessellate (iso/surface-mesh v4 11 0.5)))))
#+END_SRC
126 changes: 126 additions & 0 deletions geom-voxel/src/index.org
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
#+SETUPFILE: ../../src/setup.org
#+TITLE: thi.ng/geom-voxel

* About the project

** Overview

This is a sub-module of [[file:../../src/index.org][thi.ng/geom]] and is in early stage development.
The module is providing an early, experimental, not very efficient
implementation of a sparse voxel octree (SVO) and functions to compute
an isosurface mesh from an SVO.

** Status

ALPHA quality, in active development.

* Namespaces

- [[./svo.org][thi.ng.geom.voxel.svo]]
- [[./isosurface.org][thi.ng.geom.voxel.isosurface]]
- [[./examples.org][examples]]

* Tests

* Module definition

** Building & testing this project

Please see the [[file:../../src/index.org][parent project]] for further information.

** Injected properties :noexport:

This template uses shared project configuration defined in [[../../src/config.org][config.org]].
Module specific settings are defined below:

#+BEGIN_SRC clojure :exports none :noweb-ref project-name
thi.ng/geom-voxel
#+END_SRC

#+BEGIN_SRC clojure :exports none :noweb yes :noweb-ref cljs-artefact-path
target/geom-voxel-<<conf-version()>>.js
#+END_SRC

** Leiningen project file :noexport:

#+BEGIN_SRC clojure :tangle ../babel/project.clj :noweb yes :mkdirp yes :padline no
(defproject <<project-name>> "<<conf-version()>>"
:description "thi.ng geometry kit - voxel module"
:url "<<conf-project-url>>"
:license {:name "Apache Software License"
:url "http://www.apache.org/licenses/LICENSE-2.0"
:distribution :repo}
:scm {:name "git"
:url "<<conf-project-url>>"}

:min-lein-version "2.4.0"

:dependencies [<<dep-clj>>
[thi.ng/geom-core "<<conf-version()>>"]
[thi.ng/geom-types "<<conf-version()>>"]
[thi.ng/geom-meshops "<<conf-version()>>"]]

:source-paths ["src/cljx"]
:test-paths ["<<conf-gen-test-path>>"]

:profiles {:dev {:dependencies [<<dep-cljs>>
<<dep-criterium>>]
:plugins [<<dep-cljx>>
<<dep-cljsbuild>>
<<dep-cljs-test>>]
:global-vars {*warn-on-reflection* true}
:jvm-opts ^:replace []
:auto-clean false
:prep-tasks [["cljx" "once"]]
:aliases {"cleantest" ["do" "clean," "cljx" "once," "test," "cljsbuild" "test"]}}}

:cljx {:builds [{:source-paths ["src/cljx"]
:output-path "<<conf-gen-source-path>>"
:rules :clj}
{:source-paths ["src/cljx"]
:output-path "<<conf-gen-source-path>>"
:rules :cljs}
{:source-paths ["test/cljx"]
:output-path "<<conf-gen-test-path>>"
:rules :clj}
{:source-paths ["test/cljx"]
:output-path "<<conf-gen-test-path>>"
:rules :cljs}]}

:cljsbuild {:builds [{:source-paths ["<<conf-gen-source-path>>" "<<conf-gen-test-path>>"]
:id "simple"
:compiler {:output-to "<<cljs-artefact-path>>"
:optimizations :whitespace
:pretty-print true}}]
:test-commands {"unit-tests" ["phantomjs" :runner "<<cljs-artefact-path>>"]}}

:pom-addition [:developers [:developer
[:name "Karsten Schmidt"]
[:url "http://postspectacular.com"]
[:timezone "0"]]])
#+END_SRC

** Accessing library version during runtime

The autogenerated namespace =thi.ng.geom.voxel.version= contains a single
symbol =version= holding the version string defined above:

#+BEGIN_SRC clojure :noweb yes
(use 'thi.ng.geom.voxel.version)

(prn version)
; "<<conf-version()>>"
#+END_SRC

*** Version namespace :noexport:

#+BEGIN_SRC clojure :tangle ../babel/src/cljx/thi/ng/geom/voxel/version.cljx :noweb yes :mkdirp yes :padline no
(ns thi.ng.geom.voxel.version)
(def version "<<conf-version()>>")
#+END_SRC

** Contributors

| *Name* | *Role* | *Website* |
| [[mailto:[email protected]][Karsten Schmidt]] | initiator & principal developer | [[http://postspectacular.com][postspectacular.com]] |
| | | [[http://thi.ng][thi.ng]] |
132 changes: 85 additions & 47 deletions geom-voxel/src/isosurface.org
Original file line number Diff line number Diff line change
@@ -1,27 +1,32 @@
#+SETUPFILE: setup.org

* Contents :toc_3_gh:
- [[#namespace-thinggeomisosurface][Namespace: thi.ng.geom.isosurface]]
- [[#marching-cube-lookup-tables][Marching cube lookup tables]]
- [[#edge-offset-table][Edge offset table]]
- [[#triangles-per-cube-index][Triangles per cube index]]
- [[#edges-per-cube-index][Edges per cube index]]
- [[#helper-functions][Helper functions]]
- [[#voxel-bit-test-macro][Voxel bit test macro]]
- [[#voxel-lookups][Voxel lookups]]
- [[#isosurface-computation][Isosurface computation]]
- [[#namespace-declaration][Namespace declaration]]

* Namespace: thi.ng.geom.isosurface
** Namespace declaration
#+BEGIN_SRC clojure :tangle ../babel/src/cljx/thi/ng/geom/isosurface.cljx :mkdirp yes :padline no
(ns thi.ng.geom.isosurface
(:require
[thi.ng.common.math.core :as m]
[thi.ng.geom.core :as g :refer [vec3]]
[thi.ng.geom.svo :as svo :refer [cell-index select-cells voxel-cell voxel-config-at-depth]]
[thi.ng.geom.mesh :as mesh]
,#+clj [thi.ng.geom.macros.voxel :as mac])
,#+cljs (:require-macros [thi.ng.geom.macros.voxel :as mac]))
#+END_SRC

** Marching cube lookup tables
*** Edge offset table
#+BEGIN_SRC clojure :tangle ../babel/src/cljx/thi/ng/geom/isosurface.cljx

#+BEGIN_SRC clojure :noweb-ref lut
(def edge-offsets
[[0 0 0 0] [1 0 0 2] [0 0 1 0] [0 0 0 2]
[0 1 0 0] [1 1 0 2] [0 1 1 0] [0 1 0 2]
[0 0 0 1] [1 0 0 1] [1 0 1 1] [0 0 1 1]])
#+END_SRC

*** Triangles per cube index
#+BEGIN_SRC clojure :tangle ../babel/src/cljx/thi/ng/geom/isosurface.cljx

#+BEGIN_SRC clojure :noweb-ref lut
(def cell-triangles
[[]
[[0 8 3]]
Expand Down Expand Up @@ -280,8 +285,10 @@
[[0 3 8]]
[]])
#+END_SRC

*** Edges per cube index
#+BEGIN_SRC clojure :tangle ../babel/src/cljx/thi/ng/geom/isosurface.cljx

#+BEGIN_SRC clojure :noweb-ref lut
(def compute-edges
[0 7 1 6 0 7 1 6 4 3 5 2 4 3 5 2
2 5 3 4 2 5 3 4 6 1 7 0 6 1 7 0
Expand All @@ -300,36 +307,48 @@
0 7 1 6 0 7 1 6 4 3 5 2 4 3 5 2
2 5 3 4 2 5 3 4 6 1 7 0 6 1 7 0])
#+END_SRC

** Helper functions
*** Voxel bit test macro

#+BEGIN_SRC clojure :tangle ../babel/src/cljx/thi/ng/geom/voxel/macros.clj :noweb yes :mkdirp yes :padline no
(ns thi.ng.geom.voxel.macros)

(defmacro set-bit-if-index
[voxels idx mask id]
`(if (~voxels ~idx) ~id (bit-or ~id ~mask)))
#+END_SRC

*** Voxel lookups
#+BEGIN_SRC clojure :tangle ../babel/src/cljx/thi/ng/geom/isosurface.cljx

#+BEGIN_SRC clojure :noweb-ref helpers
(defn voxel-id-front
[voxels {:keys [stride stride-z]} idx]
(let [y2 (+ idx stride) z2 (+ idx stride-z) yz (+ z2 stride)
idx1 (inc idx) y21 (inc y2) z21 (inc z2) yz1 (inc yz)]
(->> 0
(mac/set-bit-if-index voxels idx 0x01)
(mac/set-bit-if-index voxels idx1 0x02)
(mac/set-bit-if-index voxels z21 0x04)
(mac/set-bit-if-index voxels z2 0x08)
(mac/set-bit-if-index voxels y2 0x10)
(mac/set-bit-if-index voxels y21 0x20)
(mac/set-bit-if-index voxels yz1 0x40)
(mac/set-bit-if-index voxels yz 0x80))))
(set-bit-if-index voxels idx 0x01)
(set-bit-if-index voxels idx1 0x02)
(set-bit-if-index voxels z21 0x04)
(set-bit-if-index voxels z2 0x08)
(set-bit-if-index voxels y2 0x10)
(set-bit-if-index voxels y21 0x20)
(set-bit-if-index voxels yz1 0x40)
(set-bit-if-index voxels yz 0x80))))

(defn voxel-id-back
[voxels {:keys [stride stride-z]} idx]
(let [y2 (- idx stride) z2 (- idx stride-z) yz (- z2 stride)
idx1 (dec idx) y21 (dec y2) z21 (dec z2) yz1 (dec yz)]
(->> 0
(mac/set-bit-if-index voxels idx 0x01)
(mac/set-bit-if-index voxels idx1 0x02)
(mac/set-bit-if-index voxels z21 0x04)
(mac/set-bit-if-index voxels z2 0x08)
(mac/set-bit-if-index voxels y2 0x10)
(mac/set-bit-if-index voxels y21 0x20)
(mac/set-bit-if-index voxels yz1 0x40)
(mac/set-bit-if-index voxels yz 0x80))))
(set-bit-if-index voxels idx 0x01)
(set-bit-if-index voxels idx1 0x02)
(set-bit-if-index voxels z21 0x04)
(set-bit-if-index voxels z2 0x08)
(set-bit-if-index voxels y2 0x10)
(set-bit-if-index voxels y21 0x20)
(set-bit-if-index voxels yz1 0x40)
(set-bit-if-index voxels yz 0x80))))

(defn boundary-voxels
[config cells]
Expand All @@ -352,8 +371,10 @@
:when (and (pos? id) (< id 0xff))]
[id (* 3 idx) idx (voxel-cell config idx)]))
#+END_SRC

** Isosurface computation
#+BEGIN_SRC clojure :tangle ../babel/src/cljx/thi/ng/geom/isosurface.cljx

#+BEGIN_SRC clojure :noweb-ref isosurf
(defn surface-mesh
"Computes a triangle mesh of a voxel tree's iso surface
at the given tree depth and iso value (between 0.0 ... 1.0)"
Expand Down Expand Up @@ -391,19 +412,36 @@
(assoc! vertices vid2 (vec3 x y (if (zero? (bit-and voxel-id 0x08)) (+ z delta) (- z delta))))
vertices))
vertices)))
(transient {}) cells))
_ (prn "creating mesh...")
m (mesh/mesh3)]
(mesh/commit-edit
m
(reduce
(fn [mesh [vid eid]]
(reduce
(fn [m t]
(mesh/add-face! m
[(vertices (+ eid (indexed-eo (t 0))))
(vertices (+ eid (indexed-eo (t 2))))
(vertices (+ eid (indexed-eo (t 1))))]))
mesh (cell-triangles vid)))
(mesh/begin-edit m) cells))))
(transient {}) cells))]
(prn "creating mesh...")
(->> cells
(mapcat
(fn [[vid eid]]
(map
(fn [t]
[(vertices (+ eid (indexed-eo (t 0))))
(vertices (+ eid (indexed-eo (t 2))))
(vertices (+ eid (indexed-eo (t 1))))])
(cell-triangles vid))))
(g/into (bm/basic-mesh)))))
#+END_SRC

** Namespace declaration

#+BEGIN_SRC clojure :tangle ../babel/src/cljx/thi/ng/geom/voxel/isosurface.cljx :noweb yes :mkdirp yes :padline no
(ns thi.ng.geom.voxel.isosurface
,#+cljs (:require-macros [thi.ng.geom.voxel.macros :refer [set-bit-if-index]])
(:require
[thi.ng.geom.core :as g]
[thi.ng.geom.core.vector :as v :refer [vec3]]
[thi.ng.geom.basicmesh :as bm]
[thi.ng.geom.voxel.svo :as svo :refer [cell-index select-cells voxel-cell voxel-config-at-depth]]
[thi.ng.common.math.core :as m]
,#+clj [thi.ng.geom.voxel.macros :refer [set-bit-if-index]]))

<<lut>>

<<helpers>>

<<isosurf>>
#+END_SRC
Loading

0 comments on commit f732d61

Please sign in to comment.