Skip to content

Commit 90988fe

Browse files
committed
update Mesh & MetaMesh utils
1 parent ff64179 commit 90988fe

File tree

3 files changed

+122
-68
lines changed

3 files changed

+122
-68
lines changed

src/basic_types.jl

Lines changed: 69 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -349,32 +349,68 @@ struct Mesh{
349349
return new{D, T, FT, Names, VAT, FVT}(va, f, views)
350350
end
351351
end
352+
353+
@inline function Base.hasproperty(mesh::Mesh, field::Symbol)
354+
return hasproperty(getfield(mesh, :vertex_attributes), field) || hasfield(Mesh, field)
355+
end
356+
@inline function Base.getproperty(mesh::Mesh, field::Symbol)
357+
if hasfield(Mesh, field)
358+
return getfield(mesh, field)
359+
else
360+
return getproperty(getfield(mesh, :vertex_attributes), field)
361+
end
362+
end
363+
@inline function Base.propertynames(mesh::Mesh)
364+
return (fieldnames(Mesh)..., propertynames(getfield(mesh, :vertex_attributes))...)
365+
end
366+
367+
coordinates(mesh::Mesh) = mesh.position
352368
faces(mesh::Mesh) = mesh.connectivity
353-
Base.getindex(mesh::Mesh, i::Integer) = mesh.vertices[mesh.connectivity[i]]
369+
normals(mesh::Mesh) = hasproperty(mesh, :normals) ? mesh.normals : nothing
370+
texturecoordinates(mesh::Mesh) = hasproperty(mesh, :uv) ? mesh.uv : nothing
371+
vertex_attributes(mesh::Mesh) = getfield(mesh, :vertex_attributes)
372+
373+
Base.getindex(mesh::Mesh, i::Integer) = mesh[mesh.connectivity[i]]
354374
Base.length(mesh::Mesh) = length(mesh.connectivity)
355-
Base.:(==)(a::Mesh, b::Mesh) = coordinates(a) == coordinates(b) && faces(a) == faces(b)
375+
376+
# TODO: temp
377+
function Base.getindex(mesh::Mesh{D, T, <: AbstractFace, (:position,)}, f::AbstractFace) where {D, T}
378+
return getfield(mesh, :vertex_attributes).position[f]
379+
end
380+
function Base.getindex(::Mesh, f::MultiFace)
381+
error("TODO")
382+
end
383+
function Base.getindex(::Mesh, f::AbstractFace)
384+
error("TODO")
385+
end
386+
387+
function Base.:(==)(a::Mesh, b::Mesh)
388+
return a.vertex_attributes == b.vertex_attributes &&
389+
faces(a) == faces(b) && a.views == b.views
390+
end
356391

357392
function Base.iterate(mesh::Mesh, i=1)
358393
return i - 1 < length(mesh) ? (mesh[i], i + 1) : nothing
359394
end
360395

396+
function Mesh(faces::AbstractVector{<:AbstractFace}; attributes...)
397+
return Mesh(NamedTuple(attributes), faces)
398+
end
399+
361400
function Mesh(points::AbstractVector{Point{Dim, T}},
362-
faces::AbstractVector{<:AbstractFace}) where {Dim, T}
363-
return Mesh{Dim, T, }(points, faces)
401+
faces::AbstractVector{<:AbstractFace}; kwargs...) where {Dim, T}
402+
return Mesh((position = points, kwargs...), faces)
364403
end
365404

366405
function Mesh(points::AbstractVector{<:Point}, faces::AbstractVector{<:Integer},
367406
facetype=TriangleFace, skip=1)
368407
return Mesh(points, connect(faces, facetype, skip))
369408
end
370409

410+
371411
struct MetaMesh{Dim, T, M <: AbstractMesh{Dim, T}} <: AbstractMesh{Dim, T}
372412
mesh::M
373413
meta::Dict{Symbol, Any}
374-
375-
function MetaMesh(mesh::AbstractMesh{Dim, T}, meta::Dict{Symbol, Any}) where {Dim, T}
376-
return new{Dim, T, typeof(mesh)}(mesh, meta)
377-
end
378414
end
379415

380416
function MetaMesh(mesh::AbstractMesh; kwargs...)
@@ -385,14 +421,34 @@ function MetaMesh(points::AbstractVector{<:Point}, faces::AbstractVector{<:Abstr
385421
MetaMesh(Mesh(points, faces), Dict{Symbol, Any}(kwargs))
386422
end
387423

388-
@inline Base.hasproperty(mesh::MetaMesh, field::Symbol) = hasproperty(getfield(mesh, :meta), field)
389-
@inline Base.getproperty(mesh::MetaMesh, field::Symbol) = getproperty(getfield(mesh, :meta), field)
390-
@inline Base.propertynames(mesh::MetaMesh) = propertynames(getfield(mesh, :meta))
424+
# TODO: Do we want to access meta here?
425+
@inline function Base.hasproperty(mesh::MetaMesh, field::Symbol)
426+
return hasfield(MetaMesh, field) || hasproperty(getfield(mesh, :mesh), field)
427+
end
428+
@inline function Base.getproperty(mesh::MetaMesh, field::Symbol)
429+
if hasfield(MetaMesh, field)
430+
return getfield(mesh, field)
431+
else
432+
return getproperty(getfield(mesh, :mesh), field)
433+
end
434+
end
435+
@inline function Base.propertynames(mesh::MetaMesh)
436+
return (fieldnames(MetaMesh)..., propertynames(getfield(mesh, :mesh))...)
437+
end
438+
439+
# TODO: or via getindex?
440+
Base.haskey(mesh::MetaMesh, key::Symbol) = haskey(getfield(mesh, :meta), key)
441+
Base.get(f, mesh::MetaMesh, key::Symbol) = get(f, getfield(mesh, :meta), key)
442+
Base.get!(f, mesh::MetaMesh, key::Symbol) = get!(f, getfield(mesh, :meta), key)
443+
Base.getindex(mesh::MetaMesh, key::Symbol) = getindex(getfield(mesh, :meta), key)
444+
Base.setindex!(mesh::MetaMesh, value, key::Symbol) = setindex!(getfield(mesh, :meta), value, key)
445+
Base.delete!(mesh::MetaMesh, key::Symbol) = delete!(getfield(mesh, :meta), key)
391446

392447
coordinates(mesh::MetaMesh) = coordinates(Mesh(mesh))
393448
faces(mesh::MetaMesh) = faces(Mesh(mesh))
394-
normals(mesh::MetaMesh) = hasproperty(mesh, :normals) ? mesh.normals : nothing
395-
texturecoordinates(mesh::MetaMesh) = hasproperty(mesh, :uv) ? mesh.uv : nothing
449+
normals(mesh::MetaMesh) = normals(Mesh(mesh))
450+
texturecoordinates(mesh::MetaMesh) = texturecoordinates(Mesh(mesh))
451+
vertex_attributes(mesh::MetaMesh) = vertex_attributes(Mesh(mesh))
396452

397453
meta(@nospecialize(m)) = NamedTuple()
398454
meta(mesh::MetaMesh) = getfield(mesh, :meta)

src/meshes.jl

Lines changed: 50 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ Note, that this can be an `Int` or `Tuple{Int, Int}``, when the primitive is gri
1111
It also only losely correlates to the number of vertices, depending on the algorithm used.
1212
#TODO: find a better number here!
1313
"""
14-
function mesh(primitive::AbstractGeometry; pointtype=Point, facetype=GLTriangleFace)
14+
function mesh(primitive::AbstractGeometry; pointtype=Point, facetype=GLTriangleFace, vertex_attributes...)
1515
positions = decompose(pointtype, primitive)
1616
_f = faces(primitive)
1717
# If faces returns nothing for primitive, we try to triangulate!
@@ -25,11 +25,11 @@ function mesh(primitive::AbstractGeometry; pointtype=Point, facetype=GLTriangleF
2525
else
2626
f = decompose(facetype, _f)
2727
end
28-
return Mesh(positions, f)
28+
return Mesh(positions, f; vertex_attributes...)
2929
end
3030

31-
const SimpleMesh{N, T, FT} = Mesh{N, T, Vector{Point{N, T}}, Vector{FT}}
32-
const TriangleMesh{N} = SimpleMesh{N, Float32, GLTriangleFace}
31+
const SimpleMesh{N, T, FT} = Mesh{N, T, FT, (:position,), Tuple{Vector{Point{N, T}}}, Vector{FT}}
32+
const SimpleTriangleMesh{N} = SimpleMesh{N, Float32, GLTriangleFace}
3333

3434
"""
3535
mesh(polygon::AbstractVector{P}; pointtype=P, facetype=GLTriangleFace,
@@ -58,7 +58,7 @@ function triangle_mesh(primitive::Mesh{N}) where {N}
5858
end
5959
end
6060

61-
function triangle_mesh(primitive::Union{AbstractGeometry{N}, AbstractVector{<: Point{N}}}; nvertices = nothing)::TriangleMesh{N} where {N}
61+
function triangle_mesh(primitive::Union{AbstractGeometry{N}, AbstractVector{<: Point{N}}}; nvertices = nothing)::SimpleTriangleMesh{N} where {N}
6262
if nvertices !== nothing
6363
@warn("nvertices argument deprecated. Wrap primitive in `Tesselation(primitive, nvertices)`")
6464
primitive = Tesselation(primitive, nvertices)
@@ -67,25 +67,26 @@ function triangle_mesh(primitive::Union{AbstractGeometry{N}, AbstractVector{<: P
6767
end
6868

6969
function uv_mesh(primitive::AbstractGeometry{N,T}) where {N,T}
70-
m = triangle_mesh(primitive)
71-
return MetaMesh(m, (uv=decompose_uv(primitive),))
70+
return mesh(primitive, uv = decompose_uv(primitive), pointtype=Point{N,Float32}, facetype=GLTriangleFace)
7271
end
7372

7473
function uv_normal_mesh(primitive::AbstractGeometry{N}) where {N}
75-
m = triangle_mesh(primitive)
76-
return MetaMesh(m, (uv=decompose_uv(primitive), normals=decompose_normals(m)))
74+
return mesh(
75+
primitive, uv = decompose_uv(primitive), normals = decompose_normals(primitive),
76+
pointtype=Point{N,Float32}, facetype=GLTriangleFace)
7777
end
7878

7979
function normal_mesh(points::AbstractVector{<:Point},
8080
faces::AbstractVector{<:AbstractFace})
8181
_points = decompose(Point3f, points)
8282
_faces = decompose(GLTriangleFace, faces)
83-
return MetaMesh(Mesh(_points, _faces), (normals=normals(_points, _faces),))
83+
return Mesh(_faces, position = _points, normals = normals(_points, _faces))
8484
end
8585

8686
function normal_mesh(primitive::AbstractGeometry{N}) where {N}
87-
m = triangle_mesh(primitive)
88-
return MetaMesh(m, (normals=decompose_normals(m),))
87+
return mesh(
88+
primitive, normals = decompose_normals(primitive),
89+
pointtype=Point{N,Float32}, facetype=GLTriangleFace)
8990
end
9091

9192
"""
@@ -164,59 +165,56 @@ function map_coordinates!(f, mesh::AbstractMesh)
164165
return mesh
165166
end
166167

167-
function add_meta(mesh::MetaMesh; kw...)
168-
return MetaMesh(Mesh(mesh), (; meta(mesh)..., kw...))
169-
end
170-
171-
function add_meta(mesh::Mesh; kw...)
172-
return MetaMesh(mesh, (; meta(mesh)..., kw...))
173-
end
174-
175-
# I didn't find a simple way to remove a field from a namedtuple in a type stable way without
176-
# a generated function..
177-
@generated function pop(nt::NamedTuple{Names, Values}, ::Val{name}) where {Names, Values, name}
178-
if !(name in Names)
179-
return :(throw(Base.KeyError($(QuoteNode(name)))))
180-
else
181-
names = filter(x-> x !== name, Names)
182-
nt = map(names) do name
183-
:($name = nt.$(name))
184-
end
185-
return :((; $(nt...)), nt.$(name))
186-
end
187-
end
168+
# TODO:
169+
add_meta(m, kw...) = error("TODO")
170+
pop_meta(m, kw...) = error("TODO")
171+
# function add_meta(mesh::MetaMesh; kw...)
172+
# return MetaMesh(Mesh(mesh), (; meta(mesh)..., kw...))
173+
# end
188174

189-
function pop_meta(mesh::MetaMesh, name::Symbol)
190-
new_meta, value = pop(meta(mesh), Val(name))
191-
return MetaMesh(mesh, new_meta), value
192-
end
175+
# function add_meta(mesh::Mesh; kw...)
176+
# return MetaMesh(mesh, (; meta(mesh)..., kw...))
177+
# end
193178

179+
# # I didn't find a simple way to remove a field from a namedtuple in a type stable way without
180+
# # a generated function..
181+
# @generated function pop(nt::NamedTuple{Names, Values}, ::Val{name}) where {Names, Values, name}
182+
# if !(name in Names)
183+
# return :(throw(Base.KeyError($(QuoteNode(name)))))
184+
# else
185+
# names = filter(x-> x !== name, Names)
186+
# nt = map(names) do name
187+
# :($name = nt.$(name))
188+
# end
189+
# return :((; $(nt...)), nt.$(name))
190+
# end
191+
# end
194192

195-
function Base.get(f, mesh::MetaMesh, key::Symbol)
196-
hasproperty(mesh, key) && return getproperty(mesh, key)
197-
return f()
198-
end
193+
# function pop_meta(mesh::MetaMesh, name::Symbol)
194+
# new_meta, value = pop(meta(mesh), Val(name))
195+
# return MetaMesh(mesh, new_meta), value
196+
# end
199197

200-
function Base.show(io::IO, ::MIME"text/plain", mesh::Mesh{N, T}) where {N, T}
201-
FT = eltype(faces(mesh))
202-
println(io, "Mesh{$N, $T, $(FT)}")
203-
println(io, " vertices: ", length(coordinates(mesh)))
204-
println(io, " faces: ", length(faces(mesh)), " $(FT)")
198+
function Base.show(io::IO, ::MIME"text/plain", mesh::Mesh{N, T, FT}) where {N, T, FT}
199+
println(io, "Mesh{$N, $T, $FT}")
200+
println(io, " faces: ", length(faces(mesh)))
201+
for (name, attrib) in pairs(vertex_attributes(mesh))
202+
println(io, " vertex $(name): ", length(attrib))
203+
end
205204
end
206205

207-
function Base.show(io::IO, mesh::Mesh{N, T}) where {N, T}
208-
FT = eltype(faces(mesh))
206+
function Base.show(io::IO, ::Mesh{N, T, FT}) where {N, T, FT}
209207
print(io, "Mesh{$N, $T, $(FT)}(...)")
210208
end
211209

212210
function Base.show(io::IO, ::MIME"text/plain", mesh::MetaMesh{N, T}) where {N, T}
213211
FT = eltype(faces(mesh))
214212
println(io, "MetaMesh{$N, $T, $(FT)}")
215-
println(io, " vertices: ", length(coordinates(mesh)))
216-
println(io, " faces: ", length(faces(mesh)), " $(FT)")
217-
for (k, v) in pairs(meta(mesh))
218-
println(io, " ", k, ": ", length(v), " $(eltype(v))")
213+
println(io, " faces: ", length(faces(mesh)))
214+
for (name, attrib) in pairs(vertex_attributes(mesh))
215+
println(io, " vertex $(name): ", length(attrib))
219216
end
217+
println(io, " meta: ", keys(mesh.meta))
220218
end
221219

222220
function Base.show(io::IO, mesh::MetaMesh{N, T}) where {N, T}

test/runtests.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,13 @@ end
3636
tfaces = TetrahedronFace{Int}[(1, 2, 3, 4), (5, 6, 7, 8)]
3737
normals = rand(Vec{3, Float64}, 8)
3838
stress = LinRange(0, 1, 8)
39-
mesh = MetaMesh(points, tfaces; normals = normals, stress = stress)
39+
mesh = Mesh(points, tfaces; normals = normals, stress = stress)
4040

4141
@test hasproperty(mesh, :stress)
4242
@test hasproperty(mesh, :normals)
4343
@test mesh.stress === stress
4444
@test mesh.normals === normals
45-
@test mesh.normals === normals
45+
@test mesh.position === points
4646
@test GeometryBasics.faces(mesh) === tfaces
4747
@test propertynames(mesh) == (:normals, :stress)
4848
end
@@ -200,7 +200,7 @@ end
200200
@testset "convert mesh + meta" begin
201201
m = uv_normal_mesh(Circle(Point2f(0), 1f0))
202202
# For 2d primitives normal is just the upvector
203-
m.normals == [Vec3f(0, 0, 1) for p in coordinates(m)]
203+
@test m.normals == [Vec3f(0, 0, 1) for p in coordinates(m)]
204204
end
205205

206206
@testset "convert mesh + meta" begin

0 commit comments

Comments
 (0)