Commit | Line | Data |
---|---|---|
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 | ||
24 | namespace | |
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 | //============================================================================= | |
108 | Handle(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 | //============================================================================= | |
123 | Handle(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 | //============================================================================= | |
141 | Handle(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 | //============================================================================= | |
174 | Handle(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 | //============================================================================= | |
212 | Standard_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 | //============================================================================= | |
235 | Standard_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 | //============================================================================= | |
257 | Standard_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 | //============================================================================= | |
335 | Standard_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 | } |