Fix faces with error orientation

I have a .iges file, which contains faces with error orientation. That is, some faces point inside, while others point outside.

I convert it to obj and get 001.png.

I sew faces and get 002.png.

I sew faces and then combine shells to a solid and get 003.png, which is still not correct.

What shoud I do to fix those faces?

 

 

Attachments: 
Kirill Gavrilov's picture

Sharing a model (input IGES, sewed results and created Solid in .BREP format) would be useful to make any comment.

dade huang's picture

This is another .igs file which has similar problems.

Actually, I found that many .igs models have faces with error orientation.

Like 004.png and 005.png. Maybe this is an common issue for igs files.

Here is my code:

// import iges file
IGESControl_Reader igesReader = IGESControl_Reader();
Standard_Integer status = igesReader.ReadFile(file);
igesReader.TransferRoots();
// get shape
TopoDS_Shape igesTopoShape = igesReader.OneShape();
// sewing the shape
BRepBuilderAPI_Sewing sewing = BRepBuilderAPI_Sewing();
sewing.Add(igesTopoShape);
sewing.Perform();
TopoDS_Shape sewedShape = sewing.SewedShape();

// try make solid with shells
BRepBuilderAPI_MakeSolid makeSolid;
for (TopExp_Explorer anExp(sewedShape, TopAbs_SHELL); anExp.More(); anExp.Next())
{
    TopoDS_Shell shell = TopoDS::Shell(anExp.Current());
    makeSolid.Add(shell);
}
makeSolid.Build();
TopoDS_Solid solid = makeSolid.Solid();

// add left faces
TopoDS_Compound allShapes;
BRep_Builder aBuilder;
aBuilder.MakeCompound(allShapes);
aBuilder.Add(allShapes, solid); // add solid
for (TopExp_Explorer anExp(sewedShape, TopAbs_FACE); anExp.More(); anExp.Next())
{
    TopoDS_Face face = TopoDS::Face(anExp.Current());
    bool faceInSolid = false;
    // check if face is in solid
    for (TopExp_Explorer anExp2(solid, TopAbs_FACE); anExp2.More(); anExp2.Next())
    {
        TopoDS_Face face2 = TopoDS::Face(anExp2.Current());
        if (face == face2)
        {
            faceInSolid = true;
            break;
        }
    }
    if (!faceInSolid)
    {
        // face not in solid: add to allShapes
        // not to add face twice
        aBuilder.Add(allShapes, face);
    }
}


stateof3d_161058's picture

I haven't had any orientation problems with the .igs files before, to be honest

Kirill Gavrilov's picture

Sewing does work for me on a given shape with a reasonable tolerance - wholes between triangulated Faces do not appear and normals are oriented consistently.
Probably you should check the tolerance - algorithm does not sew any faces, only close enough, and geometry tolerance within IGES model might be larger than you expect.

pload XDE MODELING VISUALIZATION
vclear
vclose *
# import IGES file
testreadiges 8878777_0.igs i
nbshapes i
# display original shape with oriented normals
incmesh i 1
vinit v1/v1
vnormals i on -length 3 -useMesh -oriented 1
vsetdispmode i 1
vfit
vsetcolor i ORANGE4
# export original shape into i.obj file
wavefront i i

# perform sewing with tolerance=1.0
sewing s 1.0 i
nbshapes s
# display sewed shape with oriented normals
incmesh s 1
vinit v2/v1
vnormals s on -length 3 -useMesh -oriented 1
vfit
vsetdispmode s 1
vsetcolor s GREEN4
# export sewed shape into s.obj file
wavefront s s

Original shape:

Draw[5]> nbshapes i
Number of shapes in i
 VERTEX    : 72
 EDGE      : 74
 WIRE      : 6
 FACE      : 4
 SHELL     : 0
 SOLID     : 0
 COMPSOLID : 0
 COMPOUND  : 1
 SHAPE     : 157

Result shape (SHELL appeared and several EDGES merged):

Draw[13]> nbshapes s
Number of shapes in s
 VERTEX    : 64
 EDGE      : 66
 WIRE      : 6
 FACE      : 4
 SHELL     : 1
 SOLID     : 0
 COMPSOLID : 0
 COMPOUND  : 0
 SHAPE     : 141
Kirill Gavrilov's picture

Actually, I found that many .igs models have faces with error orientation.

IGES format does not support a proper Solid definition, so that surfaces orientations might be arbitrary and edges between faces duplicated.
This is expected for IGES files, considering limitations of this old format.

STEP format is much more advanced in this aspect and allows preserving correct Solid definition (if it existed in originating CAD software) .
Hence, this format is preferable for data exchange, when possible.

dade huang's picture

THANKS SO MUCH. This really solve my problem. The sewing fixes face orientation problems well if I use a proper tolerance.

BRepBuilderAPI_Sewing sewing = BRepBuilderAPI_Sewing(1.0);
sewing.Add(igesTopoShape);
sewing.Perform();
TopoDS_Shape sewedShape= sewing.SewedShape();
Kirill Gavrilov's picture

Note, that 1.0 is just an arbitrary number. Tolerance should be defined based on some other information (like a user input / knowledge of typical models / something else).

stateof3d_161058's picture

Thank you for the detailed explanation, Kirill, as always very informative.

yuan 常's picture

Export the IGES cylindrical surface anomaly. Create the section line frame through the sketch method, stretch to generate the profile, and add chamfers.
Below is the source code for exporting IGES. There are three parameter settings corresponding to three states.
bool CalculateTool::ExportShapeToIGES(std::vector <TopoDS_Shape> shapes, std::string filePath)
{
IGESControl_Writer writer;

//Situation 1
//Interface_Static::SetIVal("write.convertsurface.mode", 1);
//Interface_Static::SetIVal("write.surfacecurve.mode", 1);
//Interface_Static::SetIVal("write.iges.brep.mode", 1);
//Interface_Static::SetIVal("write.iges.curves.mode", 1);

//Situation 2
//Interface_Static::SetIVal("write.convertsurface.mode", 0);
//Interface_Static::SetIVal("write.iges.brep.mode", 1);
//Interface_Static::SetIVal("write.iges.curves.mode", 1);
//Interface_Static::SetIVal("write.surfacecurve.mode", 0);

//Situation 3
Interface_Static::SetIVal("write.iges.brep.mode", 1);
Interface_Static::SetIVal("write.iges.BRepMode", 1);
Interface_Static::SetRVal("write.precision.val", 0.001);
Interface_Static::SetIVal("write.precision.mode", 1);
Interface_Static::SetIVal("write.iges.plane.mode", 0);
Interface_Static::SetIVal("write.iges.curves.mode", 0);
Interface_Static::SetIVal("write.param.surface", 0);

for each (TopoDS_Shape shape in shapes)
{

writer.AddShape(shape);
}
writer.ComputeModel();
writer.Write(filePath.c_str());
return true;
}
Here is the code for stretching the entity and creating the wireframe. In this code, the arc edges of the wireframe have an abnormality after chamfering. The starting point and ending point of the edges are opposite to the direction of the wireframe (counterclockwise). I recreated a wireframe based on the midpoint of the starting and ending points to replace the abnormal arc edges.
bool Profile::Extrude(Vector startPoint, Vector endPoint, Vector axisX)
{
try
{
if (m_wire == nullptr)
{
m_lastError = "No wire to extrude";
return false;
}
AxisStart = startPoint;
AxisEnd = endPoint;
gp_Trsf m_gp_Trsf = gp_Trsf::gp_Trsf();
gp_Pnt start = OCCConverter::ToPnt(startPoint);
gp_Pnt end = OCCConverter::ToPnt(endPoint);
gp_Vec xVec = OCCConverter::ToVec(axisX);
bool isOriented = CalculateTool::TransformShape(start, end, xVec, m_gp_Trsf);
TopLoc_Location topLoc(m_gp_Trsf);

TopoDS_Wire* wire = static_cast<TopoDS_Wire*>(m_wire);
BRepBuilderAPI_MakeFace faceMaker(*wire, Standard_True);
if (!CalculateTool::IsShapeValid(faceMaker))
{
m_lastError = "Failed to create face from wire";
return false;
}
BRepLib::EnsureNormalConsistency(faceMaker);
TopoDS_Face face = faceMaker.Face();
double extrudeLength = start.Distance(end);
if (extrudeLength < Precision::Confusion())
{
char errBuf[256];
sprintf_s(errBuf, "Error: Extrusion length is zero. Start(%.2f,%.2f,%.2f) End(%.2f,%.2f,%.2f) Length:%.4f",
start.X(), start.Y(), start.Z(), end.X(), end.Y(), end.Z(), extrudeLength);
m_lastError = gcnew String(errBuf);
return false;
}
gp_Vec vec(0, 0, extrudeLength);
BRepPrimAPI_MakePrism prismMaker(face, vec);
TopoDS_Shape* shape = new TopoDS_Shape(prismMaker.Shape());
if (!CalculateTool::IsShapeValid(*shape))
{
m_lastError = "Failed to create face from wire";
return false;
}
shape->Location(topLoc);
m_shape = static_cast<void*>(shape);

return true;
}
catch (const Standard_Failure& e)
{
m_lastError = gcnew String(e.GetMessageString());
return false;
}
catch (...)
{
m_lastError = "Unknown error during extrusion";
return false;
}
}
bool LProfile::BuildWire()
{
double A = m_params.BigEdge; // Large edge (X axis)
double B = m_params.SmallEdge; // Small edge (Y axis)
double t = m_params.Thickness; // Thickness

// 1. Create 6 vertices in counterclockwise order
TopoDS_Vertex v0 = BRepBuilderAPI_MakeVertex(gp_Pnt(0, 0, 0)); // Origin outer corner
TopoDS_Vertex v1 = BRepBuilderAPI_MakeVertex(gp_Pnt(A, 0, 0)); // Large edge bottom right
TopoDS_Vertex v2 = BRepBuilderAPI_MakeVertex(gp_Pnt(A, t, 0)); // Large edge top right (ready for small r fillet)
TopoDS_Vertex v3 = BRepBuilderAPI_MakeVertex(gp_Pnt(t, t, 0)); // Inner corner key point (ready for large R fillet)
TopoDS_Vertex v4 = BRepBuilderAPI_MakeVertex(gp_Pnt(t, B, 0)); // Small edge inner top (ready for small r fillet)
TopoDS_Vertex v5 = BRepBuilderAPI_MakeVertex(gp_Pnt(0, B, 0)); // Small edge outer top

// 2. Create 6 edges in order
TopoDS_Edge e0 = BRepBuilderAPI_MakeEdge(v0, v1); // Edge 0: Bottom outer wall
TopoDS_Edge e1 = BRepBuilderAPI_MakeEdge(v1, v2); // Edge 1: Large edge end
TopoDS_Edge e2 = BRepBuilderAPI_MakeEdge(v2, v3); // Edge 2: Large edge inner wall
TopoDS_Edge e3 = BRepBuilderAPI_MakeEdge(v3, v4); // Edge 3: Small edge inner wall
TopoDS_Edge e4 = BRepBuilderAPI_MakeEdge(v4, v5); // Edge 4: Small edge end
TopoDS_Edge e5 = BRepBuilderAPI_MakeEdge(v5, v0); // Edge 5: Back to origin

BRepBuilderAPI_MakeWire wireMaker;
wireMaker.Add(e0);
wireMaker.Add(e1);
wireMaker.Add(e2);
wireMaker.Add(e3);
wireMaker.Add(e4);
wireMaker.Add(e5);

if (!wireMaker.IsDone())
{
m_lastError = "Base profile sharp corner wire topology construction failed.";
return false;
}
TopoDS_Wire baseWire = wireMaker.Wire();

if (m_params.RadiusR <= 0.001 && m_params.RadiusToer <= 0.001)
{
this->m_wire = static_cast<void*>(new TopoDS_Wire(baseWire));
return true;
}

BRepBuilderAPI_MakeFace faceMaker(baseWire);
TopoDS_Face tempFace = faceMaker.Face();

BRepFilletAPI_MakeFillet2d filletEngine(tempFace);

if (m_params.RadiusR > 0.001)
{
filletEngine.AddFillet(v3, m_params.RadiusR); // Add inner large fillet R
}
if (m_params.RadiusToer > 0.001)
{
filletEngine.AddFillet(v2, m_params.RadiusToer); // Add large edge end fillet r
filletEngine.AddFillet(v4, m_params.RadiusToer); // Add small edge end fillet r
}

filletEngine.Build();
if (filletEngine.IsDone() && filletEngine.Status() == ChFi2d_IsDone)
{
TopExp_Explorer wireExp(filletEngine.Shape(), TopAbs_WIRE);
if (wireExp.More())
{
TopoDS_Wire rawWire = TopoDS::Wire(wireExp.Current());

// Manually fix arc direction
BRepBuilderAPI_MakeWire newWireMaker;

OutputDebugStringA("=== Building Fixed Wire ===\n");

for (TopExp_Explorer edgeExp(rawWire, TopAbs_EDGE); edgeExp.More(); edgeExp.Next())
{
TopoDS_Edge currentEdge = TopoDS::Edge(edgeExp.Current());
BRepAdaptor_Curve adaptor(currentEdge);
GeomAbs_CurveType curveType = adaptor.GetType();

TopoDS_Edge edgeToAdd;

if (curveType == GeomAbs_Circle)
{
// Arc edge: check direction
if (currentEdge.Orientation() == TopAbs_REVERSED)
{
// Reversed: create new forward arc
OutputDebugStringA("Found reversed arc, creating new forward arc\n");
edgeToAdd = CalculateTool::ReverseAndCreateNewArc(currentEdge);
}
else
{
// Forward: use directly
edgeToAdd = currentEdge;
}
}
else
{
// Non-arc edge (line): use directly
edgeToAdd = currentEdge;
}
newWireMaker.Add(edgeToAdd);
}
if (!newWireMaker.IsDone())
{
m_lastError = "Failed to build fixed wire";
return false;
}
TopoDS_Wire fixedWire = newWireMaker.Wire();
this->m_wire = static_cast<void*>(new TopoDS_Wire(fixedWire));
return true;
}
}
this->m_wire = static_cast<void*>(new TopoDS_Wire(baseWire));
return true;
}