nicklockwood / Euclid

A Swift library for creating and manipulating 3D geometry
MIT License
644 stars 53 forks source link

Support for partial lathes (and by extension partial cylinders) #126

Open Kolpa opened 3 months ago

Kolpa commented 3 months ago

I've run into some situations now where i would benefit from the ability to only partially complete a lathes rotation giving us the ability to easily create partial cylinders and other more complex structures as seen in the image below.

Mesh.cylinder(endAngle: .halfPi)

The current state of the pr is a very rough draft of the functionality without any documentation changes as i believe there are a few implementation details that we should discuss if a merge is desired.

How should the functionality be integrated?

I quite like the current solution of adding two more arguments to lathe and its parent functions allowing for start and end angles to be defined but defaulting to the complete circle if they are not specified. I could also understand if instead the creation of an additional partialLathe method is desired, especially if complexity of the partial lathe implementation starts to grow.

Should the meshes be made watertight?

The current implementation will not close off the start- and end-face of the lathe if a whole rotation is not achieved as seen below.

I hope this is a good starting point for discussion. If you have any further requirements / ideas please reach out and i will try to implement them.

If the functionality is not desired feel free to close the PR instead.

Best Regards Kolya

nicklockwood commented 3 months ago

@Kolpa I think this makes sense. It's possible to create this effect by subtracting a wedge from a cylinder, but it's not very efficient, and likely to produce messy mesh topology.

Regarding your questions:

1) How should the functionality be integrated?

I agree that adding optional start/end angles seems like a good way to go. startAngle would default to zero and endAngle would default to startAngle. Need to consider if the arc should go clockwise or anticlockwise by default though (I don't recall what the precedent is - if any - in the other Euclid APIs). Also need to think about what happens for angles < 0 or greater than 2pi (should they be normalised, or should the relative distance be used to decide arc direction in some way?)

2) Should the meshes be made watertight?

I definitely think that the result should be watertight by default, because watertight meshes are required for CSG operations, and all existing primitives are watertight. It's not obvious what to do about texture coordinates for the newly generated surfaces though. I'm fine with having an option to not include these faces, although it's not obvious how best to include that option since it's only meaningful when start and end angle are specified - should it be a separate fillExposedFaces (don't love the name) parameter, or could it somehow be added to the existing faces enum?

Rather than an endless proliferation of parameters, it might also be worth introducing a LatheOptions struct that can then be passed as an optional parameter to all lathe-based primitives. This could include startAngle, endAngle, fillExposedFaces (or whatever) along with existing options like poleDetail and slices.