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

Fix simplify leaving way too small line segments. #1321

Merged
merged 15 commits into from Sep 17, 2020

Conversation

nallath
Copy link
Member

@nallath nallath commented Sep 10, 2020

Ultimaker/Cura#8321

I've run some tests with this and it seems to resolve the issue reported.

MezzerSealWithFix

Line length Num segments
1 60544
0.5 6256
0.1 686
0.05 32
0.01 32
0.005 32
0.001 32

MezzerSealWithoutFix

Line length Num segments
1 92846
0.5 33547
0.1 5732
0.05 2359
0.01 719
0.005 579
0.001 0

BenchyWithFix

Line length Num segments
1 120345
0.5 46781
0.1 2046
0.05 834
0.01 237
0.005 124
0.001 31

BenchyWithoutFix

Line length Num segments
1 124375
0.5 50275
0.1 4015
0.05 2429
0.01 1596
0.005 1390
0.001 27

Benchy before:
BenchyBowBeforeFix

Benchy after:
BenchyBowAfterFix

CURA-7709

@BagelOrb
Copy link
Contributor

The currently proposed code change would violate the criteria described in the documentation of this function:
"Never remove a vertex if either of the connceted segments is larger than \p smallest_line_segment"

I see that where this function is called the two parameters smallest_line_segment and allowed_error_distance are both set to meshfix_maximum_resolution, while in the default parameters of the function signature the former is twice the latter.

Perhaps we should increase the smallest_line_segment to twice the meshfix_maximum_resolution.

Or perhaps we should simply bump up the Max Resolution setting for these printers. Note that the old setting values for that setting were tweaked for a broken implementation of simplify() which removed too much, so it makes sense that now that it is working again these printer definitions will have a Max Resolution setting which doesn't remove enough.

@BagelOrb
Copy link
Contributor

Please keep me in the loop.

Copy link
Contributor

@BagelOrb BagelOrb left a comment

Choose a reason for hiding this comment

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

I really like this approach and I think it can be made to work!

However, this function has been revised a gazillion times, so we should be super careful and have a really keen eye. I therefore tried to think through all of the steps and have added detailed comments.

Please update code and/or comments/documentation where needed.

src/utils/polygon.cpp Outdated Show resolved Hide resolved
src/utils/polygon.cpp Outdated Show resolved Hide resolved
src/utils/polygon.cpp Outdated Show resolved Hide resolved
src/utils/polygon.cpp Outdated Show resolved Hide resolved
src/utils/polygon.cpp Show resolved Hide resolved
if(vSize2(current - intersection_point) > allowed_error_distance_squared)
{
// The intersection is way to far away. Drop it.
continue;
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure this should be a continue. Note that this code is in the case where the next segment is too long to be able to remove the vertex according to criterium 1.

image
black = original; red = simplified according to this line.

Copy link
Member Author

Choose a reason for hiding this comment

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

The distance is only too long if the lines are almost paralel. At that point, it will create an intersection that is very far away, which results in weird ass spikes being put there.

Copy link
Contributor

Choose a reason for hiding this comment

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

There are 3 alternatives: either we A) remove the current vertex, or B) we remove the current and last vertex and use the intersection point, or we C) leave both in. In this if-case you opt for A, but I am saying we should opt for C. You misinterpreted my comment to mean that we should opt for B.

Copy link
Contributor

Choose a reason for hiding this comment

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

Possible 4th alternative: cut a long segment so that the shortcut is as long as the allowed error distance (or smallest segment length ?)
image

Copy link
Member Author

Choose a reason for hiding this comment

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

Current solution (A):
CurrentSolution
Leave both in (C):
KeepBoth

Option C is retaining some of the weird points. I also think that my previous assessment is incorrect; It happens to be wrong if both of the lines are almost in the same direction; It's probably because of a rounding error that the found point is very far away.

Copy link
Member Author

Choose a reason for hiding this comment

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

As discussed with @Ghostkeeper, we've opted to go for option E.

It's basically option C, but when the line segment is below 5 mu, we still discard it.

Copy link
Contributor

Choose a reason for hiding this comment

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

This whole function was built to remove line segments below 5mu, but to do it in a safe way. If you choose option E then we lose the safety this function should guarantee. We might introduce escalation problems again where a lot of consecutive 5mu line segments get removed and introduce a too big error.

src/utils/polygon.cpp Outdated Show resolved Hide resolved
@nallath nallath force-pushed the CURA-7709_simply_leaves_tiny_segments branch from 512aa10 to b5996eb Compare September 11, 2020 12:34
@BagelOrb
Copy link
Contributor

The function documentation should probably change, because the following would be unsimplifiable according to spec:

""""""""""""""""""""""""""""""""i,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,

src/utils/polygon.cpp Outdated Show resolved Hide resolved
src/utils/polygon.cpp Outdated Show resolved Hide resolved
src/utils/polygon.cpp Outdated Show resolved Hide resolved
src/utils/polygon.h Show resolved Hide resolved
* Removes verts which detour from a direct line from the previous and next vert by a too small amount.
* 1. Removes verts which are connected to line segments which are too small.
* 2. Removes verts which detour from a direct line from the previous and next vert by a too small amount.
* 3. Moves a vert when a small line segment is connected to a much longer one. in order to maintain the outline of the object.
Copy link
Contributor

Choose a reason for hiding this comment

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

I think you should call it 'merging the 2 verts' rather than 'moving one vert'.

Copy link
Member Author

Choose a reason for hiding this comment

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

It's not quite merging them. I would interpret merging as averaging the two points.

Copy link
Contributor

@BagelOrb BagelOrb Sep 16, 2020

Choose a reason for hiding this comment

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

Yeah, but it's even less 'moving one point'. Maybe just 'dissolving the middle segment'?

{
if(length2 < 25)
{
// We're allowed to always delete segments of less than 5 micron.
Copy link
Contributor

@BagelOrb BagelOrb Sep 16, 2020

Choose a reason for hiding this comment

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

Wait no this is dangerous! The whole idea of this function was to avoid escalating segment removal. The default argument allowed_error_distance is already 5mu; this whole function was built to be able to remove 5mu segments, but do it in a safe way.

The if-case and continue here should be removed.

Copy link
Member Author

@nallath nallath Sep 16, 2020

Choose a reason for hiding this comment

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

It only does it if the intersection fails. The intersection only fails if the direction of the two lines is almost exactly the same.

So it's dangerous if you don't take it into context.

The alternative is that I simply always delete the point, regardless if the length is <5mu, which is the solution I had before this.

If we don't delete these tiny points, we still get these very tiny line segments in some spots, which wreak havoc on a print.

Also note that it was (and still is) part of the criteria:
Unless the segments or the distance is smaller than the rounding error of 5 micron

Copy link
Contributor

@BagelOrb BagelOrb Sep 16, 2020

Choose a reason for hiding this comment

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

The intersection only fails if the direction of the previous and next line is almost exactly the same, but not the same as the current line segment, so it's not like the collinearity means that this case can be ignored.

The alternative is that I simply always delete the point, regardless if the length is <5mu, which is the solution I had before this.

The alternative is option C or D, not option B. That is: either leave the small segment in, or move either of the verts of the current segment along the long segment, such that the current segment becomes as long as smallest_line_segment.

If we don't delete these tiny points, we still get these very tiny line segments in some spots, which wreak havoc on a print.

That may be the case for some printers, but I'd like to challenge the idea that this is inherently a problem for any firmware. Only an abundance of tiny line segments should wreak havoc, but as long as they are connected to a long segment it shouldn't cause buffer underruns and so it shouldn't be a problem.

I don't think simplify should be made to guarantee that no small line segments exist. The function is called all over the place in the engine; if it's a gcode problem then maybe we need a separate filtering step at the gcode generation phase. For example, alter LayerPlan::addExtrusionMove to only do anything if the line segment is long enough.

Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm. Actually...

We are in the case where if (next_length2 > smallest_line_segment_squared) so there is no escalation problem.

Still I think it's better to adhere to the criterion that no segment longer than shortest_line_segment may have it's direction altered, which is possible with option C or D.

Copy link
Contributor

Choose a reason for hiding this comment

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

In a live talk we decided that the effort and code complexity wasnt worth it.

src/utils/polygon.cpp Outdated Show resolved Hide resolved
Copy link
Contributor

@BagelOrb BagelOrb left a comment

Choose a reason for hiding this comment

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

I'm not sure whether it's safe to throw away 5mu segments just like that.

@rburema rburema merged commit 90222b2 into master Sep 17, 2020
@rburema rburema deleted the CURA-7709_simply_leaves_tiny_segments branch September 17, 2020 13:12
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.

None yet

4 participants