Repository navigation
Conversation
When truncated intersection points of thin edges close a narrow crack into a small hole, the output ring before cleanup is still correct there: the remainder winds +1 and a smaller loop of opposite orientation winds -1. doSplitOp dropped that loop as a spurious twist, so the remainder filled the hole and union covered area that is in none of the inputs. Upstream C++ DoSplitOp has the same condition. Before dropping such a loop, test its centroid (in doubles) against the remainder; if it is inside, keep the loop as its own path. Loops outside the remainder are still dropped.
Contributor
Author
Contributor
|
thanks - I did see that PR and I'm hoping angus merges it. in some ways clipper2-ts is "ahead" of clipper2, but I worry this might cause the need for larger refactoring in the future, so I am being somewhat conservative here - there are many issues and PRs upstream that I would like to fix in clipper2-ts but I don't want to become incompatible. on the other hand, clipper2 has been fairly inactive lately, so I may just accept this PR once I've had a moment to evaluate. I will keep you updated, and appreciate your work both here and upstream! |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
unioncan return a region that is in none of the inputs:The output is a single ring of area 1 865; 649 square units of it lie outside the inputs. The C++ library returns the same ring, so this is inherited from upstream: AngusJohnson/Clipper2#1109.
Cause
In exact arithmetic triangles 2 and 3 do not touch — there is a narrow crack between them. With integer intersection points, (0.95, 0.13) and (0.94, 0.13) become (0, 0), which moves the left edge of triangle 2 across the right edge of triangle 3 and closes the crack into a small hole.
Before cleanup the output ring is still correct at (−7, 7): the main part winds +1 there and a small loop of opposite orientation winds −1.
fixSelfIntersects→doSplitOpsplits that loop off; because it is smaller than the remaining path and of opposite orientation, it is dropped as a spurious twist. The −1 is lost and the remaining path fills the hole.Dropping a reversed loop that lies outside the remaining path only removes a sliver. Dropping one that lies inside fills a hole.
Fix
Before dropping a smaller reversed loop, test whether its centroid (computed in doubles, so it is not rounded outside a thin triangle) has a non-zero winding number relative to the remaining path. If it does, keep the loop as its own path (a hole). Loops outside the remaining path are still dropped.
The minimal case now returns the same outer ring plus the hole
(0,0) (-24,-1) (-22,50), total area 1 254.Measurements
Node 24, Windows, i7-14700K. "main" is the published 2.0.1-18. Area outside the input is measured with
difference(output, input).unionandPolyTree)uniontime, median of 11The real inputs are unions of many visibility polygons, which produce exactly these thin slivers; the 5 426-ring input was cleaned with
simplifyPaths(…, 0)first.Tests
tests/union-pocket.test.ts: the three triangles, a 3-ring fixture reduced from the real input, and the same fixture throughPolyTree. All three fail on main and pass with this change. Full suite: 340 passed (337 on main).