0028840: Data Exchange - rewrite the STL Reader/Writer
[occt.git] / src / StlAPI / StlAPI_Writer.cxx
index 1b4c6fe..5defbc4 100644 (file)
 // Alternatively, this file may be used under the terms of Open CASCADE
 // commercial license or contractual agreement.
 
+#include <StlAPI_Writer.hxx>
 
 #include <Bnd_Box.hxx>
 #include <BRepBndLib.hxx>
 #include <OSD_Path.hxx>
 #include <OSD_OpenFile.hxx>
 #include <RWStl.hxx>
-#include <StlAPI_Writer.hxx>
-#include <StlMesh_Mesh.hxx>
-#include <StlTransfer.hxx>
 #include <BRep_Tool.hxx>
 #include <TopoDS.hxx>
 #include <TopoDS_Face.hxx>
 #include <TopExp_Explorer.hxx>
 #include <Poly_Triangulation.hxx>
 
+//=============================================================================
+//function : StlAPI_Writer
+//purpose  :
+//=============================================================================
 StlAPI_Writer::StlAPI_Writer()
+: myASCIIMode (Standard_True)
 {
-  theStlMesh = new StlMesh_Mesh;
-  theASCIIMode = Standard_True;
-}
-
-Standard_Boolean& StlAPI_Writer::ASCIIMode() 
-{
-  return theASCIIMode;
+  //
 }
 
-// Auxiliary tools
-namespace
+//=============================================================================
+//function : Write
+//purpose  :
+//=============================================================================
+Standard_Boolean StlAPI_Writer::Write (const TopoDS_Shape&    theShape,
+                                       const Standard_CString theFileName)
 {
-  // Tool to get triangles from triangulation taking into account face
-  // orientation and location
-  class TriangleAccessor
-  {
-  public:
-    TriangleAccessor (const TopoDS_Face& aFace)
-    {
-      TopLoc_Location aLoc;
-      myPoly = BRep_Tool::Triangulation (aFace, aLoc);
-      myTrsf = aLoc.Transformation();
-      myNbTriangles = (myPoly.IsNull() ? 0 : myPoly->Triangles().Length());
-      myInvert = (aFace.Orientation() == TopAbs_REVERSED);
-      if (myTrsf.IsNegative())
-        myInvert = ! myInvert;
-    }
-
-    int NbTriangles () const { return myNbTriangles; } 
+  Standard_Integer aNbNodes = 0;
+  Standard_Integer aNbTriangles = 0;
 
-    // get i-th triangle and outward normal
-    void GetTriangle (int iTri, gp_Vec &theNormal, gp_Pnt &thePnt1, gp_Pnt &thePnt2, gp_Pnt &thePnt3)
-    {
-      // get positions of nodes
-      int iNode1, iNode2, iNode3;
-      myPoly->Triangles()(iTri).Get (iNode1, iNode2, iNode3);
-      thePnt1 = myPoly->Nodes()(iNode1);
-      thePnt2 = myPoly->Nodes()(myInvert ? iNode3 : iNode2);
-      thePnt3 = myPoly->Nodes()(myInvert ? iNode2 : iNode3);
-
-      // apply transormation if not identity
-      if (myTrsf.Form() != gp_Identity)
-      {
-        thePnt1.Transform (myTrsf);
-        thePnt2.Transform (myTrsf);
-        thePnt3.Transform (myTrsf);
-      }
-
-      // calculate normal
-      theNormal = (thePnt2.XYZ() - thePnt1.XYZ()) ^ (thePnt3.XYZ() - thePnt1.XYZ());
-      Standard_Real aNorm = theNormal.Magnitude();
-      if (aNorm > gp::Resolution())
-        theNormal /= aNorm;
-    }
-
-  private:
-    Handle(Poly_Triangulation) myPoly;
-    gp_Trsf myTrsf;
-    int myNbTriangles;
-    bool myInvert;
-  };
-
-  // convert to float and, on big-endian platform, to little-endian representation
-  inline float convertFloat (Standard_Real aValue)
+  // calculate total number of the nodes and triangles
+  for (TopExp_Explorer anExpSF (theShape, TopAbs_FACE); anExpSF.More(); anExpSF.Next())
   {
-#ifdef OCCT_BINARY_FILE_DO_INVERSE
-    return OSD_BinaryFile::InverseShortReal ((float)aValue);
-#else
-    return (float)aValue;
-#endif
+    TopLoc_Location aLoc;
+    Handle(Poly_Triangulation) aTriangulation = BRep_Tool::Triangulation (TopoDS::Face (anExpSF.Current()), aLoc);
+    aNbNodes     += aTriangulation->NbNodes();
+    aNbTriangles += aTriangulation->NbTriangles();
   }
-}
 
-StlAPI_ErrorStatus StlAPI_Writer::Write(const TopoDS_Shape& theShape, const Standard_CString theFileName)
-{
-  // open file
-  FILE* aFile = OSD_OpenFile (theFileName, "wb");
-  if (!aFile)
-    return StlAPI_CannotOpenFile;
+  // create temporary triangulation
+  Handle(Poly_Triangulation) aMesh = new Poly_Triangulation (aNbNodes, aNbTriangles, Standard_False);
 
-  // write
-  if (theASCIIMode)
+  // fill temporary triangulation
+  Standard_Integer aNodeOffset = 0;
+  Standard_Integer aTriangleOffet = 0;
+  for (TopExp_Explorer anExpSF (theShape, TopAbs_FACE); anExpSF.More(); anExpSF.Next())
   {
-    // header 
-    Fprintf (aFile, "solid shape, STL ascii file, created with Open CASCADE Technology\n");
+    TopLoc_Location aLoc;
+    Handle(Poly_Triangulation) aTriangulation = BRep_Tool::Triangulation (TopoDS::Face (anExpSF.Current()), aLoc);
 
-    // facets
-    for (TopExp_Explorer exp (theShape, TopAbs_FACE); exp.More(); exp.Next())
-    {
-      TriangleAccessor aTool (TopoDS::Face (exp.Current()));
-      for (int iTri = 1; iTri <= aTool.NbTriangles(); iTri++)
-      {
-        gp_Vec aNorm;
-        gp_Pnt aPnt1, aPnt2, aPnt3;
-        aTool.GetTriangle (iTri, aNorm, aPnt1, aPnt2, aPnt3);
-
-        Fprintf (aFile,
-          " facet normal %12e %12e %12e\n"
-          "   outer loop\n"
-          "     vertex %12e %12e %12e\n"
-          "     vertex %12e %12e %12e\n"
-          "     vertex %12e %12e %12e\n"
-          "   endloop\n"
-          " endfacet\n",
-          aNorm.X(), aNorm.Y(), aNorm.Z(),
-          aPnt1.X(), aPnt1.Y(), aPnt1.Z(),
-          aPnt2.X(), aPnt2.Y(), aPnt2.Z(),
-          aPnt3.X(), aPnt3.Y(), aPnt3.Z());
-      }
-    }
-
-    // footer
-    Fprintf (aFile, "endsolid shape\n");
-  }
-  else
-  {
-    // header block (meaningless 80 bytes)
-    Fprintf (aFile, "%-80.80s", "STL binary file, created with Open CASCADE Technology");
+    const TColgp_Array1OfPnt& aNodes = aTriangulation->Nodes();
+    const Poly_Array1OfTriangle& aTriangles = aTriangulation->Triangles();
 
-    // number of facets
-    int32_t aNbTri = 0;
-    for (TopExp_Explorer exp (theShape, TopAbs_FACE); exp.More(); exp.Next())
+    // copy nodes
+    gp_Trsf aTrsf = aLoc.Transformation();
+    for (Standard_Integer aNodeIter = aNodes.Lower(); aNodeIter <= aNodes.Upper(); ++aNodeIter)
     {
-      TopLoc_Location aLoc;
-      Handle(Poly_Triangulation) aPoly =
-        BRep_Tool::Triangulation (TopoDS::Face (exp.Current()), aLoc);
-      if (! aPoly.IsNull())
-        aNbTri += aPoly->NbTriangles();
+      gp_Pnt aPnt = aNodes (aNodeIter);
+      aPnt.Transform (aTrsf);
+      aMesh->ChangeNode (aNodeIter + aNodeOffset) = aPnt;
     }
-    // suppose that number of triangles must be little endian...
-#ifdef OCCT_BINARY_FILE_DO_INVERSE
-    aNbTri = OSD_BinaryFile::InverseInteger (aNbTri);
-#endif
-    fwrite (&aNbTri, sizeof(int32_t), 1, aFile);
 
-    // facets
-    struct Facet {
-      float nx, ny, nz;
-      float x1, y1, z1;
-      float x2, y2, z2;
-      float x3, y3, z3;
-      uint16_t dummy;
-    } f;
-    f.dummy = 0;
-    for (TopExp_Explorer exp (theShape, TopAbs_FACE); exp.More(); exp.Next())
+    // copy triangles
+    const TopAbs_Orientation anOrientation = anExpSF.Current().Orientation();
+    for (Standard_Integer aTriIter = aTriangles.Lower(); aTriIter <= aTriangles.Upper(); ++aTriIter)
     {
-      TriangleAccessor aTool (TopoDS::Face (exp.Current()));
-      for (int iTri = 1; iTri <= aTool.NbTriangles(); iTri++)
-      {
-        gp_Vec aNorm;
-        gp_Pnt aPnt1, aPnt2, aPnt3;
-        aTool.GetTriangle (iTri, aNorm, aPnt1, aPnt2, aPnt3);
-
-        f.nx = convertFloat (aNorm.X());
-        f.ny = convertFloat (aNorm.Y());
-        f.nz = convertFloat (aNorm.Z());
+      Poly_Triangle aTri = aTriangles (aTriIter);
 
-        f.x1 = convertFloat (aPnt1.X());
-        f.y1 = convertFloat (aPnt1.Y());
-        f.z1 = convertFloat (aPnt1.Z());
-
-        f.x2 = convertFloat (aPnt2.X());
-        f.y2 = convertFloat (aPnt2.Y());
-        f.z2 = convertFloat (aPnt2.Z());
+      Standard_Integer anId[3];
+      aTri.Get (anId[0], anId[1], anId[2]);
+      if (anOrientation == TopAbs_REVERSED)
+      {
+        // Swap 1, 2.
+        Standard_Integer aTmpIdx = anId[1];
+        anId[1] = anId[2];
+        anId[2] = aTmpIdx;
+      }
 
-        f.x3 = convertFloat (aPnt3.X());
-        f.y3 = convertFloat (aPnt3.Y());
-        f.z3 = convertFloat (aPnt3.Z());
+      // Update nodes according to the offset.
+      anId[0] += aNodeOffset;
+      anId[1] += aNodeOffset;
+      anId[2] += aNodeOffset;
 
-        fwrite (&f, 50 /* 50 bytes per facet */, 1, aFile);
-      }
+      aTri.Set (anId[0], anId[1], anId[2]);
+      aMesh->ChangeTriangle (aTriIter + aTriangleOffet) =  aTri;
     }
+
+    aNodeOffset += aNodes.Size();
+    aTriangleOffet += aTriangles.Size();
   }
 
-  fclose (aFile);
-  return ferror(aFile) ? StlAPI_WriteError : StlAPI_StatusOK;
+  OSD_Path aPath (theFileName);
+  return myASCIIMode
+       ? RWStl::WriteAscii  (aMesh, aPath)
+       : RWStl::WriteBinary (aMesh, aPath);
 }
-