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

Unexpected spike when offsetting with a Round join type #934

Closed
kintel opened this issue Jan 18, 2025 · 4 comments
Closed

Unexpected spike when offsetting with a Round join type #934

kintel opened this issue Jan 18, 2025 · 4 comments

Comments

@kintel
Copy link

kintel commented Jan 18, 2025

The following test case demonstrates that we get an unexpected spike when offsetting a simple polygon with Round join type.
This used to be a lot worse, which got improved in the main branch recently, but there is still an unexpected spike. See attached SVG output from the test + a zoomed-in screenshot.

Note: With a low enough arc tolerance, this gets smoothed out, but it feels like it should work without having to increase the arc tolerance beyond some limit. While the spike in this case is relatively small, it gets exaggerated greaty when performing subsequent offset operations, so it would be good to eliminate it early.

TEST(Clipper2Tests, TestOffsets13)
{
  Paths64 subject = {
    MakePath({
	863297822,496774708, 642936472,521821078, 640825227,522145885, 640493709,522210310, 314766074,219356059
      })
  };
  double offset = 50000000;
  double miter_limit = 2.0;
  double arc_tol = 25000000;
  const int scale_bits = 27;

  Clipper2Lib::ClipperOffset co(miter_limit, arc_tol);
  co.AddPaths(subject, Clipper2Lib::JoinType::Round, Clipper2Lib::EndType::Polygon);
  Paths64 solution;
  co.Execute(offset, solution);

  SvgWriter svg;
  SvgAddSubject(svg, subject, FillRule::NonZero);
  SvgAddSolution(svg, solution, FillRule::NonZero, false);
  std::string filename = "offset_test.svg";
  SvgSaveToFile(svg, filename, 800, 600, 10);
}

Image

Image
@AngusJohnson
Copy link
Owner

AngusJohnson commented Jan 19, 2025

The following image is a very zoomed in view of your path (with dots at vertices) focusing on the problematic location before offsetting:
Image

As you can see, there are a couple of vertices there that are very close to each other, so close that they are causing this spike as you call it. I guess you could argue that this is a bug, and I guess technically it is since this issue needs better documention, but it's not something I'm planning to fix 😁.

To avoid ever seeing this issue, I'd need to measure the length of every segment before offsetting, and removing these extremely short segments. But that is time costly, and I don't believe this performance hit should be imposed on all offsetting operations. So I'm leaving it up the the library user to clean up their paths before offsetting (see the comment I've copied from SimplifyPaths below).

[The SimplifyPaths] function is strongly recommended following offsetting (ie inflating/shrinking), especially when offsetting paths multiple times. Offsetting often creates tiny segments that don't improve path quality. Further these tiny segments can be at angles that have been affected by integer rounding. While these tiny segments are too small to be noticeable following a single offset procedure, they can degrade both the shape quality and the performance of subsequent offsets.

Edit: And I will note that the extremely high arc_tol value you've chosen doesn't make sense (to me) as to why you're using rounded offsets at all. Though on rereading your OP above I can see that this value is artificially high in order to demonstrate the issue.

Edit2: Try this before offsetting:
subject = SimplifyPaths(subject, offset / 1000);

@kintel
Copy link
Author

kintel commented Jan 19, 2025

Thanks for the answer! I'm already simplifying before offsetting, but finding a good epsilon is hard. It's a good idea to scale the epsilon with offset value though, instead of using a global epsilon; I'll tinker with that for a bit and see if that improves the geometry quality.

@AngusJohnson
Copy link
Owner

AngusJohnson commented Jan 19, 2025

Just to explain what's happening with a little more context, here's what the solution looks like when I remove the final clean-up union operation (that always follows offsetting). This union op removes any regions with Negative fill (ie the unfilled region below)

First, with a normal (ie sensible 🤣) arc tolerance ...

Image

and also with an extreme arc tolerance (that neuters the entire arc except the start and end) ...

Image

You should be able to see that a properly generated arc hides the rough corners produced by the very slight concavity that's found in the original polygon.

@kintel
Copy link
Author

kintel commented Jan 19, 2025

Thanks, that is really helpful in understanding. In my mental model, I somehow thought arc generation happened later, but I see now how concavities can leave behind some edges like this.

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

No branches or pull requests

2 participants