Wrong Normals after IGES import

I am trying to import the IGES file and then want to iterate through each face to get correct face normal.
But I am getting wrong normals for the face.Here is the steps which I am following.
1.Read IGES using IGESControl_Reader
2.Sew the body using BRepBuilderAPI_Sewing
3.Try to repair the shape using ShapeFix_Shell
4.iterating through the faces to get face normals - tried these 3 approaches (BRepGProp_Face/BRepAdaptor_Surface.D1()/GeomLProp_SLProps)
each one is giving same result. but wrong one.
Face normal coming reverse for some faces.

One approach is:
gp_Vec d1U = new gp_Vec();
gp_Vec d1V = new gp_Vec();
gp_Pnt p = new gp_Pnt();
bRepAdaptorSurface.D1(uParam, vParam, ref p, ref d1U, ref d1V);
gp_Vec norm = d1U.Crossed(d1V);
if(face.Orientation() == TopAbs_Orientation.TopAbs_REVERSED)
norm.Reverse();

Same approach works fine for STEP file.

Can someone help me in this case? Am I doing something wrong while reading IGES?
Please find the sample iges attached.

Attachments: 
vrushal.nanavati_146649's picture

Did you find the answer on how to fix the surface normals for models imported from IGES files?
I seem to have the same problem when reading an IGES file. The surface normals of some of the faces are inwards, while others have it outwards.
Everything works just fine with STEP file models.

I can see a lot of questions were asked around this but, no solid answer yet.
Can someone please help?

1027795176_159830's picture

Can you tell me the ways on how to fix the surface normals for models imported from step files? best wishes to you

Guido van Hilst not specified's picture

Are you sure that the normals of surfaces are incorrect?

The normal of a face is determined by the normal of the underlying surface and the face orientation

If the face orientation is : TopAbs_Orientation.TopAbs_REVERSE the normal is the reversed normal of the surface else its the same.

An example here: FaceNormals

1027795176_159830's picture

This is part of my code, and I got some wrong normal directions by use BRepLProp_SLProps,
Can you give me some advice?thanks a lot

for (faceExplorer.Init(aShape, TopAbs_FACE); faceExplorer.More(); faceExplorer.Next())
{

TopLoc_Location loc ;
TopoDS_Face aFace = TopoDS::Face(faceExplorer.Current());
Handle_Poly_Triangulation triFace = BRep_Tool::Triangulation(aFace, loc);
BRepLProp_SLProps theProp(BRepAdaptor_Surface(aFace), 1, Precision::Confusion());

if (triFace.IsNull()) continue;

Standard_Boolean hasNormal = triFace->HasNormals();
Standard_Boolean hasuvNormal = triFace->HasUVNodes();

Standard_Integer l = triFace->Nodes().Length();
Standard_Integer nTriangles = triFace->NbTriangles();

TColgp_Array1OfPnt nodes(1, triFace->NbNodes());
Poly_Array1OfTriangle triangles(1, triFace->NbTriangles());
nodes = triFace->Nodes();
triangles = triFace->Triangles();

BRepAdaptor_Surface S;
S.Initialize(aFace, Standard_False);

for (Standard_Integer i = 1; i <= nTriangles; i++)
{
Poly_Triangle aTriangle = triangles.Value(i);
aTriangle.Get(nVertexIndex1, nVertexIndex2, nVertexIndex3);

vertex1 = nodes.Value(nVertexIndex1).Transformed(loc.Transformation());
vertex2 = nodes.Value(nVertexIndex2).Transformed(loc.Transformation());
vertex3 = nodes.Value(nVertexIndex3).Transformed(loc.Transformation());

gp_Pnt2d theUV1 = triFace->UVNodes().Value(aTriangle(1));
gp_Pnt2d theUV2 = triFace->UVNodes().Value(aTriangle(2));
gp_Pnt2d theUV3 = triFace->UVNodes().Value(aTriangle(3));

positionBuffer.push_back(aiVector3D());
aiVector3D* vt1 = &positionBuffer.back();
vt1->x = vertex1.X();
vt1->y = vertex1.Y();
vt1->z = vertex1.Z();

positionBuffer.push_back(aiVector3D());
aiVector3D* vt2 = &positionBuffer.back();
vt2->x = vertex2.X();
vt2->y = vertex2.Y();
vt2->z = vertex2.Z();

positionBuffer.push_back(aiVector3D());
aiVector3D* vt3 = &positionBuffer.back();
vt3->x = vertex3.X();
vt3->y = vertex3.Y();
vt3->z = vertex3.Z();

// find the normal for the triangle mesh.

gp_Vec V12(vertex1, vertex2);
gp_Vec V13(vertex1, vertex3);
gp_Vec theNormal = V12 ^ V13;
gp_Vec theNormal1 = theNormal;
gp_Vec theNormal2 = theNormal;
gp_Vec theNormal3 = theNormal;
if (theNormal.Magnitude() > Precision::Confusion())
{
theNormal.Normalize();
theNormal1.Normalize();
theNormal2.Normalize();
theNormal3.Normalize();
}

theProp.SetParameters(theUV1.X(), theUV1.Y());
if (theProp.IsNormalDefined())
{
theNormal1 = theProp.Normal();
}

theProp.SetParameters(theUV2.X(), theUV2.Y());
if (theProp.IsNormalDefined())
{
theNormal2 = theProp.Normal();
}

theProp.SetParameters(theUV3.X(), theUV3.Y());
if (theProp.IsNormalDefined())
{
theNormal3 = theProp.Normal();
}

if (aFace.Orientation() == TopAbs_REVERSED)
{
theNormal.Reverse();
theNormal1.Reverse();
theNormal2.Reverse();
theNormal3.Reverse();
}

normalBuffer.push_back(aiVector3D());
aiVector3D* vn1 = &normalBuffer.back();
vn1->x = theNormal1.X();
vn1->y = theNormal1.Y();
vn1->z = theNormal1.Z();

normalBuffer.push_back(aiVector3D());
aiVector3D* vn2 = &normalBuffer.back();
vn2->x = theNormal2.X();
vn2->y = theNormal2.Y();
vn2->z = theNormal2.Z();

normalBuffer.push_back(aiVector3D());
aiVector3D* vn3 = &normalBuffer.back();
vn3->x = theNormal3.X();
vn3->y = theNormal3.Y();
vn3->z = theNormal3.Z();

}
}

Sebastian Alff's picture

Oh wow, this thread is super old, but I wanted to share some insights I had today after researching the problem. There are many people out there complaining about this kind of issue, especially with IGES files. Here is my analysis and a potential solution of the issue. Its done in FreeCAD, but I assume the C++ solution would be quite similar.

1. When I import the IGES file into FreeCAD, there are some obvious issues with edges not being strictly coincident. (1.png)
2. When I create a tessellation from the file (via the menu item "Mesh > Create Mesh from Shape"), I get a lot of black spots on the mesh, indicating that the normals are flipped in the wrong direction (2.png)
3. I deleted the broken mesh from the 2nd step and instead chose "Part > Convert to Solid" from the menu. Then generated a tessellation from that item. (3.png)

Et voila: The tessellation now is correct. Now I just need to find out how to do this in code...

Attachments: 
Thomas Anderson's picture

You should examine the shape contents of the imported iges. Solid? Sheet? Compound of faces? iges models have no or poor support for sewn sheets and solid models. So:

1) you are seeing different meshing results for the 'same' edge.

2) no consistent orientation between surfaces.

3) This is probably using something like 'BRepBuilderAPI_Sewing'

Sebastian Alff's picture

Thomas, I think you're right about BRepBuilderAPI_Sewing.

I think I have it working now. Here is some JavaScript code for a function named "sewAndFixShape". It takes the "broken" shape from the IGES file (in my case coming from "reader.OneShape()"), repairs it and returns the repaired shape. It should work in a similar way in C++.

This code does the following:

  1. It sews the faces from the IGES shape and creates a bunch of shells (I had to increase the default sewing tolerance from 1e-6 to 1e-2, otherwise it wouldn't work). After this step, the face directions come out as consistent but are sometimes flipped to the wrong side for some "child-parts" in some test files.
  2. It then iterates all shells and adds them to a solid.
  3. I then use the ShapeFix_Shape class, which orients all faces of all solids to the right direction.
  sewAndFixShape(shape) {
    const sewingTool = new this.openCascade.BRepBuilderAPI_Sewing(1e-2);
    sewingTool.Load(shape);
    sewingTool.Perform();
    const sewedShape = sewingTool.SewedShape();

    const ExpShell = new this.openCascade.TopExp_Explorer();
    const makeSolid = new this.openCascade.BRepBuilderAPI_MakeSolid();
    for(ExpShell.Init(sewedShape, this.openCascade.TopAbs_SHELL); ExpShell.More(); ExpShell.Next()) {
      makeSolid.Add(this.openCascade.TopoDS.prototype.Shell(ExpShell.Current()));
    }
    const solidShape = makeSolid.Solid();

    const fixShape = new this.openCascade.ShapeFix_Shape(solidShape);
    fixShape.Perform();
    return fixShape.Shape();
  }

If anyone has some feedback on this approach or maybe knows a cleaner / simpler way of doing it - I would appreciate the feedback. Otherwise, this seems to work for my test files.

By the way, I'm using (and maintaining) a library called "opencascade.js" for running OpenCascade via WebAssembly in the browser (will update the missing API functions in the next days).

(PS: Sorry admins for posting this a hundred times - I realized too late that this post was getting held back from being published, because it contains a link to an external website)

Guido van Hilst not specified's picture

Hi Sebastian,

Running OpenCascade via WebAssembly is real cool, nice job!

Guido

Sebastian Alff's picture

Yeah, I also think it's cool ;-). All praise should go to Emscripten, which is able to compile arbitrary C++ code to JavaScript and WebAssembly.

Here is the link to the repo: https://github.com/donalffons/opencascade.js
The NPM package: https://www.npmjs.com/package/opencascade.js