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() and test it extensively #1214

Merged
merged 30 commits into from
Apr 3, 2020
Merged

Fix simplify() and test it extensively #1214

merged 30 commits into from
Apr 3, 2020

Conversation

BagelOrb
Copy link
Contributor

@BagelOrb BagelOrb commented Mar 7, 2020

Better tests for the simplify() function.

It is a very important function and the tests were flawed.

EDIT:
The tests proved that something was wrong with the function, and something was wrong with earlier tests.

Therefore I had to rewrite and fix simplify()

Thanks to @smartavionics for laying down the ground work for this PR.

@BagelOrb BagelOrb mentioned this pull request Mar 7, 2020
The previous test only considered single corners, while simplify() works cumulatively.
Even though one corner might not simplify based on its current geometry, it might be simplified if the previous corner was simplified.
This seems to be related to the fact that the spiral has self-intersectiong geometry.
The simplification of more segments can result in a smaller error because the spiral loops back unto itself.

In the new test the whole spiral is considered to be a region of small errors and the whole thing is simplifiable,
except that we added unsimplifiable geometry in order to prevent the collapse of the whole region,
which was another problem of the original test
@BagelOrb
Copy link
Contributor Author

BagelOrb commented Mar 7, 2020

New circle test:
Starts out really smooth in the East and becomes every rougher in the clockwise direction.
Before simplify: black, after: red
image

@BagelOrb
Copy link
Contributor Author

BagelOrb commented Mar 7, 2020

Fixed spiral test:
added far away geometry to force the algorithm to start at a specific location with simplification
black: original polygon, red: FAILING test
The inner point of the spiral has to be in the output.

image

@BagelOrb
Copy link
Contributor Author

BagelOrb commented Mar 7, 2020

Sinusoidal test:
Checks whether sinusoidal errors get simplified.
Black: original, red: simplified
Note that there is an error because the long segments on the right is simplified, but this test doesn't check for that.
image

… simplify()

simplify() is not precise and might not optimally simplify

We therefore allow for twice the optimal amount of segments in the output
due to intermediate simplification, the total error should be as large as the diameter of the spiral, not the radius.
…a vertex

within the loop it was only checking the previous segment,
at the end of the loop it was requiring either segment instead of both
@BagelOrb
Copy link
Contributor Author

BagelOrb commented Mar 7, 2020

Extreme high poly test.
Segments are approx 3 micron
image

@BagelOrb
Copy link
Contributor Author

BagelOrb commented Mar 7, 2020

This PR now also includes a fix for the changed long line segment problems shown in figure 2 and 3 of this thread.

@BagelOrb BagelOrb changed the title test simplify Fix simplify() and test it extensively Mar 7, 2020
captures the intent of the original spiral test called simplifyLimitedError
the Y component might be quite high, so we introduce points in between two consecutive X values with an intermediate Y

also we add some arbitrary long segments in there to test robustness against a mix of high poly and low poly
for debugging purposes
@BagelOrb
Copy link
Contributor Author

BagelOrb commented Mar 9, 2020

Zig zag test which captures the intent of the original spiral test.
This test proved the implementation was faulty. It used to simplify half of the bends it should have before I fixed it.
image

If consecutive points are considered the colinearity is a moving target and
the removal escalates and generates rough gcode when the input model is
extremely high poly.
The function loops over as many points as size(), so we don't need to handle the first point outside of the loop.

The code for checking colinearity was buggy and could escalate.
Now we simply interpret the function requirements as stating that the smallest_line_segment criterion
ca be ignored if the local deviation is smaller than 5 micron.
We therefore use the same vertex removing code for those small errors as for larger errors.
whole polygons were removed when simplify could remove all first vertices until it got to the last one.
When it checked whether it could remove the last one it checked a point against itself, which
meant that the area was always zero and so it would always also remove the last 2 vertices as well.
The testing criteria didn't correspond to what the documentation requires of the function.
The test case used to depend too much on implementation detail.
removes duplicate code
should always execute the same
test which a degenerate vertex which lies far away from the polygon, but which
doesn't alter the area of the polygon, gets removed.
@BagelOrb
Copy link
Contributor Author

BagelOrb commented Mar 10, 2020

I've completed all my tests and verified that all automated tests succeed and that the 4 linked issues are solved by this PR as well.

JIRA ticket CURA-7243 (no github issue)
Ultimaker/Cura#6604 (JIRA: CURA-6955)
#1151
Ultimaker/Cura#6215

sine_polygons.simplify(length / 2, 2 * deviation);
if (visualize)
{
SVG svg("output/simplifySineLimitedError.svg", AABB(sine_before));
Copy link
Member

Choose a reason for hiding this comment

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

There is a lot of code duplication with writing these svg files. I think we should convert it into a re-usable function

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Well. It's not like there's any complexity in the code. Nothing can go wrong. It would maybe be a bit cleaner.. Sure,

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, it's a bit nitpicking, but I like my code to be clean :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The conceptual load of reading the current code is no more, or perhaps even less than reading a new function which fits on one line. I don't think the physical length of the code should be a criterion to decide on code-cleanliness.

I think it is nitpicking. Let's not discuss. I would be okay with you fixing this, but I am overburdened atm.

* 2. Never remove a vertex if the distance between that vertex and the final resulting polygon would be higher than \p allowed_error_distance
* 3. Simplify uses a heuristic and doesn't neccesarily remove all removable vertices under the above criteria.
* 4. But simplify may never violate these criteria.
* 5. Unless the segments or the distance is smaller than the rounding error of 5 micron
Copy link
Member

Choose a reason for hiding this comment

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

This confuses me. Does it refer to 4? (if so, it should be a part of 4) If not, I don't understand what it does refer to.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes it refers to 4.

svg.nextLayer();
svg.writePolygon(zigzag, SVG::Color::RED);
}
EXPECT_THAT(zigzag.size(), testing::Eq(zigzag_before.size() - simplifiable_bend_count)) << "Should simplify bends with height 100 up to 500";
Copy link
Member

Choose a reason for hiding this comment

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

That seems a bit light of a test, doesn't it? It's now only checking if the number of polygons is correct. Should there also not be some sort of test that actually checks if the error isn't too great?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No. It's not checking zigzag_polygons, it's checking the one polygon zigzag.

Actually this test case is testing the most important thing atm. It only succeeds if exactly the number of bends which we expect to be dsimplified out actually do. No more and no less.

@nallath
Copy link
Member

nallath commented Mar 30, 2020

Rounded corners do get worse though (it's more noticeable if you switch between the two images in image viewer):

Before:
ClosupWithoutChanges

After:
ClosupWithChanges

You can see it at the entire model as well:
Before
WithoutChangesToEngine
After
WithChangesToEngine

@BagelOrb
Copy link
Contributor Author

Rounded corners do get worse though

Yes. That's the whole goal of simplify().

@BagelOrb
Copy link
Contributor Author

Did you check the frontend PR for this PR??

The new simplify simplifies twice as hard as the old one, so I made a frontend PR to update all simplify settings to be halved. This also includes an upgrade system to update old profiles to new profiles with half the value.

@Ghostkeeper
Copy link
Collaborator

I've tested the basic functionality with a variety of organic and non-organic-but-rounded shapes and found no immediately visible concerns. The maximum resolution seems to be held in honour unless the maximum deviation is in concern. The maximum deviation seems to always be held in honour except for a minimum of 5 micrometres. Perhaps we want to set the minimum for Maximum Deviation in the front-end to 0.005mm?

I have a cylinder with 100,000 sides with a radius of 10. (this one). That results in side lengths of 0.000314mm, or approximately 0.3 micrometres, so basically just multiple vertices on every integer coordinate possible along the circle. I sliced this using a maximum resolution and maximum deviation of 0.005mm, so that the exception for "pretty much straight" doesn't get in the way. Let's take an inset of 0.175mm into account for the outer wall line width. You'd expect then that a maximum deviation of 0.005mm can lead to side lengths of up to 2 * sin(acos((9.825 - 0.005) / 9.825)) * 9.825 = 0.626817mm

Here is a snippet of g-code that I took from around the middle of the height of the cylinder (since each face is also divided diagonally into 2 triangles).

G1 X122.701 Y114.697 E88.99405
G1 X122.292 Y115.03 E88.99653
G1 X121.845 Y115.355 E88.99913
G1 X121.408 Y115.634 E89.00157
G1 X120.938 Y115.899 E89.00411
G1 X120.497 Y116.118 E89.00642

I'm seeing distances here in the ballpark of 0.5mm, which is a bit lower than I'd expect but in the correct order of magnitude.

I've also tested whether seams get removed from straight bits, which seems to work fine.

I've also sliced this model with new and old (skin and infill removed to reduce noise from things that aren't simplified):
image
The new algorithm has a deviation of 0.025mm rather than 0.05mm, but the resolution is the same. The old file was 12.0MB. The new g-code file is 10.5MB. This suggests that in this case the simplify algorithm removed a bit more, even with the lower deviation setting.

It's not entirely immune against leaving in vertices in a straight line. Here's an example where it chose the wrong vertices to remove. It chose the diagonals of a flat face. Parameters are increased to make it visible:
image
However the criterion that it needs to remove the best vertex is not in the scope of this algorithm so I think this should be accepted.

Bug CURA-7243 seems to be fixed.
Bug CURA-6729 / Ultimaker/Cura#6215 seems to be fixed.
Bug CURA-6933 / #1151 seems to be fixed.
Bug CURA-6955 / Ultimaker/Cura#6604 seems to be fixed.

In general it's pretty hard to find faults with this.

@BagelOrb
Copy link
Contributor Author

Did you guys look at the frontend changes? Ultimaker/Cura#7247

Im not sure whether it's easy to port these changes to a new version upgrade system for 4.7 instead of 4.6

@Ghostkeeper
Copy link
Collaborator

Ghostkeeper commented Apr 1, 2020

Yes, I did. Like I said in my comparison:

The new algorithm has a deviation of 0.025mm rather than 0.05mm, but the resolution is the same.

I didn't apply the version upgrade in my tests, but I did use the new defaults to compare.

To change the front-end for 4.7, we'd need to change the setting version there to 13 and rename the plug-in. It's not so hard. It does change those 1500 profiles again though.

@BagelOrb
Copy link
Contributor Author

BagelOrb commented Apr 1, 2020

Ah ok. Good!

@jornada812
Copy link

I upgraded to 4.8 and re-sliced my model. It looks like the simplification changes were not made as planned by the developer. It got worse https://yadi.sk/i/oyUH92ART1Pteg

@nallath
Copy link
Member

nallath commented Jan 25, 2021

I upgraded to 4.8 and re-sliced my model. It looks like the simplification changes were not made as planned by the developer. It got worse https://yadi.sk/i/oyUH92ART1Pteg

That looks an awefull lot like coasting. Try disabling that. If that doesn't work, please create a new issue.

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