// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.
-#include <StlAPI_Writer.ixx>
-#include <StlTransfer.hxx>
-#include <TopoDS_Shape.hxx>
+
#include <Bnd_Box.hxx>
-#include <RWStl.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>
StlAPI_Writer::StlAPI_Writer()
{
return theASCIIMode;
}
-StlAPI_ErrorStatus StlAPI_Writer::Write(const TopoDS_Shape& theShape, const Standard_CString theFileName)
+// Auxiliary tools
+namespace
{
- OSD_Path aFile(theFileName);
- StlTransfer::RetrieveMesh(theShape, theStlMesh);
-
- if (theStlMesh.IsNull() || theStlMesh->IsEmpty())
- return StlAPI_MeshIsEmpty;
-
- // Write the built mesh
- Standard_Boolean wasFileOpened = Standard_False;
- if (theASCIIMode) {
- wasFileOpened = RWStl::WriteAscii(theStlMesh, aFile);
- }
- else {
- wasFileOpened = RWStl::WriteBinary(theStlMesh, aFile);
+ // 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;
}
- if (!wasFileOpened)
+ int NbTriangles () const { return myNbTriangles; }
+
+ // 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)
+ {
+#ifdef OCCT_BINARY_FILE_DO_INVERSE
+ return OSD_BinaryFile::InverseShortReal ((float)aValue);
+#else
+ return (float)aValue;
+#endif
+ }
+}
+
+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;
- return StlAPI_StatusOK;
+ // write
+ if (theASCIIMode)
+ {
+ // header
+ Fprintf (aFile, "solid shape, STL ascii file, created with Open CASCADE Technology\n");
+
+ // 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");
+
+ // number of facets
+ int32_t aNbTri = 0;
+ for (TopExp_Explorer exp (theShape, TopAbs_FACE); exp.More(); exp.Next())
+ {
+ TopLoc_Location aLoc;
+ Handle(Poly_Triangulation) aPoly =
+ BRep_Tool::Triangulation (TopoDS::Face (exp.Current()), aLoc);
+ if (! aPoly.IsNull())
+ aNbTri += aPoly->NbTriangles();
+ }
+ // 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())
+ {
+ 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());
+
+ 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());
+
+ f.x3 = convertFloat (aPnt3.X());
+ f.y3 = convertFloat (aPnt3.Y());
+ f.z3 = convertFloat (aPnt3.Z());
+
+ fwrite (&f, 50 /* 50 bytes per facet */, 1, aFile);
+ }
+ }
+ }
+
+ fclose (aFile);
+ return ferror(aFile) ? StlAPI_WriteError : StlAPI_StatusOK;
}