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
30 //! Writing a Little Endian 32 bits integer
31 inline static void convertInteger (const Standard_Integer theValue,
32 Standard_Character* theResult)
37 Standard_Character c[4];
41 theResult[0] = anUnion.c[0];
42 theResult[1] = anUnion.c[1];
43 theResult[2] = anUnion.c[2];
44 theResult[3] = anUnion.c[3];
47 //! Writing a Little Endian 32 bits float
48 inline static void convertDouble (const Standard_Real theValue,
49 Standard_Character* theResult)
54 Standard_Character c[4];
56 anUnion.i = (Standard_ShortReal)theValue;
58 theResult[0] = anUnion.c[0];
59 theResult[1] = anUnion.c[1];
60 theResult[2] = anUnion.c[2];
61 theResult[3] = anUnion.c[3];
64 class Reader : public RWStl_Reader
68 virtual Standard_Integer AddNode (const gp_XYZ& thePnt) Standard_OVERRIDE
70 myNodes.Append (thePnt);
71 return myNodes.Size();
75 virtual void AddTriangle (Standard_Integer theNode1, Standard_Integer theNode2, Standard_Integer theNode3) Standard_OVERRIDE
77 myTriangles.Append (Poly_Triangle (theNode1, theNode2, theNode3));
80 //! Creates Poly_Triangulation from collected data
81 Handle(Poly_Triangulation) GetTriangulation()
83 if (myTriangles.IsEmpty())
84 return Handle(Poly_Triangulation)();
86 Handle(Poly_Triangulation) aPoly = new Poly_Triangulation (myNodes.Length(), myTriangles.Length(), Standard_False);
87 for (Standard_Integer aNodeIter = 0; aNodeIter < myNodes.Size(); ++aNodeIter)
89 aPoly->ChangeNode (aNodeIter + 1) = myNodes (aNodeIter);
92 for (Standard_Integer aTriIter = 0; aTriIter < myTriangles.Size(); ++aTriIter)
94 aPoly->ChangeTriangle (aTriIter + 1) = myTriangles (aTriIter);
101 NCollection_Vector<gp_XYZ> myNodes;
102 NCollection_Vector<Poly_Triangle> myTriangles;
107 //=============================================================================
110 //=============================================================================
111 Handle(Poly_Triangulation) RWStl::ReadFile (const Standard_CString theFile,
112 const Handle(Message_ProgressIndicator)& theProgress)
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();
121 //=============================================================================
122 //function : ReadFile
124 //=============================================================================
125 Handle(Poly_Triangulation) RWStl::ReadFile (const OSD_Path& theFile,
126 const Handle(Message_ProgressIndicator)& theProgress)
128 OSD_File aFile(theFile);
131 return Handle(Poly_Triangulation)();
134 TCollection_AsciiString aPath;
135 theFile.SystemName (aPath);
136 return ReadFile (aPath.ToCString(), theProgress);
139 //=============================================================================
140 //function : ReadBinary
142 //=============================================================================
143 Handle(Poly_Triangulation) RWStl::ReadBinary (const OSD_Path& theFile,
144 const Handle(Message_ProgressIndicator)& theProgress)
146 OSD_File aFile(theFile);
149 return Handle(Poly_Triangulation)();
152 TCollection_AsciiString aPath;
153 theFile.SystemName (aPath);
156 OSD_OpenStream (aBuf, aPath, std::ios::in | std::ios::binary);
159 return Handle(Poly_Triangulation)();
161 Standard_IStream aStream (&aBuf);
164 if (!aReader.ReadBinary (aStream, theProgress))
166 return Handle(Poly_Triangulation)();
169 return aReader.GetTriangulation();
172 //=============================================================================
173 //function : ReadAscii
175 //=============================================================================
176 Handle(Poly_Triangulation) RWStl::ReadAscii (const OSD_Path& theFile,
177 const Handle(Message_ProgressIndicator)& theProgress)
179 OSD_File aFile (theFile);
182 return Handle(Poly_Triangulation)();
185 TCollection_AsciiString aPath;
186 theFile.SystemName (aPath);
189 OSD_OpenStream (aBuf, aPath, std::ios::in | std::ios::binary);
192 return Handle(Poly_Triangulation)();
194 Standard_IStream aStream (&aBuf);
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);
202 if (!aReader.ReadAscii (aStream, theEnd, theProgress))
204 return Handle(Poly_Triangulation)();
207 return aReader.GetTriangulation();
210 //=============================================================================
213 //=============================================================================
214 Standard_Boolean RWStl::WriteBinary (const Handle(Poly_Triangulation)& theMesh,
215 const OSD_Path& thePath,
216 const Handle(Message_ProgressIndicator)& theProgInd)
218 if (theMesh.IsNull() || theMesh->NbTriangles() <= 0)
220 return Standard_False;
223 TCollection_AsciiString aPath;
224 thePath.SystemName (aPath);
226 FILE* aFile = OSD_OpenFile (aPath, "wb");
229 return Standard_False;
232 Standard_Boolean isOK = writeBinary (theMesh, aFile, theProgInd);
238 //=============================================================================
241 //=============================================================================
242 Standard_Boolean RWStl::WriteAscii (const Handle(Poly_Triangulation)& theMesh,
243 const OSD_Path& thePath,
244 const Handle(Message_ProgressIndicator)& theProgInd)
246 if (theMesh.IsNull() || theMesh->NbTriangles() <= 0)
248 return Standard_False;
251 TCollection_AsciiString aPath;
252 thePath.SystemName (aPath);
254 FILE* aFile = OSD_OpenFile (aPath, "w");
257 return Standard_False;
260 Standard_Boolean isOK = writeASCII (theMesh, aFile, theProgInd);
265 //=============================================================================
266 //function : writeASCII
268 //=============================================================================
269 Standard_Boolean RWStl::writeASCII (const Handle(Poly_Triangulation)& theMesh,
271 const Handle(Message_ProgressIndicator)& theProgInd)
273 // note that space after 'solid' is necessary for many systems
274 if (fwrite ("solid \n", 1, 7, theFile) != 7)
276 return Standard_False;
280 memset (aBuffer, 0, sizeof(aBuffer));
282 Message_ProgressSentry aPS (theProgInd, "Triangles", 0,
283 theMesh->NbTriangles(), IND_THRESHOLD);
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)
291 const Poly_Triangle& aTriangle = aTriangles (aTriIter);
292 aTriangle.Get (anElem[0], anElem[1], anElem[2]);
294 const gp_Pnt aP1 = aNodes (anElem[0]);
295 const gp_Pnt aP2 = aNodes (anElem[1]);
296 const gp_Pnt aP3 = aNodes (anElem[2]);
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())
307 aVNorm.SetCoord (0.0, 0.0, 0.0);
311 " facet normal % 12e % 12e % 12e\n"
313 " vertex % 12e % 12e % 12e\n"
314 " vertex % 12e % 12e % 12e\n"
315 " vertex % 12e % 12e % 12e\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());
323 if (fprintf (theFile, "%s", aBuffer) < 0)
325 return Standard_False;
328 // update progress only per 1k triangles
329 if ((aTriIter % IND_THRESHOLD) == 0)
335 if (fwrite ("endsolid\n", 1, 9, theFile) != 9)
337 return Standard_False;
340 return Standard_True;
343 //=============================================================================
344 //function : writeBinary
346 //=============================================================================
347 Standard_Boolean RWStl::writeBinary (const Handle(Poly_Triangulation)& theMesh,
349 const Handle(Message_ProgressIndicator)& theProgInd)
351 char aHeader[80] = "STL Exported by OpenCASCADE [www.opencascade.com]";
352 if (fwrite (aHeader, 1, 80, theFile) != 80)
354 return Standard_False;
357 Message_ProgressSentry aPS (theProgInd, "Triangles", 0,
358 theMesh->NbTriangles(), IND_THRESHOLD);
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();
365 const TColgp_Array1OfPnt& aNodes = theMesh->Nodes();
366 const Poly_Array1OfTriangle& aTriangles = theMesh->Triangles();
367 const Standard_Integer aNBTriangles = theMesh->NbTriangles();
369 Standard_Character aConv[4];
370 convertInteger (aNBTriangles, aConv);
371 if (fwrite (aConv, 1, 4, theFile) != 4)
373 return Standard_False;
376 Standard_Size aByteCount = 0;
377 for (Standard_Integer aTriIter = 1; aTriIter <= aNBTriangles; ++aTriIter)
379 Standard_Integer id[3];
380 const Poly_Triangle& aTriangle = aTriangles (aTriIter);
381 aTriangle.Get (id[0], id[1], id[2]);
383 const gp_Pnt aP1 = aNodes (id[0]);
384 const gp_Pnt aP2 = aNodes (id[1]);
385 const gp_Pnt aP3 = aNodes (id[2]);
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())
396 aVNorm.SetCoord (0.0, 0.0, 0.0);
399 convertDouble (aVNorm.X(), &aDataChunk[aByteCount]); aByteCount += 4;
400 convertDouble (aVNorm.Y(), &aDataChunk[aByteCount]); aByteCount += 4;
401 convertDouble (aVNorm.Z(), &aDataChunk[aByteCount]); aByteCount += 4;
403 convertDouble (aP1.X(), &aDataChunk[aByteCount]); aByteCount += 4;
404 convertDouble (aP1.Y(), &aDataChunk[aByteCount]); aByteCount += 4;
405 convertDouble (aP1.Z(), &aDataChunk[aByteCount]); aByteCount += 4;
407 convertDouble (aP2.X(), &aDataChunk[aByteCount]); aByteCount += 4;
408 convertDouble (aP2.Y(), &aDataChunk[aByteCount]); aByteCount += 4;
409 convertDouble (aP2.Z(), &aDataChunk[aByteCount]); aByteCount += 4;
411 convertDouble (aP3.X(), &aDataChunk[aByteCount]); aByteCount += 4;
412 convertDouble (aP3.Y(), &aDataChunk[aByteCount]); aByteCount += 4;
413 convertDouble (aP3.Z(), &aDataChunk[aByteCount]); aByteCount += 4;
415 aDataChunk[aByteCount] = 0; aByteCount += 1;
416 aDataChunk[aByteCount] = 0; aByteCount += 1;
418 // Chunk is filled. Dump it to the file.
419 if (aByteCount == aChunkSize)
421 if (fwrite (aDataChunk, 1, aChunkSize, theFile) != aChunkSize)
423 return Standard_False;
429 // update progress only per 1k triangles
430 if ((aTriIter % IND_THRESHOLD) == 0)
436 // Write last part if necessary.
437 if (aByteCount != aChunkSize)
439 if (fwrite (aDataChunk, 1, aByteCount, theFile) != aByteCount)
441 return Standard_False;
445 return Standard_True;