Extracting common faces and rebuilding the shapes for meshing

I'm trying to build some 3d models for export to a 3D meshing program and I'm having some issues with the modeling of adjacent volumes.

Here is the problem. I'm looking at a very simple example, imagine a cube A of size (1,1,1) sitting on top of cube B of size (3,3,3), aligned vertically. I want to be able to mesh each one separately (they could be of different materials), but have a consistent mesh. The bottom side of cube A touches the top side of cube B; the size of the overlap being a 1x1 square. Now, the issue in trying to mesh this structure (build as a TopoDS_CompSolid or TopoDS_Compound) is that the top side of cube B and bottom side of cube A are meshed separately, which causes problems with non-aligned points on that face (hence the mesh is not consistent).

What I want would be an operation such that, given the two cubes laying on top of each other, extracts this common rectangle and rebuilds the two shapes (cubes) in such a way that the common face is part of both cube A and cube B. This way, in the mesher, the common face is meshed exactly once, and the 3d mesh could be build from there.

I've tried to use BRepAlgoAPI_Fuse and BRepBuilderAPI_Sewing, but the first one creates a single shape out of cube A and B (I still want to mesh each one separately, since in my model they could be of different materials) and the second one seems to output the empty shell of the two cubes, so only 2d meshing works.

Thanks,
Tibi

Paul Jimenez's picture

Try using the Fuse operation with the Shell of each cube as input, then fix the result with ShapeFix_Shell to obtain the two separate Shells that are now sharing a Face. You can use the resulting Shells to build the Solids again, if you want/need to.

Tiberiu Chelcea's picture

I've finally gotten around to trying to fix this (been derailed by some other projects) and had some mixed success. Here's a piece of code that I'm using:

TopoDS_Solid box1 = BRepPrimAPI_MakeBox(gp_Pnt(0,0,0), 3, 3, 3);
TopoDS_Solid box2 = BRepPrimAPI_MakeBox(gp_Pnt(1,1,0), 1, 1, 1);
TopoDS_Shape res_cut = BRepAlgoAPI_Cut(box1,box2);
TopExp_Explorer Ex1;
// a pretty convoluted way to get the result of the cut, good for now only
for (Ex1.Init(res_cut, TopAbs_SOLID); Ex1.More(); Ex1.Next())
{
TopoDS_Shape gigi = Ex1.Current();
box1 = TopoDS::Solid(gigi);
}

// get the shells of each box and fuse them
TopoDS_Shell box1_sh = BRepTools::OuterShell(box1);
TopoDS_Shape box2_sh = BRepTools::OuterShell(box2);
TopoDS_Shape fused_shell = BRepAlgoAPI_Fuse(box1_sh, box2_sh);

// recreate the solids
TopoDS_Compound fused_solids;
BRep_Builder bt_solids;
bt_solids.MakeCompound( fused_solids );
TopExp_Explorer Ex;
for (Ex.Init(fused_shell,TopAbs_SHELL); Ex.More(); Ex.Next()) {
// fix the shell
TopoDS_Shell crt_shell = TopoDS::Shell(Ex.Current());
ShapeFix_Shell FixShell;
FixShell.Init(crt_shell);
FixShell.Perform();
TopoDS_Shell aShell = FixShell.Shell();
// re-create the solid
TopoDS_Solid sol = BRepBuilderAPI_MakeSolid(aShell);
// add it to a compound for further use
BuildTool.Add(res, sol);
}

This piece of code seems to work really nicely when, say, I have two boxes touching on one side. In this case, however, the two boxes are touching on 5 sides (one was carved out of the other); the result of BRepAlgoAPI_Fuse is a single Shell and the corresponding solid is pretty strange (it produces errors in meshing). I'm not sure whether this is the way to tackle this problem of if there's some step that I'm skipping.

Thanks.

Paul Jimenez's picture

One difference between your method and mine is that I use FixShell.Shape() instead to retrieve a Compound, then I explore all Shells of that Compound and build Solids out of them. Hopefully, that will do the trick.

Tiberiu Chelcea's picture

Paul, thanks for the pointer. It does not fully solve my problem, though. It is true that now I get 2 solids, but meshing is still producing errors which seem to be associated with problems with overlapping faces.

Here's my solution, hopefully I understood your hint correctly:

TopoDS_Shape fused_shell = BRepAlgoAPI_Fuse(box1_sh, box2_sh);
TopExp_Explorer Ex;
for (Ex.Init(fused_shell,TopAbs_SHELL); Ex.More(); Ex.Next()) {
TopoDS_Shell crt_shell = TopoDS::Shell(Ex.Current());
ShapeFix_Shell FixShell;
FixShell.Init(crt_shell);
FixShell.Perform();
if (FixShell.NbShells() > 1) {
TopExp_Explorer ExShls;
TopoDS_Compound shellComp = TopoDS::Compound(FixShell.Shape());
for (ExShls.Init(shellComp,TopAbs_SHELL); ExShls.More(); ExShls.Next()) {
TopoDS_Solid sol_tmp = BRepBuilderAPI_MakeSolid(TopoDS::Shell(ExShls.Current()));
BuildTool.Add(res, sol_tmp);
}
} else {
TopoDS_Shell aShell = FixShell.Shell();
TopoDS_Solid sol = BRepBuilderAPI_MakeSolid(aShell);
BuildTool.Add(res, sol);
}
}

I'm not sure that's what you had in mind with your hint. I've tried a couple of different configurations of boxes, and got the same type of error in Gmsh. Is there some additional thing that I should look at?

I know that you cannot post your solution, but is this heading in the direction of what you came up with?

Thanks,
Tibi

Tiberiu Chelcea's picture

This is a screenshot of the result of meshing for the example above. The 2D mesh works great. The 3D seems to be extremely screwed up, with elements overlapping and intersecting and being malformed. I just thought it might help with understanding the problem.

I've also tried to perform a ShapeFix_Solid on each solid created by BRepBuilderAPI_MakeSolid -- the result is pretty much the same (the number of errors in meshing decreases a bit, but the result looks the same).

Attachments: 
Tiberiu Chelcea's picture

Mystery solved. The problem was in Gmsh -- I was using their default Delaunay method for 3D meshing which (somehow), was producing incorrect results. Once I've switched to using "Frontal", the entire design seems to have been meshed correctly (I'm saying "seems to have been" since I'll have to connect it to a solver which can check whether the mesh is indeed consistent).

Tiberiu Chelcea's picture

There seems to be a problem with this algorithm. For my application, it's very likely that a solid shares faces with more than one other solid (not the same ones). BRepAlgoAPI_Fuse can take only two shells and fuses them. If I do an iterative fusing (fused_shell_stp0 = shell_0; fused_shell_stpk = BRepAlgoAPI_Fuse(fused_shell_stp(k-1), shell_k) ) I get some very weird solids: some of the solids are composed of multiple initial components, while others are just empty.

So, is there a way to extend this algorithm (or BRepAlgoAPI_Fuse) to multiple shells? If there are only two shells, it works very well. I've also tried to do pairwise reconstruction, but the new shells don't share faces with the old ones, so common faces between solids that were already rebuilt are lost yet again.