Skip to content

Commit 4199cd5

Browse files
committed
update merge for MultiFace and views
1 parent 9758bed commit 4199cd5

File tree

2 files changed

+178
-37
lines changed

2 files changed

+178
-37
lines changed

src/basic_types.jl

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,25 @@ MultiFace{Names}(args...) where {Names} = MultiFace(NamedTuple{Names}(args))
6767
MultiFace{Names}(args::Tuple{Vararg{<: AbstractFace}}) where {Names} = MultiFace(NamedTuple{Names}(args))
6868
MultiFace{Names, FT}(args) where {Names, FT <: AbstractFace} = MultiFace(NamedTuple{Names}(FT.(args)))
6969

70+
function MultiFace{Names}(f::MultiFace) where {Names}
71+
return MultiFace{Names}(getproperty.((f,), Names))
72+
end
73+
7074
Base.getindex(f::MultiFace, i::Integer) = Base.getindex(getfield(f, :faces), i)
7175
@inline Base.hasproperty(f::MultiFace, field::Symbol) = hasproperty(getfield(f, :faces), field)
7276
@inline Base.getproperty(f::MultiFace, field::Symbol) = getproperty(getfield(f, :faces), field)
7377
@inline Base.propertynames(f::MultiFace) = propertynames(getfield(f, :faces))
7478
@inline Base.propertynames(::Type{<: MultiFace{N, T, FT, Names}}) where {N, T, FT, Names} = Names
79+
Base.eltype(::MultiFace{N, T, FT}) where {N, T, FT} = FT
80+
Base.eltype(::Type{<: MultiFace{N, T, FT}}) where {N, T, FT} = FT
81+
82+
function simplify_faces(::Type{MF1}, fs::AbstractVector{MF2}) where {MF1 <: MultiFace, MF2 <: MultiFace}
83+
return simplify_faces(propertynames(MF1), fs)
84+
end
85+
86+
function simplify_faces(names::NTuple{N, Symbol}, fs::AbstractVector{MF2}) where {N, MF2 <: MultiFace}
87+
return map(f -> MultiFace{names}(f), fs)
88+
end
7589

7690
# TODO: Some shorthands
7791
const NormalFace = MultiFace{(:position, :normal)}
@@ -327,12 +341,12 @@ struct Mesh{
327341

328342
vertex_attributes::NamedTuple{Names, VertexAttribTypes}
329343
connectivity::FaceVecType
330-
views::Vector{UnitRange}
344+
views::Vector{UnitRange{Int}}
331345

332346
function Mesh(
333347
va::NamedTuple{Names, VAT},
334348
f::FVT,
335-
views::Vector{UnitRange} = UnitRange[]
349+
views::Vector{UnitRange{Int}} = UnitRange{Int}[]
336350
) where {
337351
D, T, FT, Names,
338352
VAT <: Tuple{AbstractVector{Point{D, T}}, Vararg{AbstractVector}},
@@ -346,7 +360,18 @@ struct Mesh{
346360

347361
if FT <: MultiFace
348362
f_names = propertynames(FT)
349-
if Names != f_names
363+
# if Names != f_names
364+
# error(
365+
# "Cannot construct a mesh with vertex attribute names $Names and MultiFace " *
366+
# "attribute names $f_names. These must include the same names in the same order."
367+
# )
368+
# end
369+
if Names == f_names
370+
# all good
371+
elseif issubset(Names, f_names)
372+
# remove the extras/fix order
373+
f = simplify_faces(Names, f)
374+
else
350375
error(
351376
"Cannot construct a mesh with vertex attribute names $Names and MultiFace " *
352377
"attribute names $f_names. These must include the same names in the same order."
@@ -362,7 +387,7 @@ struct Mesh{
362387
error("Face vectors that may include `MultiFace`s with different names are not allowed. (Type $FT too abstract.)")
363388
end
364389

365-
return new{D, T, FT, Names, VAT, FVT}(va, f, views)
390+
return new{D, T, eltype(typeof(f)), Names, VAT, typeof(f)}(va, f, views)
366391
end
367392
end
368393

src/meshes.jl

Lines changed: 149 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,11 @@ function mesh(primitive::AbstractGeometry; pointtype=Point, facetype=GLTriangleF
2727
if _f isa AbstractVector{<: MultiFace}
2828
if facetype isa MultiFace
2929
# drop faces that facetype doesn't include
30-
names = propertynames(facetype)
31-
_f = map(f -> MultiFace{names}(getproperty.((f,), names)), _f)
30+
_f = simplify_faces(facetype, _f)
3231
else
3332
# drop faces for vertex attributes that aren't given
3433
names = (:position, keys(vertex_attributes)...)
35-
_f2 = map(f -> MultiFace{names}(getproperty.((f,), names)), _f)
34+
_f2 = simplify_faces(names, _f)
3635

3736
# and remap to a simple face type so that decompose can handle the rest
3837
_f, mappings = merge_vertex_indices(_f2)
@@ -47,7 +46,6 @@ function mesh(primitive::AbstractGeometry; pointtype=Point, facetype=GLTriangleF
4746
return Mesh(positions, f; vertex_attributes...)
4847
end
4948

50-
5149
const SimpleMesh{N, T, FT} = Mesh{N, T, FT, (:position,), Tuple{Vector{Point{N, T}}}, Vector{FT}}
5250
const SimpleTriangleMesh{N} = SimpleMesh{N, Float32, GLTriangleFace}
5351

@@ -123,41 +121,131 @@ function volume(mesh::Mesh)
123121
return sum(volume, mesh)
124122
end
125123

124+
# TODO: Is this ok as "public" function?
125+
# MultiFace(f1, f2, f3) + (o1, o2, o3) = MultiFace(f1 + o1, f2 + o2, f3 + o3)
126+
function Base.:+(f::MultiFace{N, T, FT, Names, M}, o::NTuple{M, T}) where {N, T, FT, Names, M}
127+
return MultiFace{Names}(ntuple(m -> f[m] + o[m], M))
128+
end
129+
126130
function Base.merge(meshes::AbstractVector{<:Mesh})
127131
return if isempty(meshes)
128132
return Mesh(Point3f[], GLTriangleFace[])
129133
elseif length(meshes) == 1
130134
return meshes[1]
131135
else
132-
ps = reduce(vcat, coordinates.(meshes))
133-
fs = reduce(vcat, faces.(meshes))
134-
idx = length(faces(meshes[1]))
135-
offset = length(coordinates(meshes[1]))
136-
for mesh in Iterators.drop(meshes, 1)
137-
N = length(faces(mesh))
138-
for i = idx .+ (1:N)
139-
fs[i] = fs[i] .+ offset
140-
end
141-
idx += N
142-
offset += length(coordinates(mesh))
136+
137+
m1 = meshes[1]
138+
139+
# Check that all meshes use the same VertexAttributes
140+
# Could also do this via typing the function, but maybe error is nice?
141+
names = propertynames(m1.vertex_attributes)
142+
idx = findfirst(m -> propertynames(m.vertex_attributes) != names, meshes)
143+
if idx !== nothing
144+
error(
145+
"Cannot merge meshes with different vertex attributes. " *
146+
"First missmatch between meshes[1] with $names and " *
147+
"meshes[$idx] with $(propertynames(meshes[idx]))."
148+
)
143149
end
144-
return Mesh(ps, fs)
145-
end
146-
end
147150

148-
function Base.merge(meshes::AbstractVector{T}) where T <: MetaMesh
149-
isempty(meshes) && return T(Point3f[], GLTriangleFace[])
150-
big_mesh = merge(map(Mesh, meshes))
151-
big_meta = deepcopy(meta(meshes[1]))
152-
for mesh in Iterators.drop(meshes, 1)
153-
mm = meta(mesh)
154-
for (k, v) in pairs(mm)
155-
append!(big_meta[k], v)
151+
# We can't merge MultiFace with standard faces because MutliFace allows
152+
# desynchronizes vertex indices that normal faces assume synchronized.
153+
is_multi = facetype(m1) <: MultiFace
154+
155+
if all(m -> is_multi == (facetype(m) <: MultiFace), meshes)
156+
157+
# All the same kind of face, can just merge
158+
159+
new_attribs = NamedTuple{names}(map(names) do name
160+
return mapreduce(m -> getproperty(m, name), vcat, meshes)
161+
end)
162+
fs = reduce(vcat, faces.(meshes))
163+
164+
# TODO: is the type difference in offset bad?
165+
idx = length(faces(m1))
166+
offset = is_multi ? length.(values(vertex_attributes(m1))) : length(coordinates(m1))
167+
views = isempty(m1.views) ? [1:idx] : copy(m1.views)
168+
169+
for mesh in Iterators.drop(meshes, 1)
170+
# update face indices
171+
N = length(faces(mesh))
172+
for i = idx .+ (1:N)
173+
fs[i] = fs[i] + offset
174+
end
175+
176+
# add views
177+
if isempty(mesh.views)
178+
push!(views, idx+1 : idx+N)
179+
else
180+
append!(views, (view + idx for view in mesh.views))
181+
end
182+
183+
idx += N
184+
if is_multi
185+
offset = offset .+ length.(values(vertex_attributes(mesh)))
186+
else
187+
offset += length(coordinates(mesh))
188+
end
189+
end
190+
191+
return Mesh(new_attribs, fs, views)
192+
193+
else
194+
195+
# TODO: find simplest face type to target
196+
197+
# Varying ace types, need to convert MultiFace
198+
new_attribs = NamedTuple{names}(similar.(values(vertex_attributes(m1)), 0))
199+
FT = facetype(m1) <: MultiFace ? eltype(facetype(m1)) : facetype(m1)
200+
remapped_faces = []
201+
new_views = UnitRange{Int}[]
202+
vertex_index_counter = eltype(FT)(1)
203+
204+
for mesh in meshes
205+
# convert MultiFace mesh to normal faces, synchronizing vertex indices
206+
attribs, fs, views = merge_vertex_indices(
207+
vertex_attributes(mesh), faces(mesh), mesh.views, vertex_index_counter)
208+
209+
# increment first vertex index used by faces of the next iteration
210+
vertex_index_counter += length(attribs[1])
211+
212+
# update merged data
213+
for name in names
214+
append!(new_attribs[name], attribs[name])
215+
end
216+
217+
push!(remapped_faces, fs)
218+
219+
if isempty(views)
220+
push!(new_views, 1:length(fs))
221+
else
222+
append!(new_views, views)
223+
end
224+
end
225+
226+
# We did MultiFace -> normal face, now equalize normal face types
227+
new_faces = reduce(vcat, remapped_faces)
228+
229+
return Mesh(new_attribs, new_faces, new_views)
156230
end
231+
157232
end
158-
return MetaMesh(big_mesh, big_meta)
159233
end
160234

235+
# TODO: Probably not our problem
236+
# function Base.merge(meshes::AbstractVector{T}) where T <: MetaMesh
237+
# isempty(meshes) && return T(Point3f[], GLTriangleFace[])
238+
# big_mesh = merge(map(Mesh, meshes))
239+
# big_meta = deepcopy(meta(meshes[1]))
240+
# for mesh in Iterators.drop(meshes, 1)
241+
# mm = meta(mesh)
242+
# for (k, v) in pairs(mm)
243+
# append!(big_meta[k], v)
244+
# end
245+
# end
246+
# return MetaMesh(big_mesh, big_meta)
247+
# end
248+
161249
# TODO: naming
162250
# synchronize_vertex_attributes
163251
# merge_vertex_(attribute)_indices
@@ -170,10 +258,28 @@ function merge_vertex_indices(mesh)
170258
return Mesh(attribs, fs, views)
171259
end
172260

261+
function merge_vertex_indices(
262+
attribs::NamedTuple{Names},
263+
faces::AbstractVector{<: FT},
264+
views::Vector{UnitRange{Int}},
265+
vertex_index_counter = nothing
266+
) where {Names, FT <: AbstractFace}
267+
268+
if FT <: MultiFace
269+
error(
270+
"Failed to call correct method. This likely happened because vertex " *
271+
"attributes names $Names do not match face name $(propertynames(first(faces)))."
272+
)
273+
end
274+
275+
return attribs, faces, views
276+
end
277+
173278
function merge_vertex_indices(
174279
attribs::NamedTuple{Names},
175280
faces::AbstractVector{<: MultiFace{N, T, FT, Names}},
176-
views::Vector{UnitRange}
281+
views::Vector{UnitRange{Int}},
282+
vertex_index_counter = T(1) # TODO: test 0 vs 1 base
177283
) where {Names, N, T, FT}
178284

179285
# Note: typing checks for matching Names
@@ -186,17 +292,16 @@ function merge_vertex_indices(
186292

187293
new_attribs = NamedTuple((Pair(k, similar(v, 0)) for (k, v) in pairs(attribs)))
188294
new_faces = similar(faces, FT, 0)
189-
new_views = UnitRange[]
295+
new_views = UnitRange{Int}[]
190296

297+
# TODO: this depends on T in Face (1 based -> 1, 0 based -> 0)
191298
for idxs in views
192-
# TODO: this depends on T in Face (1 based -> 1, 0 based -> 0)
193-
vertex_index_counter = T(length(new_attribs[1]) + 1)
194-
195299
# Generate new face from current view, with the first vertex_index
196300
# corresponding to the first vertex attribute added in this iteration
197301
face_view = view(faces, idxs)
198302
new_faces_in_view, vertex_map = merge_vertex_indices(face_view, vertex_index_counter)
199-
303+
vertex_index_counter += length(vertex_map)
304+
200305
# update vertex attributes
201306
for (name, indices) in pairs(vertex_map)
202307
append!(new_attribs[name], view(attribs[name], indices))
@@ -211,6 +316,17 @@ function merge_vertex_indices(
211316
return new_attribs, new_faces, new_views
212317
end
213318

319+
function merge_vertex_indices(
320+
faces::AbstractVector{FT},
321+
vertex_index_counter = T(1)
322+
) where {N, T, FT <: AbstractFace{N, T}}
323+
324+
@assert !(FT <: MultiFace) "Dispatch failed?"
325+
326+
N_vert = mapreduce(f -> max(f), max, faces)
327+
return faces, (1:N_vert) .+ vertex_index_counter
328+
end
329+
214330
function merge_vertex_indices(
215331
faces::AbstractVector{<: MultiFace{N, T, FT, Names, N_Attrib}},
216332
vertex_index_counter = T(1)

0 commit comments

Comments
 (0)