BRepAlgoAPI_Splitter with closed wire

Hi

I am using the BRepAlgoAPI_Splitter to split a shell with a closed wire.

I need as a second step to choose the faces which are inside the wire.

Currently at a loss on how to detect this; attached is a step file with the result of an example splitting and the wire used.

Thanks.

Attachments: 
Mikhail Sazonov's picture

The step file that you attached contains each face encapsulated in its own shell. It would be helpful to see the source code of your example that produced such result. Actually, splitter must split the shell in your case in two shells.

Michael Capocci's picture

Hi, thanks for the reply. The argument for the splitter is a shell encapsulating faces, yes, and there are 43 faces. But the result does not produce two shells - if it did then that is exactly what I would require. Instead it returns one shell containing 56 faces with no obvious way of detecting the 'inside' faces.

The code is below where toSplit is the shell containing 43 faces, and splitBy is the wire.

The file I attached is for diagnostics to show the output face ( contents of splitter.Shape(), a collection of faces) and the original wire.

using var shellShapes = toSplit.ToTopToolsList();
using var wireShapes = splitBy.ToTopToolsList();
using var splitter = new BRepAlgoAPI_Splitter();
splitter.SetFuzzyValue(tolerance);
splitter.SetArguments(shellShapes);
splitter.SetTools(wireShapes);
splitter.Build();
return splitter.Shape();

Strangely, if I write splitter.Shape() directly, I get just the original faces (43), each one encapsulated in a shell. If I get the faces (via TopExp_Explorer) from splitter.Shape(), I get the cut faces (56).

I don't know if there's a way of just getting two shells.

Mikhail Sazonov's picture

Your mistake is that you get faces from the input shell and put them in a list. In such way you have lost the initial shell. You need to create a new list and put there only the shell. And pass that list to the splitter.

Michael Capocci's picture

Sorry, my fault for the confusion. The method I call is my own extension, so I am in fact (I think) putting only the shell into the splitter:

        public static TopTools_ListOfShape ToTopToolsList(this TopoDS_Shape shape)
        {
            var list = new TopTools_ListOfShape();
            list.Append(shape);
            return list;
        }
Mikhail Sazonov's picture

Could you attach the original shell and wire before split?

Michael Capocci's picture

Hi Mikhail,

Thanks again for your time on this.

I've attached the files requested. I think the way we have a shell for each face is due to being exported to STEP - so I've included .brep files too - we don't see that effect there.

Attachments: 
Eugeny's picture

Hello Michael,

BRepAlgoAPI_Splitter is not able to split the shell on parts. It splits edges, faces, solids by other arguments, but not the wires and shells. But still, the result of Splitter algorithm may be used as a base to build the desired result, but some post treatment required. The idea is to build connected blocks of shells, but avoid the input wire in the connection elements. The following code may be used for that (may contain some errors as just written in notepad so you may have to adjust it):

BRepAlgoAPI_Splitter aSplitter;
aSplitter.AddArgument(theShell);
aSplitter.AddTool(theWire);
aSplitter.Build();

if (!aSplitter.IsDone())
{
    return;
}

const TopoDS_Shape& aSplitterResult = aSplitter.Shape();

TopTools_IndexedDataMapOfShapeListOfShape aConnectionMap;
// Map shapes to find connected elements
TopExp::MapShapesAndAncestors(aSplitterResult, TopAbs_EDGE, TopAbs_FACE, aConnectionMap);

// Build map of edges from the input wire to avoid passing through its edges
// when building the resulting shells
TopTools_IndexedMapOfShape aMEAvoid;
if (const TopTools_ListOfShape* pWireImages = aSplitter.Images().Seek(theWire))
{
    for (const auto& aWireIm : *pWireImages)
    {
        TopExp::MapShapes(aWireIm, TopAbs_EDGE, aMEAvoid);
    }
}
else
{
    TopExp::MapShapes(theWire, TopAbs_EDGE, aMEAvoid);
}

// Fence map to avoid adding faces twice
TopTools_MapOfShape aMFence;

// Resulting shells
TopTools_ListOfShape aLShells;

TopExp_Explorer aExp(aSplitterResult, TopAbs_FACE);
for (; aExp.More(); aExp.Next())
{
    const TopoDS_Face& aS = TopoDS::Face(aExp.Current());
    if (!aMFence.Add(aS))
    {
        continue;
    }

    // The new shell
    TopoDS_Shell aShell;
    BRep_Builder().MakeShell(aShell);

    // Start the block
    BRep_Builder().Add(aShell, aS);

    // Look for connected parts
    TopoDS_Iterator itF(aShell);
    for (; itF.More(); itF.Next())
    {
        const TopoDS_Shape& aS1 = aItB.Value();
        TopExp_Explorer aExpSS(aS1, TopAbs_EDGE);
        for (; aExpSS.More(); aExpSS.Next())
        {
            const TopoDS_Shape& aSubS = aExpSS.Current();
            if (aMEAvoid.Contains(aSubS))
            {
                continue;
            }

            const TopTools_ListOfShape& aLS = aConnectionMap.FindFromKey(aSubS);
            TopTools_ListIteratorOfListOfShape aItLS(aLS);
            for (; aItLS.More(); aItLS.Next())
            {
                const TopoDS_Face& aS2 = TopoDS::Face(aItLS.Value());
                if (aMFence.Add(aS2))
                {
                    BRep_Builder().Add(aShell, aS2);
                }
            }
        }
    }
    // Add the block into result
    aLShells.Append(aShell);
}
Michael Capocci's picture

Thanks Eugeny! I shall try this and let you know how I get on.

Michael Capocci's picture

Hi Again, fiddled a similar algorithm into c# and made use ShapeAnalysis_Edge.CheckOverlapping to handle odd configurations of faces.

Seems to be effective in finding the 'connected' components so I can get an inside and outside shell. Thanks!

Next step - detecting which one is inside the wire?

Mikhail Sazonov's picture

This task has solution if your shell is open and has only one closed boundary. In this case you just select the part that has the boundary edges.

If this is a closed shell of a solid (it has no boundary like a sphere) you cannot determine which part is inner, because both of them have the same topological meaning.

If a shell is open but has several bounds (like a cylindrical face closed along the periodical direction) you again cannot determine which part is inner. Here you have two cases. First is the wire is closed locally and creates a cutout in the face. The second is the wire goes around all cylinder dividing it on two parts, each having its own bound.

But you can take the decision that your wire has the orientation counter-clockwise if you see on it from a definite direction. In this case the solution is available. If you go along the wire and at a moment you have two faces located one to the left and another to the right you should take the face to the left as the inner one.

Michael Capocci's picture

Hi Mikhail

Yes, the shell is open with once closed boundary, so this is always possible.

I have been trying to use the wire's orientation to do this, but still confused. If you could detail the procedure a little I would definitely appreciate it!

The wire is always Forward but the underlying edges have varying orientations.

Mikhail Sazonov's picture

The true wire orientation is defined by edges' underlying curves. But we should consider a curve in reverse manner if the cumulated orientation of the given edge is reversed.

The wire orientation is coherent with the shell if the wire contour is counter-clockwise oriented with reference to the shell faces' normals.

Taking the above, you should consider a face adjacent to a wire's edge inner if this edge has the same orientation in the face and in the wire.

Michael Capocci's picture

I think my problem may be that the original wire, because it is not bounding a face, has indeterminate orientation. So comparing the split faces edges with this maybe isn't helpful.

Mikhail Sazonov's picture

If you know the normal direction you can make this wire correctly oriented.

Mikhail Sazonov's picture

Well, in your case the simplest way is to count the number of external boundaries of both shell parts. The shell containing only one boundary is the inner part, and another one will contain 2 boundaries, one is the outer and another is the hole.

Use the class ShapeAnalysis_FreeBounds, and its method GetClosedWires() to get all shell boundaries.