Brep Workflow for Thread Fails in GHPython, Succeeds in Grasshopper?

Hi,

I’m back trying to construct a water-tight thread brep in GHPython from scratch and failing.

Some of you might remember this topic from about two years ago, where I encountered the same issue, while trying something similar, and never really managed to solve it.
I’m trying a different way of constructing the thread here, namely by lofting its defining helical curves.

This Grasshopper definition leads to a closed thread brep, which is fabulous.

However, when translated to GHPython using RhinoCommon a broken, open brep is the result.

import Rhino.Geometry as rg
import scriptcontext as sc


MTOL = sc.doc.ModelAbsoluteTolerance
ATOL = sc.doc.ModelAngleToleranceRadians

# Hardcoded Constants Matching Input Geometry (don't change)
PLANE = rg.Plane.WorldYZ
MAJOR_DIAMETER = 4.0  # M4
PITCH = 0.7           # M4
TURN_COUNT = 123

if __name__ == "__main__":
    # Create Thread
    thread_brep = rg.Brep.CreateFromLoft(C, rg.Point3d.Unset, rg.Point3d.Unset, rg.LoftType.Straight, False)[0]

    # Trim Thread to Length And Construct Caps
    start_plane = rg.Plane(PLANE.Origin + PLANE.ZAxis * (PITCH + MTOL), -PLANE.XAxis, PLANE.YAxis)
    end_plane = rg.Plane(PLANE.Origin + PLANE.ZAxis * (PITCH * TURN_COUNT - MTOL), PLANE.XAxis, PLANE.YAxis)
    
    cutter_domain = rg.Interval(-MAJOR_DIAMETER, MAJOR_DIAMETER)
    cutter_breps = []
    cap_breps = []
    for plane in [start_plane, end_plane]:
        # End cutters
        rect = rg.Rectangle3d(plane, cutter_domain, cutter_domain)
        corners = [rect.Corner(i) for i in range(4)]
        p, q, r, s = corners
        cutter_breps.append(rg.Brep.CreateFromCornerPoints(p, q, r, s, MTOL))
        # End caps
        rc, crvs, _ = rg.Intersect.Intersection.BrepPlane(thread_brep, plane, MTOL)
        boundary_breps = rg.Brep.CreatePlanarBreps(crvs, MTOL)
        cap_breps.append(boundary_breps[0])

    thread_brep_segments = thread_brep.Split(cutter_breps, MTOL)

    thread_brep = None 
    for brep_seg in thread_brep_segments:
        #brep_seg.Edges.RemoveNakedMicroEdges(MTOL)
        #brep_seg.Edges.MergeAllEdges(ATOL)
        bbox = brep_seg.GetBoundingBox(False)
        dist_start = start_plane.DistanceTo(bbox.Center)
        dist_end = end_plane.DistanceTo(bbox.Center)
        if(abs(dist_start - dist_end) < MTOL):
            thread_brep = brep_seg
            break

    #faces = [f.DuplicateFace(False) for f in thread_brep.Faces]
    #thread_brep = rg.Brep.JoinBreps(faces, MTOL)[0]
    
    start_cap, end_cap = cap_breps
    thread_brep.Join(start_cap, MTOL, False)
    thread_brep.Join(end_cap, MTOL, True)

    # Outputs
    B = thread_brep

Superficially, I do exactly the same, however I suspect the Grasshopper components to do some cleaning up and/or rebuilding here and there, basically some magic behind the scenes.

I’ve tried some de- and reconstructing the brep, but to no avail. Cleaning up naked micro edges and merging all edges also didn’t seem to do much.

Thanks for taking a look! :face_blowing_a_kiss:

thread-rhf.gh (127.2 KB)

Hi @diff-arch

After this:

thread_brep = rg.Brep.CreateFromLoft(C, rg.Point3d.Unset, rg.Point3d.Unset, rg.LoftType.Straight, False)[0]

do this:

thread_brep.Faces.SplitKinkyFaces()

– Dale

1 Like

Hi Dale,

Oh wow, that totally fixed it. Thank you, thank you, thank you!

The documentation for this method states “splits any faces with creases into G1 pieces”.
Does this mean that the face is split so that the tangent directions where the split pieces meet are the same but the curvature may vary? I couldn’t really find a specific explanation on what “G1” exactly means.

Thanks again.

This may or may not help.

https://developer.rhino3d.com/api/rhinocommon/rhino.geometry.continuity

– Dale

1 Like