360 degrees face offset

Hi! I’m trying to offset a face 360 degrees. I tried different approaches but none of them worked properly, so I got stuck and I don't know how to continue. I'm going to explain to you what I want to achieve and what I tried until now, hoping that maybe you can offer me some suggestions or guidance. So, here it is:

  • Concrete examples of what I want to achieve:
  1. Planar face offset

                           From a planar face:                       To a solid with rounded edges and corners:  

  1. Nonplanar face offset (bezier in this particular case)

                     From a nonplanar face:                      To a solid with rounded edges and corners: 

  • What I tried:
  1. The first approach I tried was offsetting the face outwards and inwards by using BRepOffset_MakeOffset, creating two solids, and merging them. After that, I created pipes from the edges with BRepOffsetAPI_MakePipe and spheres with BRepPrimAPI_MakeSphere to simulate the corners. Everything went smoothly until BRepAlgoAPI_Fuse failed when  I tried to fuse them all together to obtain one single solid.
  2. For the second approach, I tried ChFi2d_AnaFilletAlgo for the fillets but it fails on nonplanar faces, as expected. I thought that maybe the fusing is failing because of the pipes and spheres' fusion. Do you know if there’s something similar for fillet construction in the case of nonplanar edges?
  3. The last one was offsetting the face first so it can get a small thickness and become solid. After this operation, I created the offset of the solid using BRepOffsetAPI_MakeOffsetShape with the flag pipe BRepOffset_Pipe to round the corners of the solid. 
    BRepOffset_MakeOffset offsetCreator0; //for offsetting the initial face with a small offset to get some thickness
    offsetCreator0.Initialize(face, 0.001, Precision::Confusion(), BRepOffset_Mode::BRepOffset_Skin, false, true, GeomAbs_Intersection, true);
    offsetCreator0.MakeOffsetShape();
    solid_from_face = TopoDS::Solid(offsetCreator0.Shape());
    
    BRepOffsetAPI_MakeOffsetShape offsetCreator1; //for offsetting the solid created from a face in order to generate rounded edges and corners 
    offsetCreator1.PerformByJoin(solid_from_face, offset, Precision::Confusion(), BRepOffset_Pipe, true); 
    

          This approach has two problems:

  • with the offset = 0.001, when I'm trying to offset some of the faces, it returns an empty shape. When I increase it to 0.01, it’s working. Do you know what’s the rule for choosing the right offset?
  • a result cannot be generated if the underlying geometry of S is BSpline, as mentioned in the documentation. Any suggestions of what to use in the case of BSplines, is there some functionality already implemented in OCC that I can use?

I think I covered everything. If there's something unclear feel free to reach out to me in the comment section. Looking forward to any suggestions for new approaches! 

Thomas Anderson's picture

I think I know what you are after and here would be my first attempt using BRepOffset_MakeOffset:
1) offset the face 1/2 the distance of your final distance in the negative direction. make sure thickening is turned off.
2) take the result of the first offset operation and run it through another offset operation. Use the full thickness value for the offset in the positive direction. Make sure thickening is turned on. Set the join type GeomAbs_Arc.

Claudia-Roxana Budimir's picture

Hi, Thomas! Thanks for your reply! Unfortunately, I'm still unable to obtain the rounded edges even if I set the join type as GeomAbs_Arc. Seems to me that the face has to have a small thickness so the roundedness can be computed, or am I wrong? I'm attaching the brep files of the faces I'm trying to offset and also some screenshots of the current results. 

My code looks like this now:

            BRepOffset_MakeOffset offsetCreatorF;
            offsetCreatorF.Initialize(face,
                                    -(offset/2),
                                    Precision::Confusion(),
                                    BRepOffset_Mode::BRepOffset_Skin,
                                    false,
                                    false,
                                    GeomAbs_Intersection,
                                    false);
            if (!offsetCreatorF.CheckInputData({}))
            {
                return false;
            }
            offsetCreatorF.MakeOffsetShape();
            
            BRepOffset_MakeOffset offsetCreatorS;
            offsetCreatorS.Initialize(offsetCreatorF.Shape(),
                                    offset,
                                    Precision::Confusion(),
                                    BRepOffset_Mode::BRepOffset_Pipe,
                                    false,
                                    true,
                                    GeomAbs_Arc,
                                    true);
            if (!offsetCreatorS.CheckInputData({}))
            {
                return false;
            }

            offsetCreatorS.MakeOffsetShape();
            solidOut = TopoDS::Solid(offsetCreatorS.Shape());
Thomas Anderson's picture

After some investigation, it appears the offset api is not setup for this use case. 'join type' is only considered for handling c0 continuity across faces of a shell and not considered during thickening. The code for building the thickening face is here:

https://git.dev.opencascade.org/gitweb/?p=occt.git;a=blob;f=src/BRepOffs...

Don't think I can help. Good luck.

Mikhail Sazonov's picture

I would stay on the first approach:

  1. The first approach I tried was offsetting the face outwards and inwards by using BRepOffset_MakeOffset, creating two solids, and merging them. After that, I created pipes from the edges with BRepOffsetAPI_MakePipe and spheres with BRepPrimAPI_MakeSphere to simulate the corners. Everything went smoothly until BRepAlgoAPI_Fuse failed when  I tried to fuse them all together to obtain one single solid

If Fuse operation fails then please create a bug in the bugtracker. If ther are two valid shapes and they are failed to be fused by Boolean operation then probably it can be fixed. If the bug will be fixed you will be nearer to the final target of your quest.

Eventually, this could be ended with creation of a new operation adopted and integrated into OCCT.

Boolean operation is known to be sensitive to the cases of tangential intersections between surfaces of arguments. If the surfaces are of canonical types they could be easier for the algorithm to consider coinciding in some given tolerance. Use Fussy value parameter for this to take effect. If your surfaces are of B-spline type but can be approximated by canonical types it is better to convert them to canonical ones (like plane, sphere, cylinder, cone). You can apply to Canonical recognition component of OpenCascade company. 

Claudia-Roxana Budimir's picture

I tried to do the fusing using the fuzzy parameter, but the result was not the one that I expected. Furthermore, I tested the shapes' validity using BRepCheck_Analyzer and they are valid. Another thing to be mentioned is that when I fused two neighboring shapes it worked, the problem is with multifusing. I created the bug in the bugtracker and I hope it can be fixed. Thanks a lot for the help!

This is the code I used for fusing:

TopoDS_Shape fuse(std::vector<TopoDS_Shape> shapes)
{
    Standard_Boolean bRunParallel;
    Standard_Real aFuzzyValue;
    BRepAlgoAPI_Fuse aBuilder;

    // perpare the arguments
    TopTools_ListOfShape aLS;
    const TopoDS_Shape& arg = shapes.front();
    aLS.Append(arg);

    TopTools_ListOfShape aLT;
    for (std::vector<TopoDS_Shape>::iterator it = shapes.begin() + 1; it != shapes.end(); ++it)
        aLT.Append(*it);

    bRunParallel=Standard_True;
    aFuzzyValue=2.1e-5;

    // set the arguments
    aBuilder.SetArguments(aLS);
    aBuilder.SetTools(aLT);

    // Set options for the algorithm
    aBuilder.SetFuzzyValue(aFuzzyValue);
    aBuilder.SetRunParallel(bRunParallel);
    Standard_Boolean bSafeMode = Standard_True;
    aBuilder.SetNonDestructive(bSafeMode);
    bool bCheckInverted = Standard_True;
    aBuilder.SetCheckInverted(bCheckInverted);

    aBuilder.Build();
    return aBuilder.Shape();
}