diff --git a/src/RWStepVisual/RWStepVisual_RWComplexTriangulatedSurfaceSet.cxx b/src/RWStepVisual/RWStepVisual_RWComplexTriangulatedSurfaceSet.cxx index 0bd832d90a..ddf7af7495 100644 --- a/src/RWStepVisual/RWStepVisual_RWComplexTriangulatedSurfaceSet.cxx +++ b/src/RWStepVisual/RWStepVisual_RWComplexTriangulatedSurfaceSet.cxx @@ -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(); diff --git a/src/STEPCAFControl/STEPCAFControl_GDTProperty.cxx b/src/STEPCAFControl/STEPCAFControl_GDTProperty.cxx index ba81e4bd98..8732c92216 100644 --- a/src/STEPCAFControl/STEPCAFControl_GDTProperty.cxx +++ b/src/STEPCAFControl/STEPCAFControl_GDTProperty.cxx @@ -29,13 +29,271 @@ #include #include #include +#include #include #include #include +#include #include #include #include +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 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 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 aCurves = new StepVisual_VectorOfHSequenceOfInteger; - NCollection_Vector 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 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 aTesselatedItems = + new StepVisual_Array1OfTessellatedItem(1, static_cast(aCTSSs.size()) + 1); + aTesselatedItems->SetValue(1, aTCS); + for (size_t aCTSSIndex = 0; aCTSSIndex < aCTSSs.size(); ++aCTSSIndex) + { + aTesselatedItems->SetValue(static_cast(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 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; }