Skip to content

Commit 6e60238

Browse files
Refactor `HalfEdgeTopology(::Vector{Connectivity})
1 parent b68dd64 commit 6e60238

File tree

1 file changed

+103
-55
lines changed

1 file changed

+103
-55
lines changed

src/topologies/halfedge.jl

Lines changed: 103 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,29 @@ function HalfEdgeTopology(halves::AbstractVector{Tuple{HalfEdge,HalfEdge}}; nele
139139
HalfEdgeTopology(halfedges, half4elem, half4vert, edge4pair)
140140
end
141141

142+
function any_edges_exist(inds, half4pair)
143+
n = length(inds)
144+
for i in eachindex(inds)
145+
uv = (inds[i], inds[mod1(i + 1, n)])
146+
if haskey(half4pair, uv)
147+
return true
148+
end
149+
end
150+
return false
151+
end
152+
153+
const NULL_EDGE = HalfEdge(0, nothing)
154+
function any_claimed_edges_exist(inds, half4pair)
155+
n = length(inds)
156+
for i in eachindex(inds)
157+
uv = (inds[i], inds[mod1(i + 1, n)])
158+
if !isnothing(get(half4pair, uv, NULL_EDGE).elem)
159+
return true
160+
end
161+
end
162+
return false
163+
end
164+
142165
function HalfEdgeTopology(elems::AbstractVector{<:Connectivity}; sort=true)
143166
assertion(all(e -> paramdim(e) == 2, elems), "invalid element for half-edge topology")
144167

@@ -153,75 +176,100 @@ function HalfEdgeTopology(elems::AbstractVector{<:Connectivity}; sort=true)
153176

154177
# initialize with first element
155178
half4pair = Dict{Tuple{Int,Int},HalfEdge}()
156-
elem = first(adjelems)
157-
inds = collect(indices(elem))
158-
v = CircularVector(inds)
159-
n = length(v)
160-
for i in 1:n
161-
half4pair[(v[i], v[i + 1])] = HalfEdge(v[i], eleminds[1])
179+
inds = first(adjelems)
180+
for i in eachindex(inds)
181+
u = inds[i]
182+
u1 = inds[mod1(i + 1, length(inds))]
183+
ei = eleminds[1]
184+
he = get!(() -> HalfEdge(u, ei), half4pair, (u, u1))
185+
# reserve half-edge to enable recognizing orientation mismatches
186+
half = get!(() -> HalfEdge(u1, nothing), half4pair, (u1, u))
187+
he.half = half
188+
half.half = he
162189
end
163190

164191
# insert all other elements
165-
for e in 2:length(adjelems)
166-
elem = adjelems[e]
167-
inds = collect(indices(elem))
168-
v = CircularVector(inds)
169-
n = length(v)
170-
for i in 1:n
171-
# if pair of vertices is already in the
172-
# dictionary this means that the current
173-
# polygon has inconsistent orientation
174-
if haskey(half4pair, (v[i], v[i + 1]))
175-
# delete inserted pairs so far
176-
CCW[e] = false
177-
for j in 1:(i - 1)
178-
delete!(half4pair, (v[j], v[j + 1]))
179-
end
180-
break
192+
remaining = collect(2:length(adjelems))
193+
added = false
194+
disconnected = false
195+
while !isempty(remaining)
196+
iter = 1
197+
while iter length(remaining)
198+
e = remaining[iter]
199+
inds = adjelems[e]
200+
n = length(inds)
201+
if any_edges_exist(inds, half4pair) || disconnected
202+
# at least one edge has been reserved, so we can assess the orientation w.r.t.
203+
# previously added elements/edges
204+
deleteat!(remaining, iter)
205+
added = true
206+
disconnected = false
181207
else
182-
# insert pair in consistent orientation
183-
half4pair[(v[i], v[i + 1])] = HalfEdge(v[i], eleminds[e])
208+
iter += 1
209+
continue
184210
end
185-
end
186211

187-
if !CCW[e]
188-
# reinsert pairs in CCW orientation
189-
for i in 1:n
190-
half4pair[(v[i + 1], v[i])] = HalfEdge(v[i + 1], eleminds[e])
212+
if any_claimed_edges_exist(inds, half4pair)
213+
CCW[e] = false
191214
end
192-
end
193-
end
194215

195-
# add missing pointers
196-
for (e, elem) in Iterators.enumerate(adjelems)
197-
inds = CCW[e] ? indices(elem) : reverse(indices(elem))
198-
v = CircularVector(collect(inds))
199-
n = length(v)
200-
for i in 1:n
201-
# update pointers prev and next
202-
he = half4pair[(v[i], v[i + 1])]
203-
he.prev = half4pair[(v[i - 1], v[i])]
204-
he.next = half4pair[(v[i + 1], v[i + 2])]
205-
206-
# if not a border element, update half
207-
if haskey(half4pair, (v[i + 1], v[i]))
208-
he.half = half4pair[(v[i + 1], v[i])]
209-
else # create half-edge for border
210-
be = HalfEdge(v[i + 1], nothing)
211-
be.half = he
212-
he.half = be
216+
ei = eleminds[e]
217+
if !CCW[e]
218+
# reinsert pairs in CCW orientation
219+
for i in eachindex(inds)
220+
u = inds[i]
221+
u1 = inds[mod1(i + 1, n)]
222+
he = get!(() -> HalfEdge(u1, ei), half4pair, (u1, u))
223+
if !isnothing(he.elem)
224+
@assert he.elem === ei lazy"inconsistent duplicate edge $he for $(ei) and $(he.elem)"
225+
else
226+
he.elem = ei
227+
end
228+
half = get!(() -> HalfEdge(u, nothing), half4pair, (u, u1))
229+
he.half = half
230+
half.half = he
231+
end
232+
else
233+
for i in eachindex(inds)
234+
u = inds[i]
235+
u1 = inds[mod1(i + 1, n)]
236+
he = get!(() -> HalfEdge(u, ei), half4pair, (u, u1))
237+
he.elem = ei # this may be a pre-existing/reserved edge with a nothing `elem` field
238+
half = get!(() -> HalfEdge(u1, nothing), half4pair, (u1, u))
239+
he.half = half
240+
half.half = he
241+
end
213242
end
214243
end
244+
245+
if added
246+
added = false
247+
elseif !isempty(remaining)
248+
disconnected = true
249+
added = false
250+
end
215251
end
216252

217-
# save halfedges in a vector of pairs
253+
# add missing pointers and save halfedges in a vector of pairs
218254
halves = Vector{Tuple{HalfEdge,HalfEdge}}()
219255
visited = Set{Tuple{Int,Int}}()
220-
for ((u, v), he) in half4pair
221-
uv = minmax(u, v)
222-
if uv visited
223-
push!(halves, (he, he.half))
224-
push!(visited, uv)
256+
for (e, inds) in enumerate(adjelems)
257+
inds = CCW[e] ? inds : reverse(inds)
258+
n = length(inds)
259+
for i in eachindex(inds)
260+
vi = inds[i]
261+
vi1 = inds[mod1(i + 1, n)]
262+
vi2 = inds[mod1(i + 2, n)]
263+
# update pointers prev and next
264+
he = half4pair[(vi, vi1)]
265+
he.next = half4pair[(vi1, vi2)]
266+
he.next.prev = he
267+
268+
uv = minmax(vi, vi1)
269+
if uv visited
270+
push!(halves, (he, he.half))
271+
push!(visited, uv)
272+
end
225273
end
226274
end
227275

0 commit comments

Comments
 (0)