Non-manifold Surfaces after cutting as Two Meshes

Dear community,

I use these methods for cutting a mesh and I have a problem with non-manifold shape.

std::vector<OCCTMeshModel> OCCTShapeHealer::getSplittedMeshesAsTwoPart(OCCTMeshModel mesh_model, float height)
{
    std::vector<OCCTMeshModel> splitted_mesh_models;

    OCCTMeshModel top_mesh, bottom_mesh;
    TopoDS_Shape being_splitted_shape = mesh_model.getMeshShape();
    if(being_splitted_shape.NbChildren() > 0){
        being_splitted_shape = unifyToBeCuttedShape(being_splitted_shape);
    }
    double Xmin, Ymin, Zmin, Xmax, Ymax, Zmax;
    Bnd_Box bounding_box_of_being_splitted_shape;
    BRepBndLib::Add(being_splitted_shape, bounding_box_of_being_splitted_shape);
    bounding_box_of_being_splitted_shape.Get(Xmin, Ymin, Zmin, Xmax, Ymax, Zmax);


    //Cut the bottom part of the tool
    auto box_to_cut_for_bottom_part = BRepPrimAPI_MakeBox(Xmax - Xmin, Ymax - Ymin, height);
    gp_Trsf XYZTransformation_of_box_to_cut_for_bottom_part;

    // 1,0,0 Corresponds to X axis; 0,1,0 Corresponds to Y axis; 0,0,1 Corresponds to Z axis.
    XYZTransformation_of_box_to_cut_for_bottom_part.SetValues(1, 0, 0, Xmin, 0, 1, 0, Ymin, 0, 0, 1, Zmin);
    BRepBuilderAPI_Transform transformXYZ_of_bottom_box(XYZTransformation_of_box_to_cut_for_bottom_part);
    transformXYZ_of_bottom_box.Perform(box_to_cut_for_bottom_part.Shape());
    auto transformed_cutter_box_bottom = transformXYZ_of_bottom_box.Shape();
    auto cutter_for_bottom_shape = BRepAlgoAPI_Cut(being_splitted_shape, transformed_cutter_box_bottom);
    auto bottom_shape_of_splitted_shape = cutter_for_bottom_shape.Shape();
    auto cutter_for_top_shape = BRepAlgoAPI_Cut(being_splitted_shape, bottom_shape_of_splitted_shape);
    auto top_shape_of_splitted_shape = cutter_for_top_shape.Shape();

    // Rebuild top and bottom shape.
    top_mesh.setMeshShape(top_shape_of_splitted_shape);
    top_mesh.setMeshModelId(0);
    top_mesh.setMeshModelName("top_shape_of_stl");

    bottom_mesh.setMeshShape(bottom_shape_of_splitted_shape);
    bottom_mesh.setMeshModelId(1);
    bottom_mesh.setMeshModelName("bottom_shape_of_stl");

    splitted_mesh_models.push_back(top_mesh);
    splitted_mesh_models.push_back(bottom_mesh);
    return splitted_mesh_models;
}

TopoDS_Shape OCCTShapeHealer::unifyToBeCuttedShape(TopoDS_Shape shape){

    ShapeUpgrade_UnifySameDomain unifier;
    unifier.Initialize(shape,true, true, true);
    unifier.Build();
    return unifier.Shape();
}

I am attaching current screenshots of STL files:

Before cutting:

After cutting:

I would like to fill the cutted surface areas. How can I handle this issuee?

Thank you in advance!

Best regards

Mikhail Sazonov's picture

Your shape is just a set of faces. You need to handle it as a solid object. So, you need to construct a closed shell from faces, and then create a solid shape from the shell. Such shape will be cut as a solid body.

Nezihe Sözen's picture

Hello Mikhail,

Thank you for your support. It helped a lot. Currently, I am able to get closed shapes after cutting operations.

Best regards

Nezihe Sözen's picture

 Hello again,

Actually, I had implemented a solution for this problem and it worked. However, unfortunately, there is another problem now.

My implementation is:

// If we don't create a SOLID shape from TopoDS_Shape, meshes will not be manifold.
    TopoDS_Shell shell;
    BRep_Builder builder;
    builder.MakeShell(shell);

    TopExp_Explorer explorer(being_splitted_shape, TopAbs_FACE);
    while (explorer.More()) {
        const TopoDS_Face& face = TopoDS::Face(explorer.Current());
        builder.Add(shell, face);
        explorer.Next();
    }
    TopoDS_Solid being_splitted_solid;
    BRepBuilderAPI_MakeSolid solid_maker(shell);
    if (!solid_maker.IsDone()) {
        throw std::runtime_error("Failed to create solid shape");
    }
    being_splitted_solid = solid_maker.Solid();

The current problem is that if we cut some step files after importing them, it adds a rectangular prism. They can be shown as below images:

How can I handle this issue with some meshes?

Thank you in advance!

Mikhail Sazonov's picture

It is unclear what were arguments of the cut operation.

Nezihe Sözen's picture

Hi again,

Is these clear now after adding some code snippets, step file and its screenshots? If not, let me know for extra information.

Best regards

Nezihe Sözen's picture

Hello,

I am sorry for unclearness.

My full implementation is: 

std::vector<OCCTMeshModel> OCCTShapeHealer::getSplittedMeshesAsTwoPart(OCCTMeshModel mesh_model, float height)
{
    std::vector<OCCTMeshModel> splitted_mesh_models;

    OCCTMeshModel top_mesh, bottom_mesh;
    TopoDS_Shape being_splitted_shape = mesh_model.getMeshShape();
    if(being_splitted_shape.NbChildren() > 0){
        being_splitted_shape = unifyToBeCuttedShape(being_splitted_shape);
    }
    double Xmin, Ymin, Zmin, Xmax, Ymax, Zmax;
    Bnd_Box bounding_box_of_being_splitted_shape;
    BRepBndLib::Add(being_splitted_shape, bounding_box_of_being_splitted_shape);
    bounding_box_of_being_splitted_shape.Get(Xmin, Ymin, Zmin, Xmax, Ymax, Zmax);

    if((Zmax - Zmin ) <= height){
        throw std::invalid_argument("This value is equal or bigger than the tool length");
    }
    //Cut the bottom part of the tool
    auto box_to_cut_for_bottom_part = BRepPrimAPI_MakeBox(Xmax - Xmin, Ymax - Ymin, height);
    gp_Trsf XYZTransformation_of_box_to_cut_for_bottom_part;

    // 1,0,0 Corresponds to X axis; 0,1,0 Corresponds to Y axis; 0,0,1 Corresponds to Z axis.
    XYZTransformation_of_box_to_cut_for_bottom_part.SetValues(1, 0, 0, Xmin, 0, 1, 0, Ymin, 0, 0, 1, Zmin);
    BRepBuilderAPI_Transform transformXYZ_of_bottom_box(XYZTransformation_of_box_to_cut_for_bottom_part);
    transformXYZ_of_bottom_box.Perform(box_to_cut_for_bottom_part.Shape());
    auto transformed_cutter_box_bottom = transformXYZ_of_bottom_box.Shape();

    // If we don't create a SOLID shape from TopoDS_Shape, meshes will not be manifold.
    TopoDS_Shell shell;
    BRep_Builder builder;
    builder.MakeShell(shell);

    TopExp_Explorer explorer(being_splitted_shape, TopAbs_FACE);
    while (explorer.More()) {
        const TopoDS_Face& face = TopoDS::Face(explorer.Current());
        builder.Add(shell, face);
        explorer.Next();
    }
    TopoDS_Solid being_splitted_solid;
    BRepBuilderAPI_MakeSolid solid_maker(shell);
    if (!solid_maker.IsDone()) {
        throw std::runtime_error("Failed to create solid shape");
    }
    being_splitted_solid = solid_maker.Solid();

    //Cutting operations on SOLID shape
    auto cutter_for_bottom_shape = BRepAlgoAPI_Cut(being_splitted_solid, transformed_cutter_box_bottom);
    auto bottom_shape_of_splitted_shape = cutter_for_bottom_shape.Shape();
    auto cutter_for_top_shape = BRepAlgoAPI_Cut(being_splitted_solid, bottom_shape_of_splitted_shape);
    auto top_shape_of_splitted_shape = cutter_for_top_shape.Shape();


    // Rebuild top and bottom shape.
    top_mesh.setMeshShape(top_shape_of_splitted_shape);
    top_mesh.setMeshModelId(0);
    top_mesh.setMeshModelName("top_shape_of_stl");

    bottom_mesh.setMeshShape(bottom_shape_of_splitted_shape);
    bottom_mesh.setMeshModelId(1);
    bottom_mesh.setMeshModelName("bottom_shape_of_stl");

    splitted_mesh_models.push_back(top_mesh);
    splitted_mesh_models.push_back(bottom_mesh);
    return splitted_mesh_models;
}

TopoDS_Shape OCCTShapeHealer::unifyToBeCuttedShape(TopoDS_Shape shape){

    ShapeUpgrade_UnifySameDomain unifier;
    unifier.Initialize(shape,true, true, true);
    unifier.Build();
    return unifier.Shape();
}

Test STEP File is : B292A03000YPL_GTM.stp

I've downloaded from: https://www.kennametal.com/tr/tr/products/p.b292-ypl-5-x-d-kcms15-a-saft...

And I am splitting from 10mm as a height.

Before Splitting:

After Splitting-part1:

After Splitting- Part2:

Attachments: 
Mikhail Sazonov's picture

Sorry for long delay. Now I will answer your question and solve the problem. Definitely, your code is wrong. I will give you some recommendations to fix it.

  1. Concerning the size of the shape box_to_cut_for_bottom_part. I would give it a little larger size (in directions of -x, +x, -y, +y, and -z) to get rid of possible touching of its shell with the shell of the shape to cut.
  2. The biggest mistake is that your mix all faces of the shape into one solid. The attached shape contains two solids. If you want to reconstruct right solids, you must do the following procedure:
    1. Perform sewing using the algorithm BRepBuilderAPI_Sewing. It will return as much shells as the faces form distinct regions.
    2. Create solid for each shell. If a solid has a hollow then you need to add it to a solid with the outer shell. In this case you should probably prefer using the tool BOPAlgo_MakerVolume instead of doing these two steps.
    3. Make the compound from created solids.
  3. The shape bottom_shape_of_splitted_shape must be got as Common operation between got compound and cutter box shape.
  4. The shape top_shape_of_splitted_shape must be got as Cut operation between got compound and cutter box shape. Do not use the result of previous operation to cut it from the initial shape, because of a number of touch cases, which make operation slow and not robust.
  5. Check the result of each Boolean operation with BRepCheck_Analyzer, and if it reports errors then correct it with the class ShapeFix_Shape.

I have performed all these steps using Draw commands, and have got good results.

Nezihe Sözen's picture

Hello,

Thank you for getting back to me. This time, I wrote late :) I appreciate your willingness to help and provide recommendations to fix the code.

I have done some modifications with your proposal however, I fear that I am wrong it. Additionally, below implementation occurs open mesh (non-manifold problem again):

std::vector<OCCTMeshModel> OCCTShapeHealer::getSplittedMeshesAsTwoPart(OCCTMeshModel mesh_model, float height)
{
    std::vector<OCCTMeshModel> splitted_mesh_models;

    OCCTMeshModel top_mesh, bottom_mesh;
    TopoDS_Shape being_splitted_shape = mesh_model.getMeshShape();
    if (being_splitted_shape.NbChildren() > 0) {
        being_splitted_shape = unifyToBeCuttedShape(being_splitted_shape);
    }
    double Xmin, Ymin, Zmin, Xmax, Ymax, Zmax;
    Bnd_Box bounding_box_of_being_splitted_shape;
    BRepBndLib::Add(being_splitted_shape, bounding_box_of_being_splitted_shape);
    bounding_box_of_being_splitted_shape.Get(Xmin, Ymin, Zmin, Xmax, Ymax, Zmax);

    if ((Zmax - Zmin) <= height) {
        throw std::invalid_argument("This value is equal or bigger than the tool length");
    }

    float margin = 0.1;
    auto box_to_cut_for_bottom_part = BRepPrimAPI_MakeBox(
        Xmax - Xmin + 2 * margin, Ymax - Ymin + 2 * margin, Zmax - Zmin + 2 * margin + height
    );

    gp_Trsf XYZTransformation_of_box_to_cut_for_bottom_part;
    XYZTransformation_of_box_to_cut_for_bottom_part.SetValues(1, 0, 0, Xmin - margin, 0, 1, 0, Ymin - margin, 0, 0, 1, Zmin - margin);
    BRepBuilderAPI_Transform transformXYZ_of_bottom_box(XYZTransformation_of_box_to_cut_for_bottom_part);
    transformXYZ_of_bottom_box.Perform(box_to_cut_for_bottom_part.Shape());
    auto transformed_cutter_box_bottom = transformXYZ_of_bottom_box.Shape();

    BRepBuilderAPI_Sewing sewing;
    sewing.Add(being_splitted_shape);
    sewing.Perform();
    TopoDS_Shape compound_shape = sewing.SewedShape();

    BRepCheck_Analyzer analyzer(compound_shape);
    if (analyzer.IsValid(compound_shape) != Standard_True) {
        ShapeFix_Shape fixer(compound_shape);
        fixer.Perform();
        compound_shape = fixer.Shape();
    }

    BOPAlgo_MakerVolume maker;
    TopExp_Explorer exp(compound_shape, TopAbs_SOLID);
    for (; exp.More(); exp.Next()) {
        const TopoDS_Solid& solid = TopoDS::Solid(exp.Current());
        maker.AddArgument(solid);
    }
    maker.Perform();

    TopoDS_Shape being_splitted_solid = maker.Shape();

    auto cutter_for_bottom_shape = BRepAlgoAPI_Common(being_splitted_solid, transformed_cutter_box_bottom);
    auto bottom_shape_of_splitted_shape = cutter_for_bottom_shape.Shape();
    auto cutter_for_top_shape = BRepAlgoAPI_Cut(being_splitted_solid, bottom_shape_of_splitted_shape);
    auto top_shape_of_splitted_shape = cutter_for_top_shape.Shape();

    top_mesh.setMeshShape(top_shape_of_splitted_shape);
    top_mesh.setMeshModelId(0);
    top_mesh.setMeshModelName("top_shape_of_stl");

    bottom_mesh.setMeshShape(bottom_shape_of_splitted_shape);
    bottom_mesh.setMeshModelId(1);
    bottom_mesh.setMeshModelName("bottom_shape_of_stl");

    splitted_mesh_models.push_back(top_mesh);
    splitted_mesh_models.push_back(bottom_mesh);
    return splitted_mesh_models;
}

Can you share the code you wrote here? Because it works absolutely right according to your image. I'm looking forward to hearing your suggestions.

Best regards,

Nezihe

Mikhail Sazonov's picture

I did not wrote any code, I just performed some tcl commands in the Draw application. Looking at your new code, I see several mistakes, and you still do not follow my recommendations.

  1.  You are creating the cutting box box_to_cut_for_bottom_part too high, because you add Zmax - Zmin + 2 * margin to the Z component. I proposed to add a margin in -Z direction, it means you shoud create box with the size height + margin in Z component.
  2. If you decided to use BOPAlgo_MakerVolume you should not use sewing. But any case you do not yet have any solids to add to the volume maker, instead you should give it the faces.
  3. You did not follow the recommendation #4.
  4. Use ShapeFix after Boolean operation, not before.