]> OCCT Git - occt.git/commitdiff
0033498: Data Exchange, Step Export - Meshed pretessellated geometry is skipped on...
authordkulikov <dkulikov@opencascade.com>
Fri, 27 Sep 2024 14:14:57 +0000 (14:14 +0000)
committerdpasukhi <dpasukhi@opencascade.com>
Sat, 28 Sep 2024 15:59:02 +0000 (15:59 +0000)
Processing of complex_triangulated_surface_set is added to
  STEPCAFControl_GDTProperty::GetTessellation().
Processing of tessellated_curve_set is refactored and moved to a
  separate function.

Problems related to normals list are fixed in
RWStepVisual_RWComplexTriangulatedSurfaceSet::WriteStep():
  Normals list is now checked for nullptr. It is a valid situation that
  occurs when complex_triangulated_surface_set in STEP file has no
  normals.
  Traversing of normals list is performed in correct order. Previously
  rows and columns were switched, which led to crashes or incorrect data.

src/RWStepVisual/RWStepVisual_RWComplexTriangulatedSurfaceSet.cxx
src/STEPCAFControl/STEPCAFControl_GDTProperty.cxx

index 0bd832d90acf623c7a5eef0e5da3d706d0576e4a..ddf7af7495f4614ffe6ea9bf77a58a5fb3b54978 100644 (file)
@@ -180,16 +180,23 @@ void RWStepVisual_RWComplexTriangulatedSurfaceSet::WriteStep(
   theSW.Send(theEnt->Pnmax());
 
   theSW.OpenSub();
-  for (Standard_Integer i3 = 1; i3 <= theEnt->Normals()->RowLength(); i3++)
+  // According to "Recommended Practices Recommended Practices for 3D Tessellated Geometry", Release 1.1:
+  // "...The size of the list of normals may be:
+  //    0: no normals are defined..."
+  // In OCC this situation is reflected by nullptr normals container.
+  if (theEnt->NbNormals() != 0)
   {
-    theSW.NewLine(Standard_False);
-    theSW.OpenSub();
-    for (Standard_Integer j3 = 1; j3 <= theEnt->Normals()->ColLength(); j3++)
+    for (Standard_Integer i3 = 1; i3 <= theEnt->Normals()->NbRows(); i3++)
     {
-      Standard_Real Var0 = theEnt->Normals()->Value(i3,j3);
-      theSW.Send(Var0);
+      theSW.NewLine(Standard_False);
+      theSW.OpenSub();
+      for (Standard_Integer j3 = 1; j3 <= theEnt->Normals()->NbColumns(); j3++)
+      {
+        Standard_Real Var0 = theEnt->Normals()->Value(i3, j3);
+        theSW.Send(Var0);
+      }
+      theSW.CloseSub();
     }
-    theSW.CloseSub();
   }
   theSW.CloseSub();
 
index ba81e4bd989134b3b62cfd7339be27ea54b4e5a5..8732c92216281d4fe2c5658df5426d9e06d4850c 100644 (file)
 #include <StepDimTol_StraightnessTolerance.hxx>
 #include <StepDimTol_SurfaceProfileTolerance.hxx>
 #include <StepRepr_DescriptiveRepresentationItem.hxx>
+#include <StepVisual_ComplexTriangulatedSurfaceSet.hxx>
 #include <StepVisual_TessellatedCurveSet.hxx>
 #include <TopExp_Explorer.hxx>
 #include <TopoDS.hxx>
+#include <TopoDS_Face.hxx>
 #include <TopoDS_Shape.hxx>
 #include <XCAFDimTolObjects_DatumModifiersSequence.hxx>
 #include <XCAFDimTolObjects_DatumModifWithValue.hxx>
 
+namespace
+{
+  //=======================================================================
+  //function : GenerateCoordinateList
+  //purpose  : Generates a coordinate_list by filling it with coordinates
+  //           of the nodes of theTriangulation. Each node will be
+  //           transformed with theTransformation.
+  //=======================================================================
+  Handle(StepVisual_CoordinatesList) GenerateCoordinateList(
+    const Handle(Poly_Triangulation)& theTriangulation,
+    const gp_Trsf&                    theTransformation)
+  {
+    Handle(TColgp_HArray1OfXYZ) thePoints = new TColgp_HArray1OfXYZ(1, theTriangulation->NbNodes());
+    for (Standard_Integer aNodeIndex = 1; aNodeIndex <= theTriangulation->NbNodes(); ++aNodeIndex)
+    {
+      const gp_Pnt aCurrentNode = theTriangulation->Node(aNodeIndex).Transformed(theTransformation);
+      thePoints->SetValue(aNodeIndex, aCurrentNode.XYZ());
+    }
+    Handle(StepVisual_CoordinatesList) aCoordinatesList = new StepVisual_CoordinatesList;
+    aCoordinatesList->Init(new TCollection_HAsciiString(), thePoints);
+    return aCoordinatesList;
+  }
+
+  //=======================================================================
+  //function : CountNormals
+  //purpose  : Returns a number of normals that theTriangulation contains
+  //           for the purpose of generating
+  //           StepVisual_ComplexTriangulatedSurfaceSet.
+  //           Possible outputs are:
+  //           0, if theTriangulation has no normals.
+  //           1, if all normals contained in theTriangulation are equal.
+  //             Note that Poly_Triangulation supports only 2 options:
+  //             either no normals or a normal assosciated with each node.
+  //             So when source complex_triangulated_surface_set has just
+  //             one normal, it will be just associated with every node in
+  //             Poly_Triangulation. Return value of one indicates that
+  //             that's what probably happen during reading.
+  //           theTriangulation->NbNodes(), if each vertex has a unique
+  //             node ossociated with it.
+  //=======================================================================
+  Standard_Integer CountNormals(const Handle(Poly_Triangulation)& theTriangulation)
+  {
+    if (!theTriangulation->HasNormals())
+    {
+      return 0;
+    }
+
+    // Function to compare normal coordinates values.
+    auto isEqual = [](const Standard_Real theVal1, const Standard_Real theVal2)
+    {
+      return std::abs(theVal1 - theVal2) < Precision::Confusion();
+    };
+    // Checking if all normals are equal.
+    const gp_Dir aReferenceNormal = theTriangulation->Normal(1);
+    for (Standard_Integer aNodeIndex = 1; aNodeIndex <= theTriangulation->NbNodes(); ++aNodeIndex)
+    {
+      const gp_Dir aCurrentNormal = theTriangulation->Normal(aNodeIndex);
+      if (!isEqual(aReferenceNormal.X(), aCurrentNormal.X())
+          || !isEqual(aReferenceNormal.Y(), aCurrentNormal.Y())
+          || !isEqual(aReferenceNormal.Z(), aCurrentNormal.Z()))
+      {
+        return theTriangulation->NbNodes();
+      }
+    }
+
+    // All normals were equal, so we can use just one normal.
+    return 1;
+  }
+
+  //=======================================================================
+  //function : GenerateNormalsArray
+  //purpose  : Generates array of normals from theTriangulation. Normals
+  //           wiil be transformed with theTransformation.
+  //           IMPORTANT: Output will be nullptr if theTriangulation has
+  //           no normals.
+  //=======================================================================
+  Handle(TColStd_HArray2OfReal) GenerateNormalsArray(
+    const Handle(Poly_Triangulation)& theTriangulation,
+    const gp_Trsf&                    theTransformation)
+  {
+    const Standard_Integer aNormalCount = CountNormals(theTriangulation);
+    if (aNormalCount == 0)
+    {
+      return nullptr;
+    }
+    else if (aNormalCount == 1)
+    {
+      Handle(TColStd_HArray2OfReal) aNormals = new TColStd_HArray2OfReal(1, 1, 1, 3);
+      const gp_Dir aNormal = theTriangulation->Normal(1).Transformed(theTransformation);
+      aNormals->SetValue(1, 1, aNormal.X());
+      aNormals->SetValue(1, 2, aNormal.Y());
+      aNormals->SetValue(1, 3, aNormal.Z());
+      return aNormals;
+    }
+    else
+    {
+      Handle(TColStd_HArray2OfReal) aNormals =
+        new TColStd_HArray2OfReal(1, theTriangulation->NbNodes(), 1, 3);
+
+      for (Standard_Integer aNodeIndex = 1; aNodeIndex <= theTriangulation->NbNodes(); ++aNodeIndex)
+      {
+        const gp_Dir aCurrentNormal =
+          theTriangulation->Normal(aNodeIndex).Transformed(theTransformation);
+        aNormals->SetValue(aNodeIndex, 1, aCurrentNormal.X());
+        aNormals->SetValue(aNodeIndex, 2, aCurrentNormal.Y());
+        aNormals->SetValue(aNodeIndex, 3, aCurrentNormal.Z());
+      }
+      return aNormals;
+    }
+  }
+
+  //=======================================================================
+  //function : GenerateTriangleStrips
+  //purpose  : Generates an array of triangle strips from theTriangulation.
+  //           Since Poly_Triangulation doesn't support triangle strips,
+  //           all triangles from it would just be imported as tringle
+  //           strips of one triangle.
+  //=======================================================================
+  Handle(TColStd_HArray1OfTransient) GenerateTriangleStrips(
+    const Handle(Poly_Triangulation)& theTriangulation)
+  {
+    Handle(TColStd_HArray1OfTransient) aTriangleStrips =
+      new TColStd_HArray1OfTransient(1, theTriangulation->NbTriangles());
+    for (Standard_Integer aTriangleIndex = 1; aTriangleIndex <= theTriangulation->NbTriangles();
+         ++aTriangleIndex)
+    {
+      // Since Poly_Triangulation doesn't support triangle strips or triangle fans,
+      // we just write each thriangle as triangle strip.
+      const Poly_Triangle& aCurrentTriangle           = theTriangulation->Triangle(aTriangleIndex);
+      Handle(TColStd_HArray1OfInteger) aTriangleStrip = new TColStd_HArray1OfInteger(1, 3);
+      aTriangleStrip->SetValue(1, aCurrentTriangle.Value(1));
+      aTriangleStrip->SetValue(2, aCurrentTriangle.Value(2));
+      aTriangleStrip->SetValue(3, aCurrentTriangle.Value(3));
+      aTriangleStrips->SetValue(aTriangleIndex, aTriangleStrip);
+    }
+    return aTriangleStrips;
+  }
+
+  //=======================================================================
+  //function : GenerateComplexTriangulatedSurfaceSet
+  //purpose  : Generates complex_triangulated_surface_set from theFace.
+  //           Returns nullptr if face has no triangulation.
+  //=======================================================================
+  Handle(StepVisual_ComplexTriangulatedSurfaceSet) GenerateComplexTriangulatedSurfaceSet(
+    const TopoDS_Face& theFace)
+  {
+    TopLoc_Location                  aFaceLoc;
+    const Handle(Poly_Triangulation) aTriangulation = BRep_Tool::Triangulation(theFace, aFaceLoc);
+    if (aTriangulation.IsNull())
+    {
+      return nullptr;
+    }
+    const gp_Trsf aFaceTransform = aFaceLoc.Transformation();
+
+    // coordinates
+    Handle(StepVisual_CoordinatesList) aCoordinatesList = GenerateCoordinateList(aTriangulation,
+                                                                                 aFaceTransform);
+    // pnmax
+    Standard_Integer aPnmax = aTriangulation->NbNodes();
+    // normals
+    Handle(TColStd_HArray2OfReal) aNormals = GenerateNormalsArray(aTriangulation, aFaceTransform);
+    // pnindex
+    // From "Recommended Practices Recommended Practices for 3D Tessellated Geometry", Release 1.1:
+    // "pnindex is the table of indices of the points used in the definition of the triangles.
+    //  It is an index to the coordinates_list. Its size may be:
+    //  pnmax: this is the size of normals when each point has a normal.
+    //  0: no indirection."
+    // In our case there is no indirection, so it's always empty.
+    Handle(TColStd_HArray1OfInteger) aPnindex = new TColStd_HArray1OfInteger;
+    // triangle_strips
+    Handle(TColStd_HArray1OfTransient) aTriangleStrips = GenerateTriangleStrips(aTriangulation);
+    // triangle_fans
+    // All triangles were already written as triangle strips.
+    Handle(TColStd_HArray1OfTransient) aTriangleFans = new TColStd_HArray1OfTransient;
+
+    // Initialization of complex_triangulated_surface_set.
+    Handle(StepVisual_ComplexTriangulatedSurfaceSet) aCTSS =
+      new StepVisual_ComplexTriangulatedSurfaceSet;
+    aCTSS->Init(new TCollection_HAsciiString(),
+                aCoordinatesList,
+                aPnmax,
+                aNormals,
+                aPnindex,
+                aTriangleStrips,
+                aTriangleFans);
+    return aCTSS;
+  }
+
+  //=======================================================================
+  //function : GenerateTessellatedCurveSet
+  //purpose  : Generates tesselated_curve_set from theShape.
+  //           If no valid curves were found, return nullptr.
+  //=======================================================================
+  Handle(StepVisual_TessellatedCurveSet) GenerateTessellatedCurveSet(const TopoDS_Shape& theShape)
+  {
+    NCollection_Handle<StepVisual_VectorOfHSequenceOfInteger> aLineStrips =
+      new StepVisual_VectorOfHSequenceOfInteger;
+    // Temporary contanier for points. We need points in TColgp_HArray1OfXYZ type of
+    // container, however in order to create it we need to know it's size.
+    // Currently number of points is unknown, so we will put all the points in a
+    // temporary container and then just copy them after all edges will be processed.
+    NCollection_Vector<gp_XYZ> aTmpPointsContainer;
+    for (TopExp_Explorer aCurveIt(theShape, TopAbs_EDGE); aCurveIt.More(); aCurveIt.Next())
+    {
+      // Find out type of edge curve
+      Standard_Real            aFirstParam = 0, aLastParam = 0;
+      const Handle(Geom_Curve) anEdgeCurve = BRep_Tool::Curve(TopoDS::Edge(aCurveIt.Current()),
+                                                              aFirstParam,
+                                                              aLastParam);
+      if (anEdgeCurve.IsNull())
+      {
+        continue;
+      }
+      Handle(TColStd_HSequenceOfInteger) aCurrentCurve = new TColStd_HSequenceOfInteger;
+      if (anEdgeCurve->IsKind(STANDARD_TYPE(Geom_Line))) // Line
+      {
+        for (TopExp_Explorer aVertIt(aCurveIt.Current(), TopAbs_VERTEX); aVertIt.More();
+             aVertIt.Next())
+        {
+          aTmpPointsContainer.Append(BRep_Tool::Pnt(TopoDS::Vertex(aVertIt.Current())).XYZ());
+          aCurrentCurve->Append(aTmpPointsContainer.Size());
+        }
+      }
+      else // BSpline
+      {
+        ShapeConstruct_Curve      aSCC;
+        Handle(Geom_BSplineCurve) aBSCurve =
+          aSCC.ConvertToBSpline(anEdgeCurve, aFirstParam, aLastParam, Precision::Confusion());
+        for (Standard_Integer aPoleIndex = 1; aPoleIndex <= aBSCurve->NbPoles(); ++aPoleIndex)
+        {
+          aTmpPointsContainer.Append(aBSCurve->Pole(aPoleIndex).XYZ());
+          aCurrentCurve->Append(aTmpPointsContainer.Size());
+        }
+      }
+      aLineStrips->Append(aCurrentCurve);
+    }
+
+    if (aTmpPointsContainer.IsEmpty())
+    {
+      return Handle(StepVisual_TessellatedCurveSet){};
+    }
+
+    Handle(TColgp_HArray1OfXYZ) aPoints = new TColgp_HArray1OfXYZ(1, aTmpPointsContainer.Size());
+    for (Standard_Integer aPointIndex = 1; aPointIndex <= aPoints->Size(); ++aPointIndex)
+    {
+      aPoints->SetValue(aPointIndex, aTmpPointsContainer.Value(aPointIndex - 1));
+    }
+    // STEP entities
+    Handle(StepVisual_CoordinatesList) aCoordinates = new StepVisual_CoordinatesList();
+    aCoordinates->Init(new TCollection_HAsciiString(), aPoints);
+    Handle(StepVisual_TessellatedCurveSet) aTCS = new StepVisual_TessellatedCurveSet();
+    aTCS->Init(new TCollection_HAsciiString(), aCoordinates, aLineStrips);
+    return aTCS;
+  }
+}
+
 //=======================================================================
 //function : STEPCAFControl_GDTProperty
 //purpose  : 
@@ -1305,61 +1563,33 @@ Handle(TCollection_HAsciiString) STEPCAFControl_GDTProperty::GetTolValueType(con
 
 //=======================================================================
 //function : GetTessellation
-//purpose  : 
+//purpose  :
 //=======================================================================
-Handle(StepVisual_TessellatedGeometricSet) STEPCAFControl_GDTProperty::GetTessellation(const TopoDS_Shape& theShape)
+Handle(StepVisual_TessellatedGeometricSet) STEPCAFControl_GDTProperty::GetTessellation(
+  const TopoDS_Shape& theShape)
 {
-  Handle(StepVisual_TessellatedGeometricSet) aGeomSet;
-  // Build coordinate list and curves
-  NCollection_Handle<StepVisual_VectorOfHSequenceOfInteger> aCurves = new StepVisual_VectorOfHSequenceOfInteger;
-  NCollection_Vector<gp_XYZ> aCoords;
-  Standard_Integer aPntNb = 1;
-  for (TopExp_Explorer aCurveIt(theShape, TopAbs_EDGE); aCurveIt.More(); aCurveIt.Next()) {
-    Handle(TColStd_HSequenceOfInteger) aCurve = new TColStd_HSequenceOfInteger;
-    // Find out type of edge curve
-    Standard_Real aFirst = 0, aLast = 0;
-    Handle(Geom_Curve) anEdgeCurve = BRep_Tool::Curve(TopoDS::Edge(aCurveIt.Current()), aFirst, aLast);
-    if (anEdgeCurve.IsNull())
-      continue;
-    // Line
-    if (anEdgeCurve->IsKind(STANDARD_TYPE(Geom_Line))) {
-      for (TopExp_Explorer aVertIt(aCurveIt.Current(), TopAbs_VERTEX); aVertIt.More(); aVertIt.Next()) {
-        aCoords.Append(BRep_Tool::Pnt(TopoDS::Vertex(aVertIt.Current())).XYZ());
-        aCurve->Append(aPntNb);
-        aPntNb++;
-      }
-    }
-    // BSpline
-    else {
-      ShapeConstruct_Curve aSCC;
-      Handle(Geom_BSplineCurve) aBSCurve = aSCC.ConvertToBSpline(anEdgeCurve,
-          aFirst, aLast, Precision::Confusion());
-      for (Standard_Integer i = 1; i <= aBSCurve->NbPoles(); i++) {
-        aCoords.Append(aBSCurve->Pole(i).XYZ());
-        aCurve->Append(aPntNb);
-        aPntNb++;
-      }
-    }
-    aCurves->Append(aCurve);
-  }
-
-  if (!aCoords.Length())
+  // Build complex_triangulated_surface_set.
+  std::vector<Handle(StepVisual_ComplexTriangulatedSurfaceSet)> aCTSSs;
+  for (TopExp_Explorer aFaceIt(theShape, TopAbs_FACE); aFaceIt.More(); aFaceIt.Next())
   {
-    return aGeomSet;
+    const TopoDS_Face aFace = TopoDS::Face(aFaceIt.Current());
+    aCTSSs.emplace_back(GenerateComplexTriangulatedSurfaceSet(aFace));
   }
 
-  Handle(TColgp_HArray1OfXYZ) aPoints = new TColgp_HArray1OfXYZ(1, aCoords.Length());
-  for (Standard_Integer i = 1; i <= aPoints->Length(); i++) {
-    aPoints->SetValue(i, aCoords.Value(i - 1));
+  // Build tesselated_curve_set.
+  Handle(StepVisual_TessellatedCurveSet) aTCS = GenerateTessellatedCurveSet(theShape);
+  
+  // Fill the container of tesselated items.
+  NCollection_Handle<StepVisual_Array1OfTessellatedItem> aTesselatedItems =
+    new StepVisual_Array1OfTessellatedItem(1, static_cast<Standard_Integer>(aCTSSs.size()) + 1);
+  aTesselatedItems->SetValue(1, aTCS);
+  for (size_t aCTSSIndex = 0; aCTSSIndex < aCTSSs.size(); ++aCTSSIndex)
+  {
+    aTesselatedItems->SetValue(static_cast<Standard_Integer>(aCTSSIndex) + 2, aCTSSs[aCTSSIndex]);
   }
-  // STEP entities
-  Handle(StepVisual_CoordinatesList) aCoordList = new StepVisual_CoordinatesList();
-  aCoordList->Init(new TCollection_HAsciiString(), aPoints);
-  Handle(StepVisual_TessellatedCurveSet) aCurveSet = new StepVisual_TessellatedCurveSet();
-  aCurveSet->Init(new TCollection_HAsciiString(), aCoordList, aCurves);
-  NCollection_Handle<StepVisual_Array1OfTessellatedItem> aTessItems = new StepVisual_Array1OfTessellatedItem(1, 1);
-  aTessItems->SetValue(1, aCurveSet);
-  aGeomSet = new StepVisual_TessellatedGeometricSet();
-  aGeomSet->Init(new TCollection_HAsciiString(), aTessItems);
-  return aGeomSet;
+  
+  // Build tessellated_geometric_set.
+  Handle(StepVisual_TessellatedGeometricSet) aTGS = new StepVisual_TessellatedGeometricSet();
+  aTGS->Init(new TCollection_HAsciiString(), aTesselatedItems);
+  return aTGS;
 }