Skip to content

Commit 4edea0e

Browse files
committed
relax typing in marchingTetrahedra so that isovalue can be a different type
1 parent 6d149df commit 4edea0e

File tree

3 files changed

+72
-21
lines changed

3 files changed

+72
-21
lines changed

src/marching_tetrahedra.jl

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ end
124124
Checks if a voxel has faces. Should be false for most voxels.
125125
This function should be made as fast as possible.
126126
"""
127-
function hasFaces(vals::Vector{T}, iso::T) where T<:Real
127+
function hasFaces(vals::Vector{<:Real}, iso::Real)
128128
@inbounds v = vals[1]
129129
if v < iso
130130
@inbounds for i=2:8
@@ -141,7 +141,7 @@ end
141141
"""
142142
Determines which case in the triangle table we are dealing with
143143
"""
144-
function tetIx(tIx::IType, vals::Vector{T}, iso::T, vxidx::VoxelIndices{IType}) where {T<:Real, IType <: Integer}
144+
function tetIx(tIx::IType, vals::Vector{<:Real}, iso::Real, vxidx::VoxelIndices{IType}) where {IType <: Integer}
145145
@inbounds v1 = vals[vxidx.subTets[tIx][1]]
146146
@inbounds v2 = vals[vxidx.subTets[tIx][2]]
147147
@inbounds v3 = vals[vxidx.subTets[tIx][3]]
@@ -160,7 +160,7 @@ regardless of which of its neighboring voxels is asking for it) in order
160160
for vertex sharing to be implemented properly.
161161
"""
162162
function vertId(e::IType, x::IType, y::IType, z::IType,
163-
nx::IType, ny::IType, vxidx::VoxelIndices{IType}) where IType <: Integer
163+
nx::IType, ny::IType, vxidx::VoxelIndices{IType}) where {IType <: Integer}
164164
@inbounds dx, dy, dz = vxidx.voxCrnrPos[vxidx.voxEdgeCrnrs[e][1]]
165165
vxidx.voxEdgeDir[e]+7*(x-1+dx+nx*(y-1+dy+ny*(z-1+dz)))
166166
end
@@ -172,18 +172,17 @@ eps represents the "bump" factor to keep vertices away from voxel
172172
corners (thereby preventing degeneracies).
173173
"""
174174
function vertPos(e::IType, x::IType, y::IType, z::IType,
175-
vals::Vector{T}, iso::T, eps::T, vxidx::VoxelIndices{IType}) where {T<:Real, IType <: Integer}
175+
vals::Vector{T}, iso::Real, eps::Real, vxidx::VoxelIndices{IType}) where {T<:Real, IType <: Integer}
176176

177177
@inbounds ixs = vxidx.voxEdgeCrnrs[e]
178178
@inbounds srcVal = vals[ixs[1]]
179179
@inbounds tgtVal = vals[ixs[2]]
180-
a = (iso-srcVal)/(tgtVal-srcVal)
181-
a = min(max(a, eps), one(T)-eps)
180+
a = min(max((iso-srcVal)/(tgtVal-srcVal), eps), one(T)-eps)
182181
b = one(T)-a
183182
@inbounds c1x,c1y,c1z = vxidx.voxCrnrPos[ixs[1]]
184183
@inbounds c2x,c2y,c2z = vxidx.voxCrnrPos[ixs[2]]
185184

186-
Point{3,T}(
185+
Point(
187186
x+b*c1x+a*c2x,
188187
y+b*c1y+a*c2y,
189188
z+b*c1z+a*c2z
@@ -196,9 +195,9 @@ present.
196195
"""
197196
function getVertId(e::IType, x::IType, y::IType, z::IType,
198197
nx::IType, ny::IType,
199-
vals::Vector{T}, iso::T,
200-
vts::Dict{IType, Point{3,T}},
201-
eps::T, vxidx::VoxelIndices{IType}) where {T<:Real, IType <: Integer}
198+
vals::Vector{T}, iso::Real,
199+
vts::Dict{IType, Point{3,S}},
200+
eps::Real, vxidx::VoxelIndices{IType}) where {T <: Real, S <: Real, IType <: Integer}
202201

203202
vId = vertId(e, x, y, z, nx, ny, vxidx)
204203
if !haskey(vts, vId)
@@ -222,11 +221,11 @@ end
222221
Processes a voxel, adding any new vertices and faces to the given
223222
containers as necessary.
224223
"""
225-
function procVox(vals::Vector{T}, iso::T,
224+
function procVox(vals::Vector{T}, iso::Real,
226225
x::IType, y::IType, z::IType,
227226
nx::IType, ny::IType,
228-
vts::Dict{IType, Point{3,T}}, fcs::Vector{Face{3,IType}},
229-
eps::T, vxidx::VoxelIndices{IType}) where {T<:Real, IType <: Integer}
227+
vts::Dict{IType, Point{3,S}}, fcs::Vector{Face{3,IType}},
228+
eps::Real, vxidx::VoxelIndices{IType}) where {T <: Real, S <: Real, IType <: Integer}
230229

231230
# check each sub-tetrahedron in the voxel
232231
for i::IType = 1:6
@@ -252,8 +251,9 @@ end
252251
Given a 3D array and an isovalue, extracts a mesh represention of the
253252
an approximate isosurface by the method of marching tetrahedra.
254253
"""
255-
function marchingTetrahedra(lsf::AbstractArray{T,3}, iso::T, eps::T, indextype::Type{IT}) where {T<:Real, IT <: Integer}
256-
vts = Dict{indextype, Point{3,T}}()
254+
function marchingTetrahedra(lsf::AbstractArray{T,3}, iso::Real, eps::Real, indextype::Type{IT}) where {T<:Real, IT <: Integer}
255+
vertex_eltype = promote_type(T, typeof(iso), typeof(eps))
256+
vts = Dict{indextype, Point{3,vertex_eltype}}()
257257
fcs = Array{Face{3,indextype}}(0)
258258
sizehint!(vts, div(length(lsf),8))
259259
sizehint!(fcs, div(length(lsf),4))

test/REQUIRE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ForwardDiff 0.7

test/runtests.jl

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using Meshing
22
using Base.Test
33
using GeometryTypes
4+
using ForwardDiff
45

56

67
@testset "meshing" begin
@@ -26,6 +27,13 @@ using GeometryTypes
2627
sqrt(sum(dot(v,v))) - 1 # sphere
2728
end
2829

30+
if "--profile" in ARGS
31+
HomogenousMesh(s2, MarchingTetrahedra())
32+
Profile.clear()
33+
@profile HomogenousMesh(s2, MarchingTetrahedra())
34+
#ProfileView.view()
35+
end
36+
2937
msh = HomogenousMesh(s2, MarchingTetrahedra())
3038
@test length(vertices(msh)) == 973
3139
@test length(faces(msh)) == 1830
@@ -94,12 +102,54 @@ using GeometryTypes
94102
@test faces(m4) == faces(m5)
95103
end
96104
end
97-
end
98105

106+
@testset "mixed types" begin
107+
# https://github.com/JuliaGeometry/Meshing.jl/issues/9
108+
samples = randn(10, 10, 10)
109+
m1 = HomogenousMesh(SignedDistanceField(
110+
HyperRectangle(Vec(0,0,0), Vec(1, 1, 1)),
111+
samples), MarchingTetrahedra())
112+
m2 = HomogenousMesh(SignedDistanceField(
113+
HyperRectangle(Vec(0,0,0), Vec(1, 1, 1)),
114+
Float32.(samples)), MarchingTetrahedra())
115+
@test all(isapprox.(vertices(m1), vertices(m2), rtol=1e-6))
116+
@test all(faces(m1) == faces(m2))
117+
118+
@testset "forward diff" begin
119+
# Demonstrate that we can even take derivatives through the meshing algorithm
120+
f = x -> norm(x)
121+
bounds = HyperRectangle(Vec(-1, -1, -1), Vec(2, 2, 2))
122+
resolution = 0.1
123+
sdf = SignedDistanceField(f, bounds, resolution)
124+
125+
function surface_distance_from_origin(isovalue)
126+
# Compute the mean distance of each vertex in the isosurface
127+
# mesh from the origin. This should return a value equal to the
128+
# isovalue and should have a derivative of 1.0 w.r.t. the
129+
# isovalue. This function is just meant to serve as an example
130+
# of some quantity you might want to differentiate in a mesh,
131+
# and has the benefit for testing of having a trivial expected
132+
# solution.
133+
mesh = HomogenousMesh(sdf, MarchingTetrahedra(isovalue))
134+
mean(norm.(vertices(mesh)))
135+
end
136+
137+
isoval = 0.85
138+
@test surface_distance_from_origin(isoval) isoval atol=1e-2
139+
@test ForwardDiff.derivative(surface_distance_from_origin, isoval) 1 atol=1e-2
140+
end
99141

100-
if "--profile" in ARGS
101-
HomogenousMesh(s2)
102-
Profile.clear()
103-
@profile HomogenousMesh(s2)
104-
#ProfileView.view()
142+
@testset "type stability" begin
143+
# verify that we don't lose type stability just by mixing arguments
144+
# of different types
145+
data = randn(5, 5, 5)
146+
iso = 0.2
147+
eps = 1e-4
148+
@inferred(Meshing.marchingTetrahedra(data, iso, eps, Int))
149+
@inferred(Meshing.marchingTetrahedra(Float32.(data), Float64(iso), Float16(eps), Int32))
150+
@inferred(Meshing.marchingTetrahedra(Float64.(data), Float32(iso), Float64(eps), Int64))
151+
end
152+
end
105153
end
154+
155+

0 commit comments

Comments
 (0)