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

[FEAT] Add Sweep Operation #636

Closed
wants to merge 3 commits into from
Closed

[FEAT] Add Sweep Operation #636

wants to merge 3 commits into from

Conversation

zalo
Copy link
Contributor

@zalo zalo commented Dec 5, 2023

This PR is an attempt to break out a successful experiment from Python.
#192 (comment)

  • Does this make sense to move to Impl with parallel for-loops?
  • Should I add full transform (translation/rotation/scale) variant with a $fn for more interesting sweep trajectories?
  • Maybe Catmull-Rom Spline Interpolation between Segments? (Could also be left to the user)

This PR is an attempt to break out a successful experiment from Python.

Does this make sense to move to Impl?
Copy link

codecov bot commented Dec 5, 2023

Codecov Report

Attention: 14 lines in your changes are missing coverage. Please review.

Comparison is base (638bada) 91.40% compared to head (007acd9) 91.36%.
Report is 1 commits behind head on master.

❗ Current head 007acd9 differs from pull request most recent head e78c21a. Consider uploading reports for the commit e78c21a to get more accurate results

Files Patch % Lines
src/manifold/src/manifold.cpp 22.22% 14 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #636      +/-   ##
==========================================
- Coverage   91.40%   91.36%   -0.05%     
==========================================
  Files          35       36       +1     
  Lines        4514     4700     +186     
==========================================
+ Hits         4126     4294     +168     
- Misses        388      406      +18     

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

@elalish
Copy link
Owner

elalish commented Dec 5, 2023

Hmm, I've never head of an operation like this - how would you use it in practice? Might be nice to add a pretty Sample demonstrating its usefulness.

When I think of Sweep, I think of taking a CrossSection and extruding it along a parameterized path. We have talked about adding this operation, which I think would be quite useful.

@zalo
Copy link
Contributor Author

zalo commented Dec 5, 2023

I think Swept profiles are called "Pipe"s in CAD; having a sweep on the CrossSection would be fun too.

Swept Volumes aren't used suuper often, but there also aren't many libraries that can do them (I think Siemens NX is the only CAD package with it).
In modelling, I believe they're typically used for cutting out the profile of one body against another that needs to slide or rotate near it.

Like, cutting funny shaped gears:
https://www.shadertoy.com/view/ldsBRr
Or the interesting animations shown in the last example here:
https://www.youtube.com/watch?v=6iLqMQ3kd24

Or, sometimes it's used as shorthand for the hull operation with spheres for making capsules.

It seems like the interesting stuff requires full transform support, so I'll see about adding that...

Sweeps are just the minkowski sums of shapes with lines or paths, so that would still be the ideal 😅

@elalish
Copy link
Owner

elalish commented Dec 6, 2023

Based on your examples, it sounds like another way to achieve this is through #426 - particularly if we support both of OpenSCAD's options. For a sliding part, you could extrude the projection with cut = false. This feels like a more intuitive and flexible API to me, which is part of why I think this is a rare function.

I'm thinking we'd probably make this two functions: Project() and Slice(float height), since they'd be implemented differently anyway. Would you be interested in implementing Project()? I think it would be just a 2D convex hull.

@pca006132
Copy link
Collaborator

But if project is implemented as 2D convex hull, it will not work for 2D shape that is not convex, and what about things like a torus that has a hole inside?

@elalish
Copy link
Owner

elalish commented Dec 6, 2023

Oh right, I didn't think about that long enough. Hmm, I know how to do a slice, but what's the algorithm for a projection?

@pca006132
Copy link
Collaborator

I think openscad just project every triangle into 2D and union them.

@elalish
Copy link
Owner

elalish commented Dec 6, 2023

@zalo I don't mean to be discouraging - that video is cool. Still, for n triangles, we're talking n convex hulls all unioned together? And that only gets us one step in the sweeps they're talking about, and doesn't even include rotation. How computationally feasible is this algorithm?

@pca006132
Copy link
Collaborator

I feel that the idea of using 2D projection for sweep, union that with the initial and final position should be faster.

@zalo
Copy link
Contributor Author

zalo commented Dec 6, 2023

2D projection + Extrude won’t work, see linked comment gif for counter example shape:
#192 (comment)
(Also, CrossSections lose 3D information?)

Also, I’m not familiar enough with Manifold’s internals to implement project and slice operators efficiently 😅
I’d probably try to do slicing by splitting by a plane, and then deleting all the vertices that are collocated with the original part, and projection by unioning the triangles in 2D as @pca006132 said…

Like Minkowski Sums, this might be part of a class of more expensive mesh algorithms…
Winding Number fills, Morphological Dilation/Erosion/Opening/Closing, Retopologizing, etc.

Though (save for the morphological ops), they tend to veer away from the “Exactish-CSG” spirit of the library (“does simple things fast”?), so I’d understand if you’d rather tell users to use a different library, or write their own extension kernels in the client bindings languages…

@pca006132
Copy link
Collaborator

@zalo I am deep inside a debug session of something else, will response later tmr about the slice thing. I think slower operation is fine, we just want to make sure we are not missing some obvious better implementation.

@pca006132
Copy link
Collaborator

Project: Just iterate over the halfedges (https://github.com/elalish/manifold/blob/master/src/manifold/src/impl.h#L50) to get the triangles, maybe flip them so they are in the correct winding direction.

Slicing: Also iterate over those triangles, get the ones that intersect with the plane? I was thinking about using the halfedge connection for recovering the winding direction, get the next triangle, convert it into intersection line, add it to the polygon, and repeat... Something like that (hand waiving here)

@pca006132
Copy link
Collaborator

And I see why this sweep is not that simple now. Thanks.

@pca006132
Copy link
Collaborator

Thinking about it, I think we can have a fast and simple algorithm for this.

Say the direction is v, for every face we compute the dot product of its normal with v, positive means that we should offset the face, and negative means that it should stay. To avoid self-intersection, we use the collider to compute if the swept volume of a face (positive dot product) will intersect with another face, mark them and do not move them. After moving the faces, we stitch the gaps by checking the neighbors of the moved faces. Because the marked faces have a positive dot product with v, we can compute the sweep result of each connected component of marked faces and union with the previous result.

Just thought of this, not sure if it makes sense.

@elalish
Copy link
Owner

elalish commented Dec 6, 2023

@pca006132 I came to a similar conclusion about slice and project, though I believe with project you only need to project edges that are between triangles with normals that point up and those that point down. Those should already assemble into closed (self-intersecting) polygons, which we can then run through CrossSection to get the strictly positive winding number part.

Do you want to attack those or should I? I also have an idea for offset I want to try out.

@elalish
Copy link
Owner

elalish commented Dec 6, 2023

@zalo Yeah, this particular op feels like a combination of too specialized and not really any slower to implement outside of the core library. I think a polygon-sweep-along-path op will be more generally useful, but I want to achieve this via #289, because then it will be very fast and that will also solve a number of other important problems.

@zalo
Copy link
Contributor Author

zalo commented Dec 6, 2023

Thinking about it, I think we can have a fast and simple algorithm for this.
Because the marked faces have a positive dot product with v, we can compute the sweep result of each connected component of marked faces and union with the previous result.

Just thought of this, not sure if it makes sense.

This actually sounds pretty good (though, I’m not sure it’ll work for the more general Translation+Rotation+Scale Matrix Sweep…)

My mesh-fu is a little weak… if we delete the backwards triangles, and isolate just the connected component regions, is there a straight-forward way to step along the component boundary?

I guess the boundary edges can be marked and remembered during the “backwards triangle deleting” process… That could work! I’ll think about this a little more…

@zalo
Copy link
Contributor Author

zalo commented Dec 6, 2023

Oh, there's a couple more use-cases for sweeps that might be really fun:

@elalish
Copy link
Owner

elalish commented Dec 7, 2023

From these examples it seems clear that to be useful, this object-sweep needs to handle rotation as well as translation. @pca006132 I think that means you'll have another category of triangles: those who have some verts with a positive motion dot product, and some with negative. Sounds tricky, but there might be something there.

Still, those tool-path problems look like convex solids: for those you can just hull for each step and then union the steps together to make a sweep. That should be doable without any new API.

@pca006132
Copy link
Collaborator

those who have some verts with a positive motion dot product, and some with negative

you duplicate the verts and stitch them back later

but yeah, rotation is hard...

@pca006132
Copy link
Collaborator

Do you want to attack those or should I? I also have an idea for offset I want to try out.

You can do it. I am working on other stuff right now and don't have enough time for this.

@pca006132
Copy link
Collaborator

pca006132 commented Dec 7, 2023

I feel like an efficient implementation of sweep may be used to implement offset as well, for small $fn. sorry after thinking about it it does not really work.

@zalo
Copy link
Contributor Author

zalo commented Dec 7, 2023

Alright; I'll close for now since the 90% use-case is on Convex Tools/Solids, which can just be Hulled.

@zalo zalo closed this Dec 7, 2023
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.

3 participants