Arcs/Circles are segmented when converting to BSplineCurve

Hi,

I have been trying to construct some Breps in OCC using PythonOCC. I have managed to successfully create Breps composed of BSplineSurfaces and most types of edges/curves (lines, BSplineCurves, polylines, etc.). I use the 2d curves in parameter space and the relevant surface to construct edges (i.e. BRepBuilderAPI_MakeEdge::BRepBuilderAPI_MakeEdge (Geom2d_Curve, Geom_Surface)). For 2d BSplineCurves and lines, this seems to work fine.

The problem is that, when converting Arcs (using GCE2d_MakeArcOfCircle()), it seems to segment the arcs into BSplineCurves with degree 1 (see attached screenshot).

Any idea why it is doing this? According to the OCC docs, it should create a BSplineCurve of degree 2 that is the same shape as the arc. Is this a bug? Or am I approaching this the wrong way?

Image 1 is the resultant Brep (single rectangle face with filleted corners to simplify the case) where you can see the individual vertices that approximate the arc, and that the curve is degree 1.

Image 2 is a test with a simple circle. Right side is how the Brep should come out, left side is how it comes out: the circle segmentation can be clearly seen...

Any tips or help very much appreciated :)

Mikhail Sazonov's picture

It is unclear how you created such shapes. Please provide a piece of code, and attach a brep file saved with the method BRepTools::Write().
Definitely it must not be so if you use OCCT correctly.
And please tell why in the screenshot we can see the words "Plug-in: Rhino". What is the relation between Rhinoceros kernel and your sample?

Tom Svilans's picture

Hi Mikhail,

Thank you for your response!

Here is a minimum working example for this case as well as the output in .brep and .stp format. In the screenshot, I am using Rhino to look at the resultant STEP file that comes from OCC - so it is just to view the results.

The problem seems to be either in converting the 2d circle into a Geom2d_TrimmedCurve or in using BRepBuilderAPI_MakeEdge with the surface... Any help is much appreciated :) Maybe it is very simple and obvious where I am doing something wrong!

def point2d(spoint: Point):
    return gp_Pnt2d(spoint.x, spoint.y)    

def direction2d(svector: Vector):
    return gp_Dir2d(svector.x, svector.y)

def plane2d(splane: Plane):
    return gp_Ax22d(point2d(splane.origin), 
        direction2d(splane.xdir))

def circle2d(scircle: Circle):
    return GCE2d_MakeCircle(plane2d(scircle.plane), scircle.radius).Value()

def brep(sbrep):
    vertices = [BRepBuilderAPI_MakeVertex(gp_Pnt(p.x, p.y, p.z)).Vertex() for p in sbrep.Vertices]

    curve3d = [curve(crv) for crv in sbrep.Curve3D]
    curve2d = [curve2d(crv) for crv in sbrep.Curve2D]

    # sbrep.Curve2D contains only one Circle object, with a origin and radius
    # curve() and curve2d() are methods that look at the specific type of curve being
    # converted, and calls the appropriate specific method. In this instance, there
    # is only the one Circle object, which 
    # Using the above circle2d() method, it is converted to a Geom2d_BSplineCurve

    surfaces = [surface(srf) for srf in sbrep.Surfaces]

    # sbrep.Surfaces contains only one BSpline surface, which converts fine

    trims = []
    for t in sbrep.Trims:
        face = sbrep.Faces[t.FaceIndex]
        tcurve = curve2d[t.CurveIndex]

        if isinstance(tcurve, Geom2d_Curve):
            # If the edges are built from 2d data, use the BRepBuilderAPI_MakeEdge method
            # which also takes the relevant surface into account
            edge2d = BRepBuilderAPI_MakeEdge(tcurve, surfaces[face.SurfaceIndex])
        else:
            edge2d = BRepBuilderAPI_MakeEdge(tcurve)

        if edge2d.IsDone():
            trims.append(edge2d.Edge())
        else:
            trims.append(None)
        pass

    wires = []
    for loop in sbrep.Loops:
        wire = BRepBuilderAPI_MakeWire()
        for ti in loop.TrimIndices:
            wire.Add(trims[ti])

        if not wire.IsDone():
            wires.append(None)
        else:
            wires.append(wire.Wire())

    faces = []
    for sface in sbrep.Faces:

        if wires[sface.OuterLoopIndex] is None:
            continue

        face = BRepBuilderAPI_MakeFace(surfaces[sface.SurfaceIndex], wires[sface.OuterLoopIndex], False)

        for hole in sface.LoopIndices:
            if hole != sface.OuterLoopIndex:
                face.Add(wires[hole])

        if not face.IsDone():
            continue

        faces.append(face.Face())

    builder = BRep_Builder()
    bprim = BRepPrim_Builder(builder)
    shell = TopoDS_Shell()

    bprim.MakeShell(shell)

    for face in faces:
        bprim.AddShellFace(shell, face)
        pass

    return shell
Tom Svilans's picture

Some added context... here is the code I am using to write the STEP file:

def write_step(path, shapes):
    schema = 'AP214IS'
    assembly_mode = 2

    writer = STEPControl_Writer()
    fp = writer.WS().TransferWriter().FinderProcess()
    OCC.Core.Interface.Interface_Static.SetCVal('write.step.schema', schema)
    OCC.Core.Interface.Interface_Static.SetCVal('write.step.unit', 'MM')
    OCC.Core.Interface.Interface_Static.SetCVal('write.step.assembly', str(assembly_mode))
    OCC.Core.Interface.Interface_Static.SetCVal('write.step.product.name', 'EMA Blank')

    for shape in shapes:
        status = writer.Transfer(shape, STEPControl_AsIs)
        if status != 1:
            continue

    status = writer.Write(path)
Mikhail Sazonov's picture

I am sorry, but I cannot understand your query. It is contradictual and there is no connection between initial post and the provided sample.

Tom Svilans's picture

Hi Mikhail,

Thanks again for looking into this. What is contradictory? Maybe I didn't explain it well enough :)

When I use 2d circles or arcs to construct edges for wires, the result is a face boundary with degree 1 (polygonized) when exported as STEP, not degree 2 as expected.

When I load the .brep file in Gmsh to look at it, it also appears polygonized (degree 1) so I don't think it is a problem with the STEP output.

Did you notice anything odd about the .brep file I provided? Does the code above look like the correct way to create Breps using 2d parameter space curves?

Mikhail Sazonov's picture

I looked at your brep file and found out that it is invalid. The algorithm BRepCheck_Analyzer reports errors on it. The cause of error is the absence of 3D curve. The shape healing helps in this case. The algorithm ShapeFix_Shape repairs the shape and reconstructs the 3D curve (bspline of 11th degree). The curve is constructed using approximator, and it does not limit the degree of the result curve, therefore we obtain the curve not of the 2nd degree.

Attachments: