0026338: STL export (especially binary) needs a lot of time if selected export path...
[occt.git] / src / StlAPI / StlAPI_Writer.cxx
1 // Copyright (c) 1999-2014 OPEN CASCADE SAS
2 //
3 // This file is part of Open CASCADE Technology software library.
4 //
5 // This library is free software; you can redistribute it and/or modify it under
6 // the terms of the GNU Lesser General Public License version 2.1 as published
7 // by the Free Software Foundation, with special exception defined in the file
8 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
9 // distribution for complete text of the license and disclaimer of any warranty.
10 //
11 // Alternatively, this file may be used under the terms of Open CASCADE
12 // commercial license or contractual agreement.
13
14
15 #include <Bnd_Box.hxx>
16 #include <BRepBndLib.hxx>
17 #include <OSD_Path.hxx>
18 #include <OSD_OpenFile.hxx>
19 #include <RWStl.hxx>
20 #include <StlAPI_Writer.hxx>
21 #include <StlMesh_Mesh.hxx>
22 #include <StlTransfer.hxx>
23 #include <BRep_Tool.hxx>
24 #include <TopoDS.hxx>
25 #include <TopoDS_Face.hxx>
26 #include <TopExp_Explorer.hxx>
27 #include <Poly_Triangulation.hxx>
28
29 StlAPI_Writer::StlAPI_Writer()
30 {
31   theStlMesh = new StlMesh_Mesh;
32   theASCIIMode = Standard_True;
33 }
34
35 Standard_Boolean& StlAPI_Writer::ASCIIMode() 
36 {
37   return theASCIIMode;
38 }
39
40 // Auxiliary tools
41 namespace
42 {
43   // Tool to get triangles from triangulation taking into account face
44   // orientation and location
45   class TriangleAccessor
46   {
47   public:
48     TriangleAccessor (const TopoDS_Face& aFace)
49     {
50       TopLoc_Location aLoc;
51       myPoly = BRep_Tool::Triangulation (aFace, aLoc);
52       myTrsf = aLoc.Transformation();
53       myNbTriangles = (myPoly.IsNull() ? 0 : myPoly->Triangles().Length());
54       myInvert = (aFace.Orientation() == TopAbs_REVERSED);
55       if (myTrsf.IsNegative())
56         myInvert = ! myInvert;
57     }
58
59     int NbTriangles () const { return myNbTriangles; } 
60
61     // get i-th triangle and outward normal
62     void GetTriangle (int iTri, gp_Vec &theNormal, gp_Pnt &thePnt1, gp_Pnt &thePnt2, gp_Pnt &thePnt3)
63     {
64       // get positions of nodes
65       int iNode1, iNode2, iNode3;
66       myPoly->Triangles()(iTri).Get (iNode1, iNode2, iNode3);
67       thePnt1 = myPoly->Nodes()(iNode1);
68       thePnt2 = myPoly->Nodes()(myInvert ? iNode3 : iNode2);
69       thePnt3 = myPoly->Nodes()(myInvert ? iNode2 : iNode3);
70
71       // apply transormation if not identity
72       if (myTrsf.Form() != gp_Identity)
73       {
74         thePnt1.Transform (myTrsf);
75         thePnt2.Transform (myTrsf);
76         thePnt3.Transform (myTrsf);
77       }
78
79       // calculate normal
80       theNormal = (thePnt2.XYZ() - thePnt1.XYZ()) ^ (thePnt3.XYZ() - thePnt1.XYZ());
81       Standard_Real aNorm = theNormal.Magnitude();
82       if (aNorm > gp::Resolution())
83         theNormal /= aNorm;
84     }
85
86   private:
87     Handle(Poly_Triangulation) myPoly;
88     gp_Trsf myTrsf;
89     int myNbTriangles;
90     bool myInvert;
91   };
92
93   // convert to float and, on big-endian platform, to little-endian representation
94   inline float convertFloat (Standard_Real aValue)
95   {
96 #ifdef OCCT_BINARY_FILE_DO_INVERSE
97     return OSD_BinaryFile::InverseShortReal ((float)aValue);
98 #else
99     return (float)aValue;
100 #endif
101   }
102 }
103
104 StlAPI_ErrorStatus StlAPI_Writer::Write(const TopoDS_Shape& theShape, const Standard_CString theFileName)
105 {
106   // open file
107   FILE* aFile = OSD_OpenFile (theFileName, "wb");
108   if (!aFile)
109     return StlAPI_CannotOpenFile;
110
111   // write
112   if (theASCIIMode)
113   {
114     // header 
115     Fprintf (aFile, "solid shape, STL ascii file, created with Open CASCADE Technology\n");
116
117     // facets
118     for (TopExp_Explorer exp (theShape, TopAbs_FACE); exp.More(); exp.Next())
119     {
120       TriangleAccessor aTool (TopoDS::Face (exp.Current()));
121       for (int iTri = 1; iTri <= aTool.NbTriangles(); iTri++)
122       {
123         gp_Vec aNorm;
124         gp_Pnt aPnt1, aPnt2, aPnt3;
125         aTool.GetTriangle (iTri, aNorm, aPnt1, aPnt2, aPnt3);
126
127         Fprintf (aFile,
128           " facet normal %12e %12e %12e\n"
129           "   outer loop\n"
130           "     vertex %12e %12e %12e\n"
131           "     vertex %12e %12e %12e\n"
132           "     vertex %12e %12e %12e\n"
133           "   endloop\n"
134           " endfacet\n",
135           aNorm.X(), aNorm.Y(), aNorm.Z(),
136           aPnt1.X(), aPnt1.Y(), aPnt1.Z(),
137           aPnt2.X(), aPnt2.Y(), aPnt2.Z(),
138           aPnt3.X(), aPnt3.Y(), aPnt3.Z());
139       }
140     }
141
142     // footer
143     Fprintf (aFile, "endsolid shape\n");
144   }
145   else
146   {
147     // header block (meaningless 80 bytes)
148     Fprintf (aFile, "%-80.80s", "STL binary file, created with Open CASCADE Technology");
149
150     // number of facets
151     int32_t aNbTri = 0;
152     for (TopExp_Explorer exp (theShape, TopAbs_FACE); exp.More(); exp.Next())
153     {
154       TopLoc_Location aLoc;
155       Handle(Poly_Triangulation) aPoly =
156         BRep_Tool::Triangulation (TopoDS::Face (exp.Current()), aLoc);
157       if (! aPoly.IsNull())
158         aNbTri += aPoly->NbTriangles();
159     }
160     // suppose that number of triangles must be little endian...
161 #ifdef OCCT_BINARY_FILE_DO_INVERSE
162     aNbTri = OSD_BinaryFile::InverseInteger (aNbTri);
163 #endif
164     fwrite (&aNbTri, sizeof(int32_t), 1, aFile);
165
166     // facets
167     struct Facet {
168       float nx, ny, nz;
169       float x1, y1, z1;
170       float x2, y2, z2;
171       float x3, y3, z3;
172       uint16_t dummy;
173     } f;
174     f.dummy = 0;
175     for (TopExp_Explorer exp (theShape, TopAbs_FACE); exp.More(); exp.Next())
176     {
177       TriangleAccessor aTool (TopoDS::Face (exp.Current()));
178       for (int iTri = 1; iTri <= aTool.NbTriangles(); iTri++)
179       {
180         gp_Vec aNorm;
181         gp_Pnt aPnt1, aPnt2, aPnt3;
182         aTool.GetTriangle (iTri, aNorm, aPnt1, aPnt2, aPnt3);
183
184         f.nx = convertFloat (aNorm.X());
185         f.ny = convertFloat (aNorm.Y());
186         f.nz = convertFloat (aNorm.Z());
187
188         f.x1 = convertFloat (aPnt1.X());
189         f.y1 = convertFloat (aPnt1.Y());
190         f.z1 = convertFloat (aPnt1.Z());
191
192         f.x2 = convertFloat (aPnt2.X());
193         f.y2 = convertFloat (aPnt2.Y());
194         f.z2 = convertFloat (aPnt2.Z());
195
196         f.x3 = convertFloat (aPnt3.X());
197         f.y3 = convertFloat (aPnt3.Y());
198         f.z3 = convertFloat (aPnt3.Z());
199
200         fwrite (&f, 50 /* 50 bytes per facet */, 1, aFile);
201       }
202     }
203   }
204
205   fclose (aFile);
206   return ferror(aFile) ? StlAPI_WriteError : StlAPI_StatusOK;
207 }
208