Remove Coplanar Faces

Hello,

I would like to remove all duplicated brep faces for a flown brep unsuccesfully using the methdod:
_myBrep.MergeCoplanarFaces(double tolerance)
but it always return false and it doesn’t remove any coplanar face.

I also tried to use the mothod IsDuplicated with same result:

 public static bool RemoveDuplicatedFaces(Brep input, bool removeBothFaces)
    {
        var duplicatedFaces = new List<int>();
        for (var i = 0; i < input.Faces.Count; i++)
        {
            var face1 = input.DuplicateSubBrep(new[] { i});
            for (var j = i+1; j < input.Faces.Count; j++)
            {
                var face2 = input.DuplicateSubBrep(new []{j});
                
                if (face1.IsDuplicate(face2, 0.001))  //<- it always return false
                {
                    duplicatedFaces.Add(i);
                    if (removeBothFaces)
                          duplicatedFaces.Add(j);
                   
                    break;
                }                  
            }
        }

        duplicatedFaces = duplicatedFaces.OrderByDescending(_ => _).ToList();

        if (duplicatedFaces.Any())
        {
            foreach (var dupIndex in duplicatedFaces)
            {
                input.Faces.RemoveAt(dupIndex);
            }
        }
        return duplicatedFaces.Any();
    }

Anyone know how to remove both coplanar faces, or in this case the faces with non manifold edges:

Thanks in adnvance.

Kind Regards

Hi Henry,

It not not clear to me what you are looking to do, nor why. I could use some more background on what you have an what you want. A sample .3dm file might also be useful.

Thanks,

– Dale

Hello Dale,

Maybe it’s not clear, the idea is remove the faces of a brep wich has the same size, shape, and are in the same place.

If you build a box, and you flow it to a circle, the side faces of the box are in the same place (like a duplicate):

I would like to remove this faces, do you know how to do it in RhinoCommon?
DaleExample.3dm (136.2 KB)

Regards

Hi Henry,

The best approach is to remove the end caps before flowing.

MergeCoplanarFaces won’t address this situation. You don’t want to merge - you want to remove…

– Dale

Hi Dale,

Thanks for the advice, I need to remove the faces, only in the case that there are coplanar (so I have to do it after flow the object) Do you know any way to detect when 2 Brep faces has the same geometry, position and orientation by RhinoCommon? (like _SelDup command but with the faces of the same brep)

One method is to compare the faces to the perpendicular plane at the start point of the circle.

– Dale

Hi Dale,

The faces could have curvature like that in U, V, or both (in this case it’s only one direction):

I Tried to duplicate all faces (DuplicateSubBrep() method) and compare them with .IsDuplicate but it doesn’t work, do you know if it could be a bug, or maybe the point is not use it with faces?

Anyway thanks for the help.

Best Regards

Hello @dale

I have been testing both methods in Rhino 6 (myBrep.MergeComplanar and myBrep.IsDuplicated) and it seems that still not working in both Rhino 5 and 6.

Here the code:

    protected override Result RunCommand(RhinoDoc doc, RunMode mode)
    {
        //Define sizes
        const int boxHalfLegth = 10;
        const double radius = boxHalfLegth / Math.PI;

        //Create geometry
        var boxBrep = new Box(Plane.WorldXY, new BoundingBox(-boxHalfLegth, -1, -1, boxHalfLegth, 1, 1)).ToBrep();

        //Create flow curves
        var line = new Line(new Point3d(-boxHalfLegth, 0, 0), new Point3d(boxHalfLegth, 0, 0));
        var target = new Circle(Plane.WorldZX, radius);

        //Flow Geometry & Show results
        var mc = new FlowSpaceMorph(line.ToNurbsCurve(), target.ToNurbsCurve(), false);
        RhinoApp.WriteLine($"Morph result = {mc.Morph(boxBrep)}");
        RhinoApp.WriteLine($"Morph IsValid = {mc.IsValid}");

        //Check if faces are mergeable and try to remove duplicates
        RhinoApp.WriteLine($"Merge Coplanar Faces Result = {boxBrep.MergeCoplanarFaces(doc.ModelAbsoluteTolerance)}");
        RhinoApp.WriteLine($"Remove Dup Faces Result = {RemoveDuplicatedFaces(boxBrep, doc.ModelAbsoluteTolerance, true)}");

        //Add object and redraw
        doc.Objects.Add(boxBrep);
        doc.Views.Redraw();

        return Result.Success;
    }

    public static bool RemoveDuplicatedFaces(Brep input, double tolerance, bool removeBothFaces)
    {
        //Used to store duplicated face indices
        var duplicatedFaces = new SortedList<int, int>();

        //Iterate all faces
        for (var i = 0; i < input.Faces.Count; i++)
        {
            //Create a brep from current face
            var face1 = input.DuplicateSubBrep(new[] { i });

            //Iterate from next face to check duplicate
            for (var j = i + 1; j < input.Faces.Count; j++)
            {
                //Create a brep from next face
                var face2 = input.DuplicateSubBrep(new[] { j });

                //Check if it's duplicated
                if (!face1.IsDuplicate(face2, tolerance)) continue;
                RhinoApp.WriteLine("Found a duplicated face");

                //Add current face to delete index list
                if (!duplicatedFaces.ContainsKey(i))
                    duplicatedFaces.Add(i, i);

                //If both add other face to delete index list
                if (removeBothFaces)
                    duplicatedFaces.Add(j, j);

                break;
            }
        }
        if (!duplicatedFaces.Any()) return false;
        RhinoApp.WriteLine("Removing duplicated faces");

        //Reverse to delete from latest one.
        var list = duplicatedFaces.Reverse().ToList();
        list.ForEach(_ => input.Faces.RemoveAt(_.Key));

        return true;
    }

Here a video:

And here the example project:
DuplicatedFaces.7z (169.2 KB)

Best Regards

Assuming that there will be no response and it will not be fixed, I wrote these methods which remove internal faces (full non-manifold edges faces) and remove duplicated faces (one of them or both)

  /// <summary>
    /// Remove brep faces which are full inside (all edges are non-manifold) of the brep.
    /// </summary>
    /// <param name="input">Brep to attempt to remove faces.</param>
    /// <returns><see cref="input"/> brep without full non-manifold edge faces.</returns>
    public static Brep RemoveInternalFaces(Brep input)
    {
        var facesToKeep = new List<Brep>();

        foreach (var face in input.Faces)
        {
            if (face.AdjacentEdges().All(edgeIndex => input.Edges[edgeIndex].Valence == EdgeAdjacency.NonManifold))
                facesToKeep.Add(face.DuplicateFace(true));
        }

        var jointBreps = Brep.JoinBreps(facesToKeep, RhinoDoc.ActiveDoc.ModelAbsoluteTolerance);
        return jointBreps.Length != 1 ? input : jointBreps[0];
    }

    /// <summary>
    /// Removes duplicated faces for a given brep.
    /// </summary>
    /// <param name="input">Brep to attempt to remove faces.</param>
    /// <param name="removeBoth">If true it will remove both faces, otherwise only one of the duplicated faces</param>
    /// <returns>><see cref="input"/> brep without duplicated edges.</returns>
    public static Brep RemoveDuplicatedFaces(Brep input, bool removeBoth)
    {
        input.CullUnusedEdges();
        var facesToKeep = new List<Brep>();

        for (var i = 0; i < input.Faces.Count - 1; i++)
        {
            for (var j = i + 1; j < input.Faces.Count; j++)
            {
                if (!input.Faces[i].AdjacentEdges().All(_ => input.Faces[j].AdjacentEdges().Contains(_))) continue;
                facesToKeep.Add(input.Faces[i].DuplicateFace(true));
                if (removeBoth) facesToKeep.Add(input.Faces[j].DuplicateFace(true));
                break;
            }
        }

        var jointBreps = Brep.JoinBreps(facesToKeep, RhinoDoc.ActiveDoc.ModelAbsoluteTolerance);
        return jointBreps.Length != 1 ? input : jointBreps[0];
    }

Just for if someone needs to do it.

Best Regards.

Hi @henrydm,

What needs fixing?

– Dale

Hello @dale

Maybe I’m wrong but I assumed that MergeCoplanarFaces Brep method will remove one of these coplanar faces

because are coplanar, I guessed it will keep only one face but it doesn’t.

The other option was, extract subBreps for each face and Check brepFace.IsDuplacated(OtherBrepFace) but it doesn’t work either (even reversing face direction).

Don’t worry Dale, it already solved using these functions.

Hi @dale

MergeCoplanarFaces doesn’t remove coplanar faces. So you are wise to use your own solution.

– Dale

Hi Dale,

Thanks, I know, merge coplanar faces should merge coplanar faces into one (so if the faces are coincident, located at exactly the same place and have exactly the same size and topology it should merge them into single one) but it seems that it’s not working, by other hand, brep.IsDuplicate() method doesn’t seem to be working either for attached example.

Best Regards.

HI @henrydm,

Can you post a Brep in which Brep.MergeCoplanarFaces is not working the way you expect?

Thanks,

– Dale

Sure Dale, you can use the attached 3dm file of the first post of this thread, which is a regular box flown onto a circle.

Best Regards.

Right. And as I’ve mentioned before Brep.MergeCoplanarFaces does not remove coplanar faces.

– Dale

Right, but it should merge both faces into a single one, nevertheless it keeps both faces.

By other hand Brep.IsDuplicated() should detect these faces once converted to brep as duplicated breps and it doesn’t work either.

Once again, this is not what this function is designed to do. See the attached for an example of the type of Brep this function is good for.

TestMergeCoplanarFaces.3dm (36.5 KB)

This I agree with. This is very difficult because of the amount and the complexity of the data stored by a Brep.

– Dale