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