@@ -139,6 +139,29 @@ function HalfEdgeTopology(halves::AbstractVector{Tuple{HalfEdge,HalfEdge}}; nele
139
139
HalfEdgeTopology (halfedges, half4elem, half4vert, edge4pair)
140
140
end
141
141
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
+
142
165
function HalfEdgeTopology (elems:: AbstractVector{<:Connectivity} ; sort= true )
143
166
assertion (all (e -> paramdim (e) == 2 , elems), " invalid element for half-edge topology" )
144
167
@@ -153,75 +176,100 @@ function HalfEdgeTopology(elems::AbstractVector{<:Connectivity}; sort=true)
153
176
154
177
# initialize with first element
155
178
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
162
189
end
163
190
164
191
# 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
181
207
else
182
- # insert pair in consistent orientation
183
- half4pair[(v[i], v[i + 1 ])] = HalfEdge (v[i], eleminds[e])
208
+ iter += 1
209
+ continue
184
210
end
185
- end
186
211
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
191
214
end
192
- end
193
- end
194
215
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
213
242
end
214
243
end
244
+
245
+ if added
246
+ added = false
247
+ elseif ! isempty (remaining)
248
+ disconnected = true
249
+ added = false
250
+ end
215
251
end
216
252
217
- # save halfedges in a vector of pairs
253
+ # add missing pointers and save halfedges in a vector of pairs
218
254
halves = Vector {Tuple{HalfEdge,HalfEdge}} ()
219
255
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
225
273
end
226
274
end
227
275
0 commit comments