Closing a trimmed brep (similar to boolean split command)

rhinocommon-v6
rhino6
brep

#1

Hi, i wrote a method that trims a simple brep (grey) by another brep (cyan) that is open and contains several faces.

Similar to the Boolean Split command, i want to achieve a CLOSED brep from my trim:

As a first step, i am trimming the grey brep with the cyan one. This gives me an OPEN brep, from which i take the closed outer loop.

I now split each of the cyan (cutter) brep’s faces with this closed outer loop.

Here’s the result for one of the cutter’s faces. I should get two faces, however, i get three.

Anyway, i finally identify all split-faces that are inside the closed outer loop’s bounding box, add them to a list and re-append them to my trimmed brep.

However, i am unable to achieve a clean result.

I’ve tried compacting, repairing, joining naked edges, shrinking faces, etc. etc.

Here’s my test-geometry:

closingTrimmedBrep.3dm (294.5 KB)

And my code:

 protected override Result RunCommand(RhinoDoc doc, RunMode mode)
    {
        double myTolerance = 0.1;

        #region pick brep
        var getter = new GetObject();
        getter.SetCommandPrompt("Pick extrusion brep");
        getter.GeometryFilter = Rhino.DocObjects.ObjectType.Brep;
        getter.DisablePreSelect();
        getter.SubObjectSelect = false;
        getter.Get();

        if (getter.CommandResult() != Rhino.Commands.Result.Success)
            return getter.CommandResult();

        if (null == getter.Object(0).Brep())
            return Rhino.Commands.Result.Failure;
        #endregion

        Brep untrimmedBrep = getter.Object(0).Brep();

        #region pick another brep
        var getter2 = new GetObject();
        getter2.SetCommandPrompt("Pick cutter brep");
        getter2.GeometryFilter = Rhino.DocObjects.ObjectType.Brep;
        getter2.DisablePreSelect();
        getter2.SubObjectSelect = false;
        getter2.Get();

        if (getter2.CommandResult() != Rhino.Commands.Result.Success)
            return getter.CommandResult();

        if (null == getter2.Object(0).Brep())
            return Rhino.Commands.Result.Failure;
        #endregion

        Brep cutterBrep = getter2.Object(0).Brep();

       // trim untrimmed brep with cutter
        var trimResults = untrimmedBrep.Trim(cutterBrep, myTolerance);

        if (trimResults.Length != 1)
            return Rhino.Commands.Result.Failure;

        var trimmedOpenBrep = trimResults[0];

        //close trimmed brep
        var closedBrep = CloseTrimmedProfile(trimmedOpenBrep, cutterBrep, true, myTolerance, doc);

        doc.Objects.AddBrep(closedBrep);

        doc.Views.Redraw();

        return Result.Success;
    }

    private Brep CloseTrimmedProfile(Brep trimmedOpenBrep, Brep cuttingBrep, bool accurateButSlowerBoundingBoxes, double tolerance, RhinoDoc doc)
    {
        //extract naked edge curve from brep and join them

        var nakedEdgeCurveSegments = trimmedOpenBrep.DuplicateNakedEdgeCurves(true, false);

        var joinResults = Curve.JoinCurves(nakedEdgeCurveSegments);

        if (joinResults.Length != 1)
            throw new Exception("Trimmed Brep yields more than one closed naked edge curve.");

        var nakedEdgeCurveClosed = joinResults[0];

        if (!nakedEdgeCurveClosed.IsClosed)
            throw new Exception("Trimmed Brep yields invalid edge results. Curve not closed.");

        //bounding box of entire naked edge curve

        BoundingBox masterBox = nakedEdgeCurveClosed.GetBoundingBox(accurateButSlowerBoundingBoxes);
        masterBox.Inflate(tolerance * 10); //ENLARGE BOUNDING BOX SLIGHTLY

        List<Brep> allContainedSingleBreps = new List<Brep>();

        foreach (var f in cuttingBrep.Faces)
        {
            //take single surface (face/brep) of cuttingBrep

            Brep singleFace = f.DuplicateFace(false);
            singleFace.Compact();

            //split this surface with full naked edge curve

            Brep splitResultBrep = singleFace.Faces[0].Split(nakedEdgeCurveClosed.DuplicateSegments(), tolerance);

            splitResultBrep.Compact();

            //make list of surfaces (face/brep) resulting from split operation

            List<Brep> splitResultSingleBreps = new List<Brep>();

            foreach (var face in splitResultBrep.Faces)
            {
                Brep singleFc = face.DuplicateFace(false);
                singleFc.Compact();
                singleFc.Faces[0].ShrinkFace(BrepFace.ShrinkDisableSide.ShrinkAllSides);
                splitResultSingleBreps.Add(singleFc);
            }

            //see if face's bounding box is contained in master bbox. if yes - add to list

            foreach (var brep in splitResultSingleBreps)
            {
                BoundingBox currBrepBBox = brep.Faces[0].OuterLoop.To3dCurve().GetBoundingBox(accurateButSlowerBoundingBoxes);

                bool strictOnCoincidentSurfaces = false;
                bool isContained = masterBox.Contains(currBrepBBox, strictOnCoincidentSurfaces);

                if (isContained)
                    allContainedSingleBreps.Add(brep);
            }
        }

        //join faces back together
        foreach (var item in allContainedSingleBreps)
            trimmedOpenBrep.Join(item, tolerance, true);

        return trimmedOpenBrep;
    }

#2

The additional cyan face that you pointed out is a result of Faces.Split projecting all possible segments of nakedEdgeCurveClosed to the split face. Less obvious splitting may also be occurring, leading to the open brep.

Instead of finding a solution using this approach, would you consider using Brep.CreateBooleanIntersection?

Steve


#3

@lungenstrudel,

would’t it be possible if you use brep.CreateBooleanDifference and make sure the normals of the cyan part are pointing to the gray part before ?

Alternatively you could use brep.CreateSolid then delete the smaller result of the gray part. (both are solid).

_
c.


(qythium) #4

Edit: oops, didn’t read @clement 's post directly above properly

Maybe I’m missing something, but why not just use the plain BooleanDifference command? It works just as well when the cutter (poly)surfaces are open, trimming and discarding the portion of the input surface lying on the ‘reverse’ face of the cutter surface.

(couldn’t open your file but here’s a crude recreation, yellow = reverse side of surface)
image


#5

@spb @clement @qythium

Thanks a lot, guys. It took me 5 minutes to come up with this code, which now totally solves my problem:

  protected Brep TrimAndCloseBrepImproved(Brep nakedProfile, Brep cuttingBrep, Point3d closestPointToWantedPart, double tolerance)
    {
        cuttingBrep.Flip(); //open cutter brep needs to be oriented correctly

        var differenceResult = Brep.CreateBooleanDifference(nakedProfile, cuttingBrep, tolerance);

        //boolean difference of profile to be trimmed and open cutter-brep

        if (differenceResult == null)
        {
            throw new Exception("Boolean difference yielded no result.");
        }

        //for the case of several parts resulting from the difference - find the one closest to reference point

        Dictionary<Brep, double> distancesToReferencePoint = new Dictionary<Brep, double>();

        foreach (var item in differenceResult)
        {
            distancesToReferencePoint.Add(item, closestPointToWantedPart.DistanceTo(item.GetBoundingBox(false).Center));
        }

        var sortedDict = from entry in distancesToReferencePoint orderby entry.Value ascending select entry;

        var finalBrep = sortedDict.ElementAt(0).Key;

        if (finalBrep.IsSolid && finalBrep.IsValid)
        {
            return finalBrep;
        }
        else
        {
            throw new Exception("Trimmed Brep is invalid.");
        }
    }