-
Notifications
You must be signed in to change notification settings - Fork 88
Improve HalfEdgeTopology(::Vector{<:Connectivity})
correctness
#1194
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Improve HalfEdgeTopology(::Vector{<:Connectivity})
correctness
#1194
Conversation
The code/algorithm doesn't actually check for counter-clockwise-ness, it only knows whether something has an inconsistent orientation with previously added elements (ultimately going back to whichever element was added first--whose orientation was never checked)
2a6d072
to
16aa7c4
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you @halleysfifthinc ! Left a few minor comments. I can review again after we get green tests.
src/topologies/halfedge.jl
Outdated
if !isnothing(get(half4pair, uv, NULL_EDGE).elem) | ||
return true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's avoid this auxiliary NULL_EDGE and make the logic more explicit. We can check the key in the half4pair
and then do something else if it is not present.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This way (using get
with a default of NULL_EDGE
) is faster because it avoids double indexing the Dict (compared to e.g. haskey(half4pair, uv) && !isnothing(half4pair[uv].elem)
). The const externally defined NULL_EDGE
is helpful because HalfEdge
is mutable, which means defining a HalfEdge
allocates. Defining NULL_EDGE
in the function would incur that definition/allocation cost every time the function is called.
Co-authored-by: Júlio Hoffimann <julio.hoffimann@gmail.com>
Co-authored-by: Júlio Hoffimann <julio.hoffimann@gmail.com>
Co-authored-by: Júlio Hoffimann <julio.hoffimann@gmail.com>
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## master #1194 +/- ##
==========================================
+ Coverage 88.31% 88.39% +0.07%
==========================================
Files 196 196
Lines 6094 6136 +42
==========================================
+ Hits 5382 5424 +42
Misses 712 712 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
8f116fa
to
f84346f
Compare
Benchmarking procedure same as previously described, using Quads only:
Quads and tris:
|
This PR reveals/fixes a couple of bugs in the
HalfEdgeTopology
constructor.Dict
's are unordered, so constructing thehalves::Vector{Tuple{HalfEdge,HalfEdge}}
by iterating throughhalf4pair
pairs produces a roughly random order (by element) for halves.Meshes.jl/src/topologies/halfedge.jl
Line 220 in 3a3d0e3
loop
. (Cause of most/all? test failures.)master
state of the function can lead to non-closed edge loops, such that theloop
function never terminates and allocates memory until Julia is OOM killed1.CCW
) was misleading, as actual element orientation isn't checked (and cannot be without actual vertices afaict?). Also, when there are multiple unconnected components (which I believe is still valid under a 2-manifold?) the orientation consistency checks don't/can't cross the disconnection, leading to further potential mismatches between the nameCCW
and reality. I renamed it to make this clearer.The cause of most of the current test failures are due to changing to the
halves
construction to be element ordered. I believe this is more correct, and that any stable/consistent edge indices with the previous version are because the hashing algorithm in Base has not been changed between Julia versions (that will no longer be true after 1.13 with JuliaLang/julia#57509).Another cause of test failures is a change to the reverse index iteration, which now ensures that the first index returned by
loop
(which affects several downstream dependent functions) is either:With regard to sorting and orientation consistency, the PR is correct (no infinite memory use) even for incorrectly- or unsorted elements when called with
sort=false
, but the edge indices will/may change betweensort=false
andsort=true
.If you agree with this PR's general direction. I will update the tests and share benchmarks.
Footnotes
In my code, I am redefining
loop
similar to the given example, but using a ScopedValue so that the maximum number of edges can be changed from outside the function if needed. I'm happy to share/submit that if you are interested, but I realize that such a solution might be too limiting generally to be added to the library. ↩