0d053347b1ecdf5c32e586de5152403c7dc59068
[occt.git] / src / RWStl / RWStl.cxx
1 // Created on: 2017-06-13
2 // Created by: Alexander MALYSHEV
3 // Copyright (c) 2017 OPEN CASCADE SAS
4 //
5 // This file is part of Open CASCADE Technology software library.
6 //
7 // This library is free software; you can redistribute it and/or modify it under
8 // the terms of the GNU Lesser General Public License version 2.1 as published
9 // by the Free Software Foundation, with special exception defined in the file
10 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
11 // distribution for complete text of the license and disclaimer of any warranty.
12 //
13 // Alternatively, this file may be used under the terms of Open CASCADE
14 // commercial license or contractual agreement.
15
16 #include <RWStl.hxx>
17
18 #include <Message_ProgressSentry.hxx>
19 #include <NCollection_Vector.hxx>
20 #include <OSD_File.hxx>
21 #include <OSD_OpenFile.hxx>
22 #include <RWStl_Reader.hxx>
23
24 namespace
25 {
26
27   static const Standard_Integer THE_STL_SIZEOF_FACET = 50;
28   static const Standard_Integer IND_THRESHOLD = 1000; // increment the indicator every 1k triangles
29
30   //! Writing a Little Endian 32 bits integer
31   inline static void convertInteger (const Standard_Integer theValue,
32                                      Standard_Character* theResult)
33   {
34     union
35     {
36       Standard_Integer i;
37       Standard_Character c[4];
38     } anUnion;
39     anUnion.i = theValue;
40
41     theResult[0] = anUnion.c[0];
42     theResult[1] = anUnion.c[1];
43     theResult[2] = anUnion.c[2];
44     theResult[3] = anUnion.c[3];
45   }
46
47   //! Writing a Little Endian 32 bits float
48   inline static void convertDouble (const Standard_Real theValue,
49                                     Standard_Character* theResult)
50   {
51     union
52     {
53       Standard_ShortReal i;
54       Standard_Character c[4];
55     } anUnion;
56     anUnion.i = (Standard_ShortReal)theValue;
57
58     theResult[0] = anUnion.c[0];
59     theResult[1] = anUnion.c[1];
60     theResult[2] = anUnion.c[2];
61     theResult[3] = anUnion.c[3];
62   }
63
64   class Reader : public RWStl_Reader
65   {
66   public:
67     //! Add new node
68     virtual Standard_Integer AddNode (const gp_XYZ& thePnt) Standard_OVERRIDE
69     {
70       myNodes.Append (thePnt);
71       return myNodes.Size();
72     }
73
74     //! Add new triangle
75     virtual void AddTriangle (Standard_Integer theNode1, Standard_Integer theNode2, Standard_Integer theNode3) Standard_OVERRIDE
76     {
77       myTriangles.Append (Poly_Triangle (theNode1, theNode2, theNode3));
78     }
79
80     //! Creates Poly_Triangulation from collected data
81     Handle(Poly_Triangulation) GetTriangulation()
82     {
83       if (myTriangles.IsEmpty())
84         return Handle(Poly_Triangulation)();
85
86       Handle(Poly_Triangulation) aPoly = new Poly_Triangulation (myNodes.Length(), myTriangles.Length(), Standard_False);
87       for (Standard_Integer aNodeIter = 0; aNodeIter < myNodes.Size(); ++aNodeIter)
88       {
89         aPoly->ChangeNode (aNodeIter + 1) = myNodes (aNodeIter);
90       }
91
92       for (Standard_Integer aTriIter = 0; aTriIter < myTriangles.Size(); ++aTriIter)
93       {
94         aPoly->ChangeTriangle (aTriIter + 1) = myTriangles (aTriIter);
95       }
96
97       return aPoly;
98     }
99
100   private:
101     NCollection_Vector<gp_XYZ> myNodes;
102     NCollection_Vector<Poly_Triangle> myTriangles;
103   };
104
105 }
106
107 //=============================================================================
108 //function : Read
109 //purpose  :
110 //=============================================================================
111 Handle(Poly_Triangulation) RWStl::ReadFile (const Standard_CString theFile,
112                                             const Handle(Message_ProgressIndicator)& theProgress)
113 {
114   Reader aReader;
115   aReader.Read (theFile, theProgress);
116   // note that returned bool value is ignored intentionally -- even if something went wrong,
117   // but some data have been read, we at least will return these data
118   return aReader.GetTriangulation();
119 }
120
121 //=============================================================================
122 //function : ReadFile
123 //purpose  :
124 //=============================================================================
125 Handle(Poly_Triangulation) RWStl::ReadFile (const OSD_Path& theFile,
126                                             const Handle(Message_ProgressIndicator)& theProgress)
127 {
128   OSD_File aFile(theFile);
129   if (!aFile.Exists())
130   {
131     return Handle(Poly_Triangulation)();
132   }
133
134   TCollection_AsciiString aPath;
135   theFile.SystemName (aPath);
136   return ReadFile (aPath.ToCString(), theProgress);
137 }
138
139 //=============================================================================
140 //function : ReadBinary
141 //purpose  :
142 //=============================================================================
143 Handle(Poly_Triangulation) RWStl::ReadBinary (const OSD_Path& theFile,
144                                               const Handle(Message_ProgressIndicator)& theProgress)
145 {
146   OSD_File aFile(theFile);
147   if (!aFile.Exists())
148   {
149     return Handle(Poly_Triangulation)();
150   }
151
152   TCollection_AsciiString aPath;
153   theFile.SystemName (aPath);
154
155   std::filebuf aBuf;
156   OSD_OpenStream (aBuf, aPath, std::ios::in | std::ios::binary);
157   if (!aBuf.is_open())
158   {
159     return Handle(Poly_Triangulation)();
160   }
161   Standard_IStream aStream (&aBuf);
162
163   Reader aReader;
164   if (!aReader.ReadBinary (aStream, theProgress))
165   {
166     return Handle(Poly_Triangulation)();
167   }
168
169   return aReader.GetTriangulation();
170 }
171
172 //=============================================================================
173 //function : ReadAscii
174 //purpose  :
175 //=============================================================================
176 Handle(Poly_Triangulation) RWStl::ReadAscii (const OSD_Path& theFile,
177                                              const Handle(Message_ProgressIndicator)& theProgress)
178 {
179   OSD_File aFile (theFile);
180   if (!aFile.Exists())
181   {
182     return Handle(Poly_Triangulation)();
183   }
184
185   TCollection_AsciiString aPath;
186   theFile.SystemName (aPath);
187
188   std::filebuf aBuf;
189   OSD_OpenStream (aBuf, aPath, std::ios::in | std::ios::binary);
190   if (!aBuf.is_open())
191   {
192     return Handle(Poly_Triangulation)();
193   }
194   Standard_IStream aStream (&aBuf);
195
196   // get length of file to feed progress indicator
197   aStream.seekg (0, aStream.end);
198   std::streampos theEnd = aStream.tellg();
199   aStream.seekg (0, aStream.beg);
200
201   Reader aReader;
202   if (!aReader.ReadAscii (aStream, theEnd, theProgress))
203   {
204     return Handle(Poly_Triangulation)();
205   }
206
207   return aReader.GetTriangulation();
208 }
209
210 //=============================================================================
211 //function : Write
212 //purpose  :
213 //=============================================================================
214 Standard_Boolean RWStl::WriteBinary (const Handle(Poly_Triangulation)& theMesh,
215                                      const OSD_Path& thePath,
216                                      const Handle(Message_ProgressIndicator)& theProgInd)
217 {
218   if (theMesh.IsNull() || theMesh->NbTriangles() <= 0)
219   {
220     return Standard_False;
221   }
222
223   TCollection_AsciiString aPath;
224   thePath.SystemName (aPath);
225
226   FILE* aFile = OSD_OpenFile (aPath, "wb");
227   if (aFile == NULL)
228   {
229     return Standard_False;
230   }
231
232   Standard_Boolean isOK = writeBinary (theMesh, aFile, theProgInd);
233
234   fclose (aFile);
235   return isOK;
236 }
237
238 //=============================================================================
239 //function : Write
240 //purpose  :
241 //=============================================================================
242 Standard_Boolean RWStl::WriteAscii (const Handle(Poly_Triangulation)& theMesh,
243                                     const OSD_Path& thePath,
244                                     const Handle(Message_ProgressIndicator)& theProgInd)
245 {
246   if (theMesh.IsNull() || theMesh->NbTriangles() <= 0)
247   {
248     return Standard_False;
249   }
250
251   TCollection_AsciiString aPath;
252   thePath.SystemName (aPath);
253
254   FILE* aFile = OSD_OpenFile (aPath, "w");
255   if (aFile == NULL)
256   {
257     return Standard_False;
258   }
259
260   Standard_Boolean isOK = writeASCII (theMesh, aFile, theProgInd);
261   fclose (aFile);
262   return isOK;
263 }
264
265 //=============================================================================
266 //function : writeASCII
267 //purpose  :
268 //=============================================================================
269 Standard_Boolean RWStl::writeASCII (const Handle(Poly_Triangulation)& theMesh,
270                                     FILE* theFile,
271                                     const Handle(Message_ProgressIndicator)& theProgInd)
272 {
273   // note that space after 'solid' is necessary for many systems
274   if (fwrite ("solid \n", 1, 7, theFile) != 7)
275   {
276     return Standard_False;
277   }
278
279   char aBuffer[512];
280   memset (aBuffer, 0, sizeof(aBuffer));
281
282   Message_ProgressSentry aPS (theProgInd, "Triangles", 0,
283                               theMesh->NbTriangles(), IND_THRESHOLD);
284
285   const TColgp_Array1OfPnt& aNodes = theMesh->Nodes();
286   const Poly_Array1OfTriangle& aTriangles = theMesh->Triangles();
287   const Standard_Integer NBTriangles = theMesh->NbTriangles();
288   Standard_Integer anElem[3] = {0, 0, 0};
289   for (Standard_Integer aTriIter = 1; aTriIter <= NBTriangles; ++aTriIter)
290   {
291     const Poly_Triangle& aTriangle = aTriangles (aTriIter);
292     aTriangle.Get (anElem[0], anElem[1], anElem[2]);
293
294     const gp_Pnt aP1 = aNodes (anElem[0]);
295     const gp_Pnt aP2 = aNodes (anElem[1]);
296     const gp_Pnt aP3 = aNodes (anElem[2]);
297
298     const gp_Vec aVec1 (aP1, aP2);
299     const gp_Vec aVec2 (aP1, aP3);
300     gp_Vec aVNorm = aVec1.Crossed (aVec2);
301     if (aVNorm.SquareMagnitude() > gp::Resolution())
302     {
303       aVNorm.Normalize();
304     }
305     else
306     {
307       aVNorm.SetCoord (0.0, 0.0, 0.0);
308     }
309
310     Sprintf (aBuffer,
311           " facet normal % 12e % 12e % 12e\n"
312           "   outer loop\n"
313           "     vertex % 12e % 12e % 12e\n"
314           "     vertex % 12e % 12e % 12e\n"
315           "     vertex % 12e % 12e % 12e\n"
316           "   endloop\n"
317           " endfacet\n",
318           aVNorm.X(), aVNorm.Y(), aVNorm.Z(),
319           aP1.X(), aP1.Y(), aP1.Z(),
320           aP2.X(), aP2.Y(), aP2.Z(),
321           aP3.X(), aP3.Y(), aP3.Z());
322
323     if (fprintf (theFile, "%s", aBuffer) < 0)
324     {
325       return Standard_False;
326     }
327
328     // update progress only per 1k triangles
329     if ((aTriIter % IND_THRESHOLD) == 0)
330     {
331       aPS.Next();
332     }
333   }
334
335   if (fwrite ("endsolid\n", 1, 9, theFile) != 9)
336   {
337     return Standard_False;
338   }
339
340   return Standard_True;
341 }
342
343 //=============================================================================
344 //function : writeBinary
345 //purpose  :
346 //=============================================================================
347 Standard_Boolean RWStl::writeBinary (const Handle(Poly_Triangulation)& theMesh,
348                                      FILE* theFile,
349                                      const Handle(Message_ProgressIndicator)& theProgInd)
350 {
351   char aHeader[80] = "STL Exported by OpenCASCADE [www.opencascade.com]";
352   if (fwrite (aHeader, 1, 80, theFile) != 80)
353   {
354     return Standard_False;
355   }
356
357   Message_ProgressSentry aPS (theProgInd, "Triangles", 0,
358                               theMesh->NbTriangles(), IND_THRESHOLD);
359
360   const Standard_Size aNbChunkTriangles = 4096;
361   const Standard_Size aChunkSize = aNbChunkTriangles * THE_STL_SIZEOF_FACET;
362   NCollection_Array1<Standard_Character> aData (1, aChunkSize);
363   Standard_Character* aDataChunk = &aData.ChangeFirst();
364
365   const TColgp_Array1OfPnt& aNodes = theMesh->Nodes();
366   const Poly_Array1OfTriangle& aTriangles = theMesh->Triangles();
367   const Standard_Integer aNBTriangles = theMesh->NbTriangles();
368
369   Standard_Character aConv[4];
370   convertInteger (aNBTriangles, aConv);
371   if (fwrite (aConv, 1, 4, theFile) != 4)
372   {
373     return Standard_False;
374   }
375
376   Standard_Size aByteCount = 0;
377   for (Standard_Integer aTriIter = 1; aTriIter <= aNBTriangles; ++aTriIter)
378   {
379     Standard_Integer id[3];
380     const Poly_Triangle& aTriangle = aTriangles (aTriIter);
381     aTriangle.Get (id[0], id[1], id[2]);
382
383     const gp_Pnt aP1 = aNodes (id[0]);
384     const gp_Pnt aP2 = aNodes (id[1]);
385     const gp_Pnt aP3 = aNodes (id[2]);
386
387     gp_Vec aVec1 (aP1, aP2);
388     gp_Vec aVec2 (aP1, aP3);
389     gp_Vec aVNorm = aVec1.Crossed(aVec2);
390     if (aVNorm.SquareMagnitude() > gp::Resolution())
391     {
392       aVNorm.Normalize();
393     }
394     else
395     {
396       aVNorm.SetCoord (0.0, 0.0, 0.0);
397     }
398
399     convertDouble (aVNorm.X(), &aDataChunk[aByteCount]); aByteCount += 4;
400     convertDouble (aVNorm.Y(), &aDataChunk[aByteCount]); aByteCount += 4;
401     convertDouble (aVNorm.Z(), &aDataChunk[aByteCount]); aByteCount += 4;
402
403     convertDouble (aP1.X(), &aDataChunk[aByteCount]); aByteCount += 4;
404     convertDouble (aP1.Y(), &aDataChunk[aByteCount]); aByteCount += 4;
405     convertDouble (aP1.Z(), &aDataChunk[aByteCount]); aByteCount += 4;
406
407     convertDouble (aP2.X(), &aDataChunk[aByteCount]); aByteCount += 4;
408     convertDouble (aP2.Y(), &aDataChunk[aByteCount]); aByteCount += 4;
409     convertDouble (aP2.Z(), &aDataChunk[aByteCount]); aByteCount += 4;
410
411     convertDouble (aP3.X(), &aDataChunk[aByteCount]); aByteCount += 4;
412     convertDouble (aP3.Y(), &aDataChunk[aByteCount]); aByteCount += 4;
413     convertDouble (aP3.Z(), &aDataChunk[aByteCount]); aByteCount += 4;
414
415     aDataChunk[aByteCount] = 0; aByteCount += 1;
416     aDataChunk[aByteCount] = 0; aByteCount += 1;
417
418     // Chunk is filled. Dump it to the file.
419     if (aByteCount == aChunkSize)
420     {
421       if (fwrite (aDataChunk, 1, aChunkSize, theFile) != aChunkSize)
422       {
423         return Standard_False;
424       }
425
426       aByteCount = 0;
427     }
428
429     // update progress only per 1k triangles
430     if ((aTriIter % IND_THRESHOLD) == 0)
431     {
432       aPS.Next();
433     }
434   }
435
436   // Write last part if necessary.
437   if (aByteCount != aChunkSize)
438   {
439     if (fwrite (aDataChunk, 1, aByteCount, theFile) != aByteCount)
440     {
441       return Standard_False;
442     }
443   }
444
445   return Standard_True;
446 }