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