1 // Created on: 2017-06-13
2 // Created by: Alexander MALYSHEV
3 // Copyright (c) 2017 OPEN CASCADE SAS
5 // This file is part of Open CASCADE Technology software library.
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.
13 // Alternatively, this file may be used under the terms of Open CASCADE
14 // commercial license or contractual agreement.
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>
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)
31 //! Writing a Little Endian 32 bits integer
32 inline static void convertInteger (const Standard_Integer theValue,
33 Standard_Character* theResult)
38 Standard_Character c[4];
42 theResult[0] = anUnion.c[0];
43 theResult[1] = anUnion.c[1];
44 theResult[2] = anUnion.c[2];
45 theResult[3] = anUnion.c[3];
48 //! Writing a Little Endian 32 bits float
49 inline static void convertDouble (const Standard_Real theValue,
50 Standard_Character* theResult)
55 Standard_Character c[4];
57 anUnion.i = (Standard_ShortReal)theValue;
59 theResult[0] = anUnion.c[0];
60 theResult[1] = anUnion.c[1];
61 theResult[2] = anUnion.c[2];
62 theResult[3] = anUnion.c[3];
65 class Reader : public RWStl_Reader
69 virtual Standard_Integer AddNode (const gp_XYZ& thePnt) Standard_OVERRIDE
71 myNodes.Append (thePnt);
72 return myNodes.Size();
76 virtual void AddTriangle (Standard_Integer theNode1, Standard_Integer theNode2, Standard_Integer theNode3) Standard_OVERRIDE
78 myTriangles.Append (Poly_Triangle (theNode1, theNode2, theNode3));
81 //! Creates Poly_Triangulation from collected data
82 Handle(Poly_Triangulation) GetTriangulation()
84 if (myTriangles.IsEmpty())
85 return Handle(Poly_Triangulation)();
87 Handle(Poly_Triangulation) aPoly = new Poly_Triangulation (myNodes.Length(), myTriangles.Length(), Standard_False);
88 for (Standard_Integer aNodeIter = 0; aNodeIter < myNodes.Size(); ++aNodeIter)
90 aPoly->ChangeNode (aNodeIter + 1) = myNodes (aNodeIter);
93 for (Standard_Integer aTriIter = 0; aTriIter < myTriangles.Size(); ++aTriIter)
95 aPoly->ChangeTriangle (aTriIter + 1) = myTriangles (aTriIter);
102 NCollection_Vector<gp_XYZ> myNodes;
103 NCollection_Vector<Poly_Triangle> myTriangles;
108 //=============================================================================
111 //=============================================================================
112 Handle(Poly_Triangulation) RWStl::ReadFile (const Standard_CString theFile,
113 const Handle(Message_ProgressIndicator)& theProgress)
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();
122 //=============================================================================
123 //function : ReadFile
125 //=============================================================================
126 Handle(Poly_Triangulation) RWStl::ReadFile (const OSD_Path& theFile,
127 const Handle(Message_ProgressIndicator)& theProgress)
129 OSD_File aFile(theFile);
132 return Handle(Poly_Triangulation)();
135 TCollection_AsciiString aPath;
136 theFile.SystemName (aPath);
137 return ReadFile (aPath.ToCString(), theProgress);
140 //=============================================================================
141 //function : ReadBinary
143 //=============================================================================
144 Handle(Poly_Triangulation) RWStl::ReadBinary (const OSD_Path& theFile,
145 const Handle(Message_ProgressIndicator)& theProgress)
147 OSD_File aFile(theFile);
150 return Handle(Poly_Triangulation)();
153 TCollection_AsciiString aPath;
154 theFile.SystemName (aPath);
157 OSD_OpenStream (aBuf, aPath, std::ios::in | std::ios::binary);
160 return Handle(Poly_Triangulation)();
162 Standard_IStream aStream (&aBuf);
165 if (!aReader.ReadBinary (aStream, theProgress))
167 return Handle(Poly_Triangulation)();
170 return aReader.GetTriangulation();
173 //=============================================================================
174 //function : ReadAscii
176 //=============================================================================
177 Handle(Poly_Triangulation) RWStl::ReadAscii (const OSD_Path& theFile,
178 const Handle(Message_ProgressIndicator)& theProgress)
180 OSD_File aFile (theFile);
183 return Handle(Poly_Triangulation)();
186 TCollection_AsciiString aPath;
187 theFile.SystemName (aPath);
190 OSD_OpenStream (aBuf, aPath, std::ios::in | std::ios::binary);
193 return Handle(Poly_Triangulation)();
195 Standard_IStream aStream (&aBuf);
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);
203 Standard_ReadLineBuffer aBuffer (THE_BUFFER_SIZE);
204 if (!aReader.ReadAscii (aStream, aBuffer, theEnd, theProgress))
206 return Handle(Poly_Triangulation)();
209 return aReader.GetTriangulation();
212 //=============================================================================
215 //=============================================================================
216 Standard_Boolean RWStl::WriteBinary (const Handle(Poly_Triangulation)& theMesh,
217 const OSD_Path& thePath,
218 const Handle(Message_ProgressIndicator)& theProgInd)
220 if (theMesh.IsNull() || theMesh->NbTriangles() <= 0)
222 return Standard_False;
225 TCollection_AsciiString aPath;
226 thePath.SystemName (aPath);
228 FILE* aFile = OSD_OpenFile (aPath, "wb");
231 return Standard_False;
234 Standard_Boolean isOK = writeBinary (theMesh, aFile, theProgInd);
240 //=============================================================================
243 //=============================================================================
244 Standard_Boolean RWStl::WriteAscii (const Handle(Poly_Triangulation)& theMesh,
245 const OSD_Path& thePath,
246 const Handle(Message_ProgressIndicator)& theProgInd)
248 if (theMesh.IsNull() || theMesh->NbTriangles() <= 0)
250 return Standard_False;
253 TCollection_AsciiString aPath;
254 thePath.SystemName (aPath);
256 FILE* aFile = OSD_OpenFile (aPath, "w");
259 return Standard_False;
262 Standard_Boolean isOK = writeASCII (theMesh, aFile, theProgInd);
267 //=============================================================================
268 //function : writeASCII
270 //=============================================================================
271 Standard_Boolean RWStl::writeASCII (const Handle(Poly_Triangulation)& theMesh,
273 const Handle(Message_ProgressIndicator)& theProgInd)
275 // note that space after 'solid' is necessary for many systems
276 if (fwrite ("solid \n", 1, 7, theFile) != 7)
278 return Standard_False;
282 memset (aBuffer, 0, sizeof(aBuffer));
284 const Standard_Integer NBTriangles = theMesh->NbTriangles();
285 Message_ProgressSentry aPS (theProgInd, "Triangles", 0,
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)
293 const Poly_Triangle& aTriangle = aTriangles (aTriIter);
294 aTriangle.Get (anElem[0], anElem[1], anElem[2]);
296 const gp_Pnt aP1 = aNodes (anElem[0]);
297 const gp_Pnt aP2 = aNodes (anElem[1]);
298 const gp_Pnt aP3 = aNodes (anElem[2]);
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())
309 aVNorm.SetCoord (0.0, 0.0, 0.0);
313 " facet normal % 12e % 12e % 12e\n"
315 " vertex % 12e % 12e % 12e\n"
316 " vertex % 12e % 12e % 12e\n"
317 " vertex % 12e % 12e % 12e\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());
325 if (fprintf (theFile, "%s", aBuffer) < 0)
327 return Standard_False;
330 // update progress only per 1k triangles
331 if ((aTriIter % IND_THRESHOLD) == 0)
334 return Standard_False;
335 aPS.Next(IND_THRESHOLD);
339 if (fwrite ("endsolid\n", 1, 9, theFile) != 9)
341 return Standard_False;
344 return Standard_True;
347 //=============================================================================
348 //function : writeBinary
350 //=============================================================================
351 Standard_Boolean RWStl::writeBinary (const Handle(Poly_Triangulation)& theMesh,
353 const Handle(Message_ProgressIndicator)& theProgInd)
355 char aHeader[80] = "STL Exported by OpenCASCADE [www.opencascade.com]";
356 if (fwrite (aHeader, 1, 80, theFile) != 80)
358 return Standard_False;
361 const Standard_Integer aNBTriangles = theMesh->NbTriangles();
362 Message_ProgressSentry aPS (theProgInd, "Triangles", 0,
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();
370 const TColgp_Array1OfPnt& aNodes = theMesh->Nodes();
371 const Poly_Array1OfTriangle& aTriangles = theMesh->Triangles();
373 Standard_Character aConv[4];
374 convertInteger (aNBTriangles, aConv);
375 if (fwrite (aConv, 1, 4, theFile) != 4)
377 return Standard_False;
380 Standard_Size aByteCount = 0;
381 for (Standard_Integer aTriIter = 1; aTriIter <= aNBTriangles; ++aTriIter)
383 Standard_Integer id[3];
384 const Poly_Triangle& aTriangle = aTriangles (aTriIter);
385 aTriangle.Get (id[0], id[1], id[2]);
387 const gp_Pnt aP1 = aNodes (id[0]);
388 const gp_Pnt aP2 = aNodes (id[1]);
389 const gp_Pnt aP3 = aNodes (id[2]);
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())
400 aVNorm.SetCoord (0.0, 0.0, 0.0);
403 convertDouble (aVNorm.X(), &aDataChunk[aByteCount]); aByteCount += 4;
404 convertDouble (aVNorm.Y(), &aDataChunk[aByteCount]); aByteCount += 4;
405 convertDouble (aVNorm.Z(), &aDataChunk[aByteCount]); aByteCount += 4;
407 convertDouble (aP1.X(), &aDataChunk[aByteCount]); aByteCount += 4;
408 convertDouble (aP1.Y(), &aDataChunk[aByteCount]); aByteCount += 4;
409 convertDouble (aP1.Z(), &aDataChunk[aByteCount]); aByteCount += 4;
411 convertDouble (aP2.X(), &aDataChunk[aByteCount]); aByteCount += 4;
412 convertDouble (aP2.Y(), &aDataChunk[aByteCount]); aByteCount += 4;
413 convertDouble (aP2.Z(), &aDataChunk[aByteCount]); aByteCount += 4;
415 convertDouble (aP3.X(), &aDataChunk[aByteCount]); aByteCount += 4;
416 convertDouble (aP3.Y(), &aDataChunk[aByteCount]); aByteCount += 4;
417 convertDouble (aP3.Z(), &aDataChunk[aByteCount]); aByteCount += 4;
419 aDataChunk[aByteCount] = 0; aByteCount += 1;
420 aDataChunk[aByteCount] = 0; aByteCount += 1;
422 // Chunk is filled. Dump it to the file.
423 if (aByteCount == aChunkSize)
425 if (fwrite (aDataChunk, 1, aChunkSize, theFile) != aChunkSize)
427 return Standard_False;
433 // update progress only per 1k triangles
434 if ((aTriIter % IND_THRESHOLD) == 0)
437 return Standard_False;
438 aPS.Next(IND_THRESHOLD);
442 // Write last part if necessary.
443 if (aByteCount != aChunkSize)
445 if (fwrite (aDataChunk, 1, aByteCount, theFile) != aByteCount)
447 return Standard_False;
451 return Standard_True;