Skip to content
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

Earclip #564

Merged
merged 62 commits into from
Oct 25, 2023
Merged

Earclip #564

merged 62 commits into from
Oct 25, 2023

Conversation

elalish
Copy link
Owner

@elalish elalish commented Sep 29, 2023

Fixes #530
Fixes #535

Well, this turned into a big one. I spent a week or two trying to refactor my monotone triangulator to be more robust given the new test cases, and finally decided it was just too convoluted, mostly because it's a sweep-line approach, which doesn't handle the concept of epsilon perturbations well because there's not a great concept of certainty in a sorted list.

Instead, I went back to the drawing board and remembered years ago when I first wrote this triangulator that I was on the fence between using monotone triangulation or ear clipping. Either way I needed to write my own, since none existed for dealing with epsilon-valid polygons. At the time I went with monotones because it is O(nlogn), while ear clipping is O(n^2). This time, I decided that the simplicity associated with ear clipping makes it worth the extra time, especially since our polygon edges n is generally a lot smaller than our triangles n.

It's taken 2 or 3 weeks to build and robustify an ear clipping algorithm, but I must say that beats the many months worth of effort that went into the monotone triangulator. Ear clipping is also significantly less code, even with all my added precision-related code.

@pca006132 your minimize test case function was invaluable! It cut 5,000 point polygons down to 12 point test cases - you can see I've added quite a few.

Please help me kick the tires on this - the real goal is to get all our OpenSCAD problem models working properly.

@pca006132
Copy link
Collaborator

weird. maybe you can ignore this for now and fix other failures first. hopefully their causes are the same.

@elalish
Copy link
Owner Author

elalish commented Oct 21, 2023

Took a good 20 minutes, but I got all your openscad tests to run, @pca006132. They all appear to have passed for me. How's it looking for you?

I still have some ideas to further improve robustness of this triangulator, but I think it's better than it was before at least.

@pca006132
Copy link
Collaborator

It seems that the CCW check is not enabled when processOverlaps = true (default). I got bad triangulation for

$fn=350; // ignore this since it is just for testing purposes
module hole_plate(size, hole_dm, hole_margin, hole_count = [ 2, 2 ])
{
    difference()
    {
        cube(size);
        abs_margin = hole_margin + hole_dm / 2;
        x_hole_dist = (size.x - 2 * abs_margin) / (hole_count.x - 1);
        y_hole_dist = (size.y - 2 * abs_margin) / (hole_count.y - 1);
        x_values = [abs_margin:x_hole_dist:size.x - abs_margin + 0.1];
        y_values = [abs_margin:y_hole_dist:size.y - abs_margin + 0.1];
        // holes
        for (x = x_values, y = y_values)
            translate([ x, y, -1 ]) cylinder(d = hole_dm, h = size.z + 2);
    }
}
hole_plate([ 100, 50, 5 ], 6, 4);
translate([ 0, 60, 0 ]) hole_plate(size = [ 50, 50, 5 ], hole_dm = 3, hole_margin = 2);
translate([ 60, 60, 0 ]) hole_plate([ 50, 50, 5 ], hole_dm = 5, hole_margin = 5);
translate([ 110, 0, 0 ]) hole_plate([ 100, 50, 5 ], 6, 4);
translate([ 0, -60, 0 ]) hole_plate(size = [ 50, 50, 5 ], hole_dm = 3, hole_margin = 2, hole_count = [ 10, 10 ]);

but it only throws an error when I set processOverlaps = false. Setting this also makes box.scad throws:

Triangulation failed! Precision = 0.000415995
Error in file: /home/pca006132/code/openscad/submodules/manifold/src/polygon/src/polygon.cpp (127): 'std::all_of(triangles.begin(), triangles.end(), [&vertPos, precision](const glm::ivec3 &tri) { return CCW(vertPos[tri[0]], vertPos[tri[1]], vertPos[tri[2]], precision) >= 0; })' is false: triangulation is not entirely CCW!
polys.push_back({
    {-22.7753239, 8.03145695},  //
    {-22.8791199, 8.04628658},  //
    {-22.8791199, 8.0424366},  //
    {-22.8792648, 8.04237938},  //
    {-22.8794041, 8.04239368},  //
    {-22.9786491, 8.05282307},  //
    {-22.9923439, 8.06109047},  //
    {-22.995327, 8.06289101},  //
    {-23.0484314, 8.07047844},  //
    {-23.056942, 8.06713009},  //
    {-23.0641346, 8.06429958},  //
    {-23.0679455, 8.06355},  //
    {-23.0689487, 8.06335258},  //
    {-23.0691357, 8.06331635},  //
    {-23.0723953, 8.06429958},  //
    {-23.1114883, 8.0794878},  //
    {-24.4342442, 8.26848221},  //
    {-24.4342442, 8.2057848},  //
});
show(array([
  [-22.7753239, 8.03145695],
  [-22.8791199, 8.04628658],
  [-22.8791199, 8.0424366],
  [-22.8792648, 8.04237938],
  [-22.8794041, 8.04239368],
  [-22.9786491, 8.05282307],
  [-22.9923439, 8.06109047],
  [-22.995327, 8.06289101],
  [-23.0484314, 8.07047844],
  [-23.056942, 8.06713009],
  [-23.0641346, 8.06429958],
  [-23.0679455, 8.06355],
  [-23.0689487, 8.06335258],
  [-23.0691357, 8.06331635],
  [-23.0723953, 8.06429958],
  [-23.1114883, 8.0794878],
  [-24.4342442, 8.26848221],
  [-24.4342442, 8.2057848],
]))

@elalish
Copy link
Owner Author

elalish commented Oct 22, 2023

Ah, indeed, processOverlaps should be false for tests. Okay, with that change and an abort trap I'm now seeing problems in box.scad. I'll keep digging.

@pca006132
Copy link
Collaborator

I thought that the ccw check is enabled regardless of the value of processOverlaps, because iirc the result may not be manifold if the triangulation is not all ccw?

@elalish
Copy link
Owner Author

elalish commented Oct 22, 2023

No, manifoldness should never be affected by geometrically invalid results. We did have that happen because of a bug in the decimator I just fixed, which is why that had me so concerned.

@elalish
Copy link
Owner Author

elalish commented Oct 22, 2023

By the way, if you happen to find a way to change that openscad test so that it will terminate a given test when the triangulator throws, but still proceed to the next test, that would be very helpful. These shell scripts are making me really appreciate our GTest framework...

@pca006132
Copy link
Collaborator

removing the exit on error flag for bash should do the trick. will change it when I have access to my computer later today

@pca006132
Copy link
Collaborator

i.e.

diff --git a/test b/test
index ec163b2..a6bca8a 100755
--- a/test
+++ b/test
@@ -4,7 +4,7 @@
 #
 # ./bench foo.scad
 #
-set -euo pipefail
+# set -euo pipefail
 
 RUNS=${RUNS:-1}
 export OUTPUT_DIR=${OUTPUT_DIR:-$PWD/out}

@pca006132
Copy link
Collaborator

it seems that the mesh produced is fine even though it throws error when processOverlaps = false.

@elalish
Copy link
Owner Author

elalish commented Oct 23, 2023

it seems that the mesh produced is fine even though it throws error when processOverlaps = false.

As in, when you set processOverlaps = true you get a good result? Yeah, that's why it's the default. We still guarantee the result is manifold, and often the CW triangles are small enough not to be noticeable. Or the errors may be in the interior where they're harder to see. I think we're definitely getting closer now, but that CCW check is still very helpful for testing.

@elalish
Copy link
Owner Author

elalish commented Oct 24, 2023

To your point, I'm now getting only one failure in your openscad-test - box.scad is failing here:

Triangulation failed! Precision = 0.000415995
Error in file: /Users/elalish/Code/openscad/submodules/manifold/src/polygon/src/polygon.cpp (127): 'std::all_of(triangles.begin(), triangles.end(), [&vertPos, precision](const glm::ivec3 &tri) { return CCW(vertPos[tri[0]], vertPos[tri[1]], vertPos[tri[2]], precision) >= 0; })' is false: triangulation is not entirely CCW!
polys.push_back({
    {22.7400723, 6.30392075},  //
    {22.2778511, 8.47861862},  //
    {22.2781029, 8.47949028},  //
    {22.2998829, 8.37691879},  //
    {22.2994041, 8.37721729},  //
    {22.2994061, 8.37720966},  //
    {22.2994461, 8.37701893},  //
    {22.6474857, 6.73952579},  //
    {22.7400723, 6.30392075},  //
});
show(array([
  [22.7400723, 6.30392075],
  [22.2778511, 8.47861862],
  [22.2781029, 8.47949028],
  [22.2998829, 8.37691879],
  [22.2994041, 8.37721729],
  [22.2994061, 8.37720966],
  [22.2994461, 8.37701893],
  [22.6474857, 6.73952579],
  [22.7400723, 6.30392075],
]))

But I verified that this polygon is actually self-intersecting at the specified tolerance (though a bit more and the whole thing is just a degenerate line), so this result is correct. This is consistent with seeing a resulting object that looks right as well.

Do you have any other OpenSCAD tests you want to verify this triangulator on? If you think it's better than our current triangulator, then I think I'm ready to merge it.

@pca006132
Copy link
Collaborator

I think we can merge this now. This is definitely more robust!

};

TEST(Polygon, Eberly) {
Polygons polys;
Copy link
Owner Author

Choose a reason for hiding this comment

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

Long time, no see @davideberly! I've extended your earclipping algorithm to handle ε-valid polygons (polygons that may self-intersect, but for which a perturbation of each vertex by less than ε exists for which the polygon does not self-intersect). It's taken a lot of work, but I'm pleased with the results.

In the process I found a small error in your paper. It turns out Mark Spoor was wrong and your original minimize-the-angle-approach is correct. Here is a counter-example polygon for finding a mutually visible vertex.

Choose a reason for hiding this comment

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

Thanks for letting me know. I am in the process of dealing with another issue for the ear-clipping algorithm and will resolve that soon I hope. I will restore the previous algorithm.

@elalish
Copy link
Owner Author

elalish commented Oct 25, 2023

@pca006132 Sorry, I did another significant refactor - I promise this is the last! I believe it's significantly more robust now and there are no disabled tests anymore, as well as several new ones added. If you wouldn't mind trying it out on whatever OpenSCAD models we had before that were failing, I'd appreciate it. Of course the reduced polygon test cases all pass, but those reductions doesn't really apply anymore now that the algorithm has changed.

@pca006132
Copy link
Collaborator

This is nice. I don't have more openscad models to check, I think it would be fine if the models I mentioned previously works.

@elalish elalish merged commit 6218356 into master Oct 25, 2023
18 checks passed
@elalish elalish deleted the earclip branch October 25, 2023 15:16
@elalish elalish mentioned this pull request Nov 3, 2023
cartesian-theatrics pushed a commit to SovereignShop/manifold that referenced this pull request Mar 11, 2024
* earclip compiles

* some tests passing

* 46 tests passing

* refactor

* cleanup

* new cost

* cleanup

* fixed TouchingHole

* more fixes

* only 5 tests failing

* fixed short sides

* fixed Sponge2

* better cost

* improved keyholing

* Moved Rect to public

* added top/bottom check

* fixed bBox

* Polygon tests pass

* fixed Sponge3a

* another fix, another test

* keyhole refactor

* Added ClipDegenerates

* Refactor FindStarts

* fixed bugs

* fixed Sponge4

* refactor clipped

* another bug, another test

* another bug, another test

* Sponge4 passing

* all tests passing

* removed old triangulator

* fix isfinite

* debug checks

* remove 2-manifold check

* added comments

* fixed missing point bug

* another test, another fix

* two tests added and fixed

* fix build

* fixed CoplanarProp

* fixed seg fault

* reenable 2-manifold check

* refactor for polygon area

* factored out loop

* tests passing

* another test, another fix

* another test, another fix

* fixed dedupe segfault

* ignore duplicated verts

* re-enabled turn180

* re-enabled duplicate tests

* another test, another fix

* refactor keyholing

* more precise area computation

* disabled a test

* added test

* another test, another test

* fixed bridge bound

* fewer short edges

* replaced intersection sorting with InsideEdge

* another test, another fix

* allow a few degenerates in Sponge4
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.

triangulator bugs related to #528 Refactor triangulator
3 participants