I’m using Rhino.Geometry.NurbsSurface.CreateRailRevolvedSurface in GHPython with a helical Rhino.Geometry.NurbsCurve as rail and a Rhino.Geometry.PolyCurve as profile, all generated procedurally in code.
I’ve quickly created a Grasshopper definition to be able to illustrate my issues more easily.
First, I use Rhino.Geometry.Brep.Trim with two planes to crop the ends of the brep and keep the region between the planes. This way I get two planar ends that can be capped to close the brep.
However, when I set the two planes to an exact height, the Rhino.Geometry.Brep.Trim fails. I guess the planes intersecting the brep at the pink points pose issues, which is common in Rhino?
When I offset the planes ever so slightly in their normal direction, the trimming succeeds, but these tolerances get shaved off of the total length of the brep, which introduces inaccuracies in the process.
The second problem lies with finally getting a solid closed brep.
After trimming the ends, I cap them with Rhino.Geometry.Brep.CapPlanarEnds, which works fine.
However, there remains a naked seam along the brep - where the initial profile starts and ends while being revolved -, which prevents it from being watertight.
I’ve read the entire Rhino.Geometry.Brep documentation and tried 10 different things to find a solution to my second problem, namely how to get rid of the naked seam (cf. last picture above).
The Rhino.Geometry.Brep.CreateSolid method does the trick, although nothing really pointed to it being a better thing to try than for instance Rhino.Geometry.Brep.JoinNakesEdges method, which didn’t perform its task at all. I mean it asks for an array of breps or surfaces and I’m providing a single brep that I now have to put inside a list.
Anyway, if anybody has a suggestion or explanation for the first problem, I’d be an even happy camper.
The first problem is, I think, arising because your cutting plane is passing exactly through a point where various sub-element edges come together, one of which becomes coincident with the plane as it approaches its end.
One solution is to twist the spiral object down and round slightly (as though driving in a screw) so the cutting plane no longer passes through the confluence (you probably need to increase the number of repetitions from 5 to 6 to give sufficient length of surface to accommodate this). This creates a surface that can be trimmed and capped.
However, major caveat: the Cap command doesn’t succeed with this geometry. It is necessary to select the trimmed edges of each opening and use them to create planar surfaces. These will join to the helicoidal surface where Cap couldn’t. One for the McNeelies to study. I imagine your .gh can be rewritten to take this approach. Here’s a version with the “screw in” added. I’m sorry but I’ll have to leave you to rework the cap part. demo3.gh (52.8 KB)
Right, thanks for reply anyway. I’m trying to do something rather similar.
Sure, but I don’t see how the result would be any different? Thanks.
Thanks for the extensive answer, Jeremy. I figured as much. Do you maybe know why this is so problematic? It’s something that I never really understood, but that I’m aware of.
Yes, but that would mess up my calculations. Everything’s up to ISO spec currently.
I guess I’ll have to stick with offsetting the planes and losing some total length (i.e. 0.002mm).
After some more experimenting, sweep2 does actually give a better outcome. After sweeping the surface you can use the naughty Join Naked Edges to close the surface along the sweep spiral. That allows you to trim and cap. If you try the same thing with the original rail revolve geometry you have a series of naked edges running vertically to join and the end result still doesn’t permit trim and cap.
If you don’t join the spiral naked edges you have a 0.032mm gap at the top top cutting plane, even though the spiral ends are precisely aligned in x and y.
I get an untrimmed surface when using Sweep2, with no naked seam, so there’s no need to join any naked edges. The geometry is a little off somehow, but resembles the result from the RailRev.
The top and bottom trim fine, even without tilting or moving the planes, but capping fails afterwards, because the trimming seems to introduce self-intersecting curves.
I wasn’t expecting the naked seam with the sweep2.
I have modeled a few threads and I would usually sweep2 a closed curve of just the thread which can be capped after sweeping. I’d trim it and then union it with a cylinder.
@diff-arch I think I’ve solved both your issues, using a couple of Brep intersection and rebuilding tricks. It makes the code a bit more fragile if you wanted to change your input Breps (because it needs to select one part of the Breps after the split method), but that should be manageable.
To solve the trim issue at the exact height of the “pink points”, I replaced the trim operation with 1. intersecting the brep and the plane and 2. using the intersection curves to split the Brep in 3 parts, and selectind the middle one.
To solve the naked edges issues, I exploded the Brep into its individual faces then joined all these faces together again. rg.Breps.JoinBreps() is fairly lenient on tolerances and has a lot of preliminary cleanup steps that will try to repair common problems with the inputs. brep.Edges.RemoveNakedMicroEdges(MTOL, True) and brep.Edges.MergeAllEdges(ATOL) also helped get rid of some naked edges, but could not get all of them. Rhino.Geometry.Brep.CreateSolid uses brep.Edges.RemoveNakedMicroEdges(2.5 * MTOL, True) and Rhino.Geometry.Brep.JoinNakesEdges(2.5 * MTOL) as part of its cleanup steps; the higher tolerance is probably one of the reasons why it gets you better results.
Here’s the relevant code:
import Rhino.Geometry as rg
import scriptcontext as sc
from System.Collections.Generic import IEnumerable
MTOL = sc.doc.ModelAbsoluteTolerance
ATOL = sc.doc.ModelAngleToleranceRadians
brep = B
cA = rg.Intersect.Intersection.BrepPlane(brep, Pa, MTOL)[1][0]
cB = rg.Intersect.Intersection.BrepPlane(brep, Pb, MTOL)[1][0]
brep = brep.Split.Overloads[IEnumerable[rg.Curve], float]([cA, cB], MTOL)[2]
# brep.Edges.RemoveNakedMicroEdges(MTOL, True)
# brep.Edges.MergeAllEdges(ATOL)
pieces = []
for f in brep.Faces:
pieces.Add(f.DuplicateFace(False))
brep = rg.Brep.JoinBreps(pieces, MTOL)[0]
psA = rg.Brep.CreatePlanarBreps(cA)[0]
psB = rg.Brep.CreatePlanarBreps(cB)[0]
brep.Join(psA, MTOL, False)
brep.Join(psB, MTOL, True)
G = brep
@GregArden@chuck There might be interesting test cases in here for the Brep trim / intersection tools, and for dealing with naked micro edges when joining / rebuilding a Brep.
I extracted the relevant bits in a 3dm file, let me know if you’d like to have this in a YT.