Skip to content

Commit 091e182

Browse files
committed
wip dual contours
1 parent 70bc91c commit 091e182

File tree

4 files changed

+126
-1
lines changed

4 files changed

+126
-1
lines changed

Project.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@ name = "Meshing"
22
uuid = "e6723b4c-ebff-59f1-b4b7-d97aa5274f73"
33

44
[deps]
5+
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
56
GeometryTypes = "4d00f742-c7ba-57c2-abde-4428a4b178cb"
7+
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
8+
Roots = "f2b01f46-fcfa-551c-844a-d8ac1e96c665"
9+
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
610

711
[extras]
812
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"

src/Meshing.jl

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,22 @@
11
module Meshing
22

33
using GeometryTypes
4+
using Roots
5+
using ForwardDiff
6+
using LinearAlgebra
7+
using StaticArrays
48

59
abstract type AbstractMeshingAlgorithm end
610

711
include("marching_tetrahedra.jl")
812
include("marching_cubes.jl")
913
include("surface_nets.jl")
14+
include("dual_contours.jl")
1015

1116
export marching_cubes,
1217
MarchingCubes,
1318
MarchingTetrahedra,
14-
NaiveSurfaceNets
19+
NaiveSurfaceNets,
20+
DualContours
1521

1622
end # module

src/dual_contours.jl

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
#Cardinal directions
2+
const dirs = ( Point(1,0,0), Point(0,1,0), Point(0,0,1) )
3+
4+
#Vertices of cube
5+
const cube_verts = (Point(0, 0, 0), Point(0, 0, 1), Point(0, 1, 0),
6+
Point(0, 1, 1), Point(1, 0, 0), Point(1, 0, 1),
7+
Point(1, 1, 0), Point(1, 1, 1))
8+
9+
10+
#Edges of cube
11+
const cube_edges_dc = ((0, 1), (0, 2), (0, 1), (0, 4), (0, 2), (0, 4), (2, 3), (1, 3),
12+
(4, 5), (1, 5), (4, 6), (2, 6), (4, 5), (4, 6), (2, 3), (2, 6),
13+
(1, 3), (1, 5), (6, 7), (5, 7), (6, 7), (3, 7), (5, 7), (3, 7))
14+
15+
16+
#Use non-linear root finding to compute intersection point
17+
function estimate_hermite(f, v0, v1)
18+
l(t) = f((1.0-t)*v0 + t*v1)
19+
dl(t) = ForwardDiff.derivative(l,t)
20+
t0 = find_zero((l,dl),(0, 1))
21+
x0 = (1.0-t0)*v0 + t0*v1
22+
return (x0, ForwardDiff.gradient(f,x0))
23+
end
24+
25+
#Input:
26+
# f = implicit function
27+
# df = gradient of f
28+
# nc = resolution
29+
function dual_contours(f, bounds::HyperRectangle, nc::NTuple{3,Int})
30+
31+
orig = origin(bounds)
32+
width = widths(bounds)
33+
scale = width ./ Point(nc)
34+
#Compute vertices
35+
dc_verts = []
36+
vindex = Dict()
37+
for x in 0:nc[1], y in 0:nc[2], z in 0:nc[3]
38+
idx = Point(x,y,z)
39+
o = Point(x,y,z) .* scale + orig
40+
41+
#Get signs for cube
42+
cube_signs = [ f(o+(v.*scale))>0 for v in cube_verts ]
43+
44+
if all(cube_signs) || !any(cube_signs)
45+
continue
46+
end
47+
48+
#Estimate hermite data
49+
h_data = [ estimate_hermite(f, o+cube_verts[e[1]+1].*scale, o+cube_verts[e[2]+1].*scale)
50+
for e in cube_edges_dc if cube_signs[e[1]+1] != cube_signs[e[2]+1] ]
51+
52+
#Solve qef to get vertex
53+
A = Array{Float64,2}(undef,length(h_data),3)
54+
for i in eachindex(h_data)
55+
A[i,:] = h_data[i][2]
56+
end
57+
b = [ dot(d[1],d[2]) for d in h_data ]
58+
v = A\b
59+
60+
#Throw out failed solutions
61+
if norm(v-o) > 2
62+
continue
63+
end
64+
65+
#Emit one vertex per every cube that crosses
66+
push!(vindex, idx => length(dc_verts))
67+
push!(dc_verts, (v, ForwardDiff.gradient(f,v)))
68+
end
69+
70+
#Construct faces
71+
dc_faces = Face[]
72+
for x in 0:nc[1], y in 0:nc[2], z in 0:nc[3]
73+
74+
idx = Point(x,y,z)
75+
if !haskey(vindex,idx)
76+
continue
77+
end
78+
79+
#Emit one face per each edge that crosses
80+
o = Point(x,y,z) .* scale + orig
81+
for i in (1,2,3)
82+
for j in 1:i
83+
if haskey(vindex,idx+dirs[i]) && haskey(vindex,idx + dirs[j]) && haskey(vindex,idx + dirs[i] + dirs[j])
84+
# determine orientation of the face from the true normal
85+
v1, tn1 = dc_verts[vindex[idx]+1]
86+
v2, tn2 = dc_verts[vindex[idx+dirs[i]]+1]
87+
v3, tn3 = dc_verts[vindex[idx+dirs[j]]+1]
88+
89+
e1 = v1-v2
90+
e2 = v1-v3
91+
c = cross(e1,e2)
92+
if dot(c, tn1) > 0
93+
push!(dc_faces, Face{3,Int}(vindex[idx]+1, vindex[idx+dirs[i]]+1, vindex[idx+dirs[j]]+1) )
94+
push!(dc_faces, Face{3,Int}(vindex[idx+dirs[i]+dirs[j]]+1, vindex[idx+dirs[j]]+1, vindex[idx+dirs[i]]+1) )
95+
else
96+
push!(dc_faces, Face{3,Int}(vindex[idx]+1, vindex[idx+dirs[j]]+1, vindex[idx+dirs[i]]+1) )
97+
push!(dc_faces, Face{3,Int}(vindex[idx+dirs[i]+dirs[j]]+1, vindex[idx+dirs[i]]+1, vindex[idx+dirs[j]]+1) )
98+
end
99+
end
100+
end
101+
end
102+
103+
end
104+
return HomogenousMesh([Point(v[1]...) for v in dc_verts], dc_faces)
105+
end
106+

test/runtests.jl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,15 @@ using LinearAlgebra: dot, norm
141141
@test minimum(vertices(mesh)) [-0.5, -0.5, -0.5] atol=0.2
142142
end
143143

144+
@testset "Dual Contours" begin
145+
146+
sphere(v) = sqrt(sum(dot(v,v))) - 1 # sphere
147+
148+
m = Meshing.dual_contours(sphere, HyperRectangle(Vec(-1,-1,-1.),Vec(2,2,2.)), (50,50,50))
149+
@test length(vertices(m)) == 11754
150+
@test length(faces(m)) == 51186
151+
end
152+
144153
@testset "AbstractMeshingAlgorithm interface" begin
145154
f = x -> norm(x) - 0.5
146155
bounds = HyperRectangle(Vec(-1, -1, -1), Vec(2, 2, 2))

0 commit comments

Comments
 (0)