8000 Fix triangulator handling of splinters by elalish · Pull Request #1205 · elalish/manifold · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Fix triangulator handling of splinters #1205

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

Merged
merged 7 commits into from
Mar 31, 2025
Merged

Fix triangulator handling of splinters #1205

merged 7 commits into from
Mar 31, 2025

Conversation

elalish
Copy link
Owner
@elalish elalish commented Mar 29, 2025

Fixes #970

First is just the tests - the polygon is epsilon-valid, but fails to properly triangulate because it doesn't clip the splinters. I also added @pca006132's original test that produced the problem polygon, but that passes on my machine - perhaps simplification changes improved this? I'm pushing it here to see if some other arch has trouble with it.

Okay, I think I have it fixed now, and the algorithm simplified to boot.

And we no longer have any disabled tests!

@elalish elalish self-assigned this Mar 29, 2025
return true;
}
return left->InsideEdge(left->right, epsilon, true);
return CCW(left->pos, pos, right->pos, epsilon) >= 0;
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is interesting - I had been operating under the assumption that I needed to effectively determine a correct perturbation of the epsilon-valid polygon to make it truly valid (non-self-intersecting). This was in fact necessary for my previous triangulator, but since ear clipping proceeds in a naturally manifold way, I tried just letting it clip all degenerate ears. It seems to work great, and vastly simplifies the code. I think the keyholing step may be the only part that's really sensitive to this kind of thing.

Copy link
codecov bot commented Mar 30, 2025

Codecov Report

Attention: Patch coverage is 15.00000% with 17 lines in your changes missing coverage. Please review.

Project coverage is 92.19%. Comparing base (4fabfc3) to head (5c11993).
Report is 2 commits behind head on master.

Files with missing lines Patch % Lines
src/properties.cpp 0.00% 17 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1205      +/-   ##
==========================================
- Coverage   92.23%   92.19%   -0.04%     
==========================================
  Files          31       31              
  Lines        5988     5985       -3     
==========================================
- Hits         5523     5518       -5     
- Misses        465      467       +2     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@elalish elalish requested a review from pca006132 March 30, 2025 01:35
@@ -215,46 +215,47 @@ std::mutex dump_lock;
* Note that this is not checking for epsilon-validity.
*/
bool Manifold::Impl::IsSelfIntersecting() const {
const double epsilonSq = epsilon_ * epsilon_;
const double ep = 2 * epsilon_;
Copy link
Owner Author
@elalish elalish Mar 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the main change to this function - in line with our degenerate checks and such, we increase epsilon to account for error within these checks themselves. The rest of this function is just renames because I was confused by x and y not being directions.

Manifold result = a + b;
EXPECT_EQ(result.Status(), Manifold::Error::NoError);
ManifoldParams().selfIntersectionChecks = selfIntersectionChecks;
}

TEST(BooleanComplex, DISABLED_OffsetSelfIntersect) {
TEST(BooleanComplex, OffsetSelfIntersect) {
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These disabled tests are now fixed!

@@ -786,7 +771,8 @@ class EarClip {
above * CCW(start->pos, vert->pos, connector->pos, epsilon_);
if (vert->pos.x > start->pos.x - epsilon_ &&
vert->pos.y * above > start->pos.y * above - epsilon_ &&
(inside > 0 || (inside == 0 && vert->pos.x < connector->pos.x)) &&
(inside > 0 || (inside == 0 && vert->pos.x < connector->pos.x &&
vert->pos.y * above < connector->pos.y * above)) &&
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's the second fix - for the disabled sponge test.

@pca006132
Copy link
Collaborator
pca006132 commented Mar 30, 2025

Interesting. I think you can try to run the fuzz test. It should report cases that are causing errors in either self-intersection check or triangulation. Let me know if you have trouble running the fuzz test.

Btw we don't need to run the script for the fuzz test now, just compile it with fuzzing enabled and run

$ ./test/manifold_fuzz --fuzz=ManifoldFuzz.SimpleCube

@pca006132
Copy link
Collaborator

Got a failure case:

TEST(ManifoldFuzz, SimpleCubeRegression) {
  SimpleCube(
    {CubeOp{{Transform{static_cast<TransformType>(1), {-0.22291143964604709, 0.10000000000000001, -0.15746689630359223}}}, true}, CubeOp{{Transform{static_cast<TransformType>(1), {-0.23746613978666686, 0.10000000000000001, -0.10000000039702957}}}, false}, CubeOp{{}, true}, CubeOp{{Transform{static_cast<TransformType>(1), {-0.10000000000000001, 0.10131119384128703, 0.10000000000000028}}}, true}, CubeOp{{Transform{static_cast<TransformType>(2), {0.10000000000000001, 0.10000000001019704, -0.10000000000000001}}}, true}, CubeOp{{Transform{static_cast<TransformType>(1), {0.10000000097050545, 0.1000000000000008, 0.10000000000000001}}}, false}, CubeOp{{}, true}}
  );
}

will translate it into something readable later...

@elalish
Copy link
Owner Author
elalish commented Mar 31, 2025

No, I can't build with the fuzzer - first I needed to change the clang check to if(NOT CMAKE_CXX_COMPILER_ID MATCHES "(C|c?)lang"), and python stubgen had some error, so I had to turn off pybind. Now I'm down to polygon_fuzz.cpp and it says gettid() isn't defined and it doesn't like the pthread_cancel call either.

I'm going to go ahead and merge this and we can mess with the fuzzer as a follow-on.

@elalish elalish merged commit 00a589d into master Mar 31, 2025
27 checks passed
@elalish elalish deleted the testing branch March 31, 2025 09:04
@starseeker
Copy link
Contributor

@elalish Sounds like we might be able to try removing the interior Status() checks on the SimpleOffset test case now as well?

@elalish
Copy link
Owner Author
elalish commented Mar 31, 2025

@starseeker sure - care to make a PR?

starseeker added a commit to starseeker/manifold that referenced this pull request Mar 31, 2025
elalish pushed a commit that referenced this pull request Apr 1, 2025
)

In-loop Status calls were a hacky work-around - see if #1205 had cleared the issue.
@elalish elalish mentioned this pull request May 14, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Potential boolean bug
3 participants
0