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 | |
c37bd936 | 29 | static const size_t THE_BUFFER_SIZE = 1024; // The length of buffer to read (in bytes) |
4178b353 | 30 | |
31 | //! Writing a Little Endian 32 bits integer | |
32 | inline static void convertInteger (const Standard_Integer theValue, | |
33 | Standard_Character* theResult) | |
fcc61cc4 | 34 | { |
4178b353 | 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]; | |
fcc61cc4 | 46 | } |
4178b353 | 47 | |
48 | //! Writing a Little Endian 32 bits float | |
49 | inline static void convertDouble (const Standard_Real theValue, | |
50 | Standard_Character* theResult) | |
fcc61cc4 | 51 | { |
4178b353 | 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]; | |
fcc61cc4 | 63 | } |
fcc61cc4 | 64 | |
4178b353 | 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 | } | |
8cb69787 | 74 | |
4178b353 | 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 | } | |
7fd59977 | 80 | |
4178b353 | 81 | //! Creates Poly_Triangulation from collected data |
82 | Handle(Poly_Triangulation) GetTriangulation() | |
83 | { | |
22e70738 | 84 | if (myTriangles.IsEmpty()) |
85 | return Handle(Poly_Triangulation)(); | |
86 | ||
4178b353 | 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 | } | |
7fd59977 | 92 | |
4178b353 | 93 | for (Standard_Integer aTriIter = 0; aTriIter < myTriangles.Size(); ++aTriIter) |
94 | { | |
95 | aPoly->ChangeTriangle (aTriIter + 1) = myTriangles (aTriIter); | |
96 | } | |
7fd59977 | 97 | |
4178b353 | 98 | return aPoly; |
99 | } | |
7fd59977 | 100 | |
4178b353 | 101 | private: |
102 | NCollection_Vector<gp_XYZ> myNodes; | |
103 | NCollection_Vector<Poly_Triangle> myTriangles; | |
104 | }; | |
ed482379 | 105 | |
4178b353 | 106 | } |
7fd59977 | 107 | |
4178b353 | 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; | |
22e70738 | 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 | |
4178b353 | 119 | return aReader.GetTriangulation(); |
7fd59977 | 120 | } |
121 | ||
4178b353 | 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 | } | |
7fd59977 | 139 | |
4178b353 | 140 | //============================================================================= |
141 | //function : ReadBinary | |
142 | //purpose : | |
143 | //============================================================================= | |
144 | Handle(Poly_Triangulation) RWStl::ReadBinary (const OSD_Path& theFile, | |
145 | const Handle(Message_ProgressIndicator)& theProgress) | |
7fd59977 | 146 | { |
4178b353 | 147 | OSD_File aFile(theFile); |
148 | if (!aFile.Exists()) | |
149 | { | |
150 | return Handle(Poly_Triangulation)(); | |
151 | } | |
7fd59977 | 152 | |
4178b353 | 153 | TCollection_AsciiString aPath; |
154 | theFile.SystemName (aPath); | |
7fd59977 | 155 | |
4178b353 | 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); | |
ed482379 | 163 | |
4178b353 | 164 | Reader aReader; |
165 | if (!aReader.ReadBinary (aStream, theProgress)) | |
166 | { | |
167 | return Handle(Poly_Triangulation)(); | |
168 | } | |
7fd59977 | 169 | |
4178b353 | 170 | return aReader.GetTriangulation(); |
7fd59977 | 171 | } |
172 | ||
4178b353 | 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 | } | |
7fd59977 | 185 | |
4178b353 | 186 | TCollection_AsciiString aPath; |
187 | theFile.SystemName (aPath); | |
7fd59977 | 188 | |
4178b353 | 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); | |
7fd59977 | 196 | |
4178b353 | 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); | |
7fd59977 | 201 | |
4178b353 | 202 | Reader aReader; |
c37bd936 | 203 | Standard_ReadLineBuffer aBuffer (THE_BUFFER_SIZE); |
204 | if (!aReader.ReadAscii (aStream, aBuffer, theEnd, theProgress)) | |
4178b353 | 205 | { |
206 | return Handle(Poly_Triangulation)(); | |
207 | } | |
7fd59977 | 208 | |
4178b353 | 209 | return aReader.GetTriangulation(); |
210 | } | |
7fd59977 | 211 | |
4178b353 | 212 | //============================================================================= |
213 | //function : Write | |
214 | //purpose : | |
215 | //============================================================================= | |
22e70738 | 216 | Standard_Boolean RWStl::WriteBinary (const Handle(Poly_Triangulation)& theMesh, |
9c6afe19 RK |
217 | const OSD_Path& thePath, |
218 | const Handle(Message_ProgressIndicator)& theProgInd) | |
7fd59977 | 219 | { |
22e70738 | 220 | if (theMesh.IsNull() || theMesh->NbTriangles() <= 0) |
221 | { | |
222 | return Standard_False; | |
223 | } | |
224 | ||
4178b353 | 225 | TCollection_AsciiString aPath; |
226 | thePath.SystemName (aPath); | |
9c6afe19 | 227 | |
4178b353 | 228 | FILE* aFile = OSD_OpenFile (aPath, "wb"); |
229 | if (aFile == NULL) | |
9c6afe19 | 230 | { |
4178b353 | 231 | return Standard_False; |
232 | } | |
9c6afe19 | 233 | |
22e70738 | 234 | Standard_Boolean isOK = writeBinary (theMesh, aFile, theProgInd); |
9c6afe19 | 235 | |
4178b353 | 236 | fclose (aFile); |
237 | return isOK; | |
238 | } | |
ed482379 | 239 | |
4178b353 | 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 | { | |
22e70738 | 248 | if (theMesh.IsNull() || theMesh->NbTriangles() <= 0) |
249 | { | |
250 | return Standard_False; | |
251 | } | |
252 | ||
4178b353 | 253 | TCollection_AsciiString aPath; |
254 | thePath.SystemName (aPath); | |
9c6afe19 | 255 | |
4178b353 | 256 | FILE* aFile = OSD_OpenFile (aPath, "w"); |
257 | if (aFile == NULL) | |
258 | { | |
259 | return Standard_False; | |
7fd59977 | 260 | } |
4178b353 | 261 | |
262 | Standard_Boolean isOK = writeASCII (theMesh, aFile, theProgInd); | |
263 | fclose (aFile); | |
264 | return isOK; | |
7fd59977 | 265 | } |
7fd59977 | 266 | |
4178b353 | 267 | //============================================================================= |
268 | //function : writeASCII | |
269 | //purpose : | |
270 | //============================================================================= | |
271 | Standard_Boolean RWStl::writeASCII (const Handle(Poly_Triangulation)& theMesh, | |
272 | FILE* theFile, | |
9c6afe19 | 273 | const Handle(Message_ProgressIndicator)& theProgInd) |
7fd59977 | 274 | { |
4178b353 | 275 | // note that space after 'solid' is necessary for many systems |
276 | if (fwrite ("solid \n", 1, 7, theFile) != 7) | |
9c6afe19 | 277 | { |
4178b353 | 278 | return Standard_False; |
279 | } | |
ed482379 | 280 | |
4178b353 | 281 | char aBuffer[512]; |
282 | memset (aBuffer, 0, sizeof(aBuffer)); | |
ed482379 | 283 | |
1fc1a207 | 284 | const Standard_Integer NBTriangles = theMesh->NbTriangles(); |
4178b353 | 285 | Message_ProgressSentry aPS (theProgInd, "Triangles", 0, |
1fc1a207 | 286 | NBTriangles, 1); |
4178b353 | 287 | |
288 | const TColgp_Array1OfPnt& aNodes = theMesh->Nodes(); | |
289 | const Poly_Array1OfTriangle& aTriangles = theMesh->Triangles(); | |
4178b353 | 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, | |
9c6afe19 RK |
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", | |
4178b353 | 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 | { | |
1fc1a207 | 333 | if (!aPS.More()) |
334 | return Standard_False; | |
335 | aPS.Next(IND_THRESHOLD); | |
7fd59977 | 336 | } |
337 | } | |
ed482379 | 338 | |
4178b353 | 339 | if (fwrite ("endsolid\n", 1, 9, theFile) != 9) |
340 | { | |
341 | return Standard_False; | |
342 | } | |
343 | ||
344 | return Standard_True; | |
7fd59977 | 345 | } |
7fd59977 | 346 | |
4178b353 | 347 | //============================================================================= |
348 | //function : writeBinary | |
349 | //purpose : | |
350 | //============================================================================= | |
351 | Standard_Boolean RWStl::writeBinary (const Handle(Poly_Triangulation)& theMesh, | |
352 | FILE* theFile, | |
9c6afe19 | 353 | const Handle(Message_ProgressIndicator)& theProgInd) |
7fd59977 | 354 | { |
4178b353 | 355 | char aHeader[80] = "STL Exported by OpenCASCADE [www.opencascade.com]"; |
356 | if (fwrite (aHeader, 1, 80, theFile) != 80) | |
357 | { | |
358 | return Standard_False; | |
7fd59977 | 359 | } |
1fc1a207 | 360 | |
361 | const Standard_Integer aNBTriangles = theMesh->NbTriangles(); | |
4178b353 | 362 | Message_ProgressSentry aPS (theProgInd, "Triangles", 0, |
1fc1a207 | 363 | aNBTriangles, 1); |
7fd59977 | 364 | |
4178b353 | 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(); | |
7fd59977 | 369 | |
4178b353 | 370 | const TColgp_Array1OfPnt& aNodes = theMesh->Nodes(); |
371 | const Poly_Array1OfTriangle& aTriangles = theMesh->Triangles(); | |
7fd59977 | 372 | |
4178b353 | 373 | Standard_Character aConv[4]; |
374 | convertInteger (aNBTriangles, aConv); | |
375 | if (fwrite (aConv, 1, 4, theFile) != 4) | |
a47d34ef | 376 | { |
4178b353 | 377 | return Standard_False; |
a47d34ef | 378 | } |
4178b353 | 379 | |
380 | Standard_Size aByteCount = 0; | |
381 | for (Standard_Integer aTriIter = 1; aTriIter <= aNBTriangles; ++aTriIter) | |
a47d34ef | 382 | { |
4178b353 | 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 | } | |
7fd59977 | 402 | |
4178b353 | 403 | convertDouble (aVNorm.X(), &aDataChunk[aByteCount]); aByteCount += 4; |
404 | convertDouble (aVNorm.Y(), &aDataChunk[aByteCount]); aByteCount += 4; | |
405 | convertDouble (aVNorm.Z(), &aDataChunk[aByteCount]); aByteCount += 4; | |
7fd59977 | 406 | |
4178b353 | 407 | convertDouble (aP1.X(), &aDataChunk[aByteCount]); aByteCount += 4; |
408 | convertDouble (aP1.Y(), &aDataChunk[aByteCount]); aByteCount += 4; | |
409 | convertDouble (aP1.Z(), &aDataChunk[aByteCount]); aByteCount += 4; | |
fcc61cc4 | 410 | |
4178b353 | 411 | convertDouble (aP2.X(), &aDataChunk[aByteCount]); aByteCount += 4; |
412 | convertDouble (aP2.Y(), &aDataChunk[aByteCount]); aByteCount += 4; | |
413 | convertDouble (aP2.Z(), &aDataChunk[aByteCount]); aByteCount += 4; | |
7fd59977 | 414 | |
4178b353 | 415 | convertDouble (aP3.X(), &aDataChunk[aByteCount]); aByteCount += 4; |
416 | convertDouble (aP3.Y(), &aDataChunk[aByteCount]); aByteCount += 4; | |
417 | convertDouble (aP3.Z(), &aDataChunk[aByteCount]); aByteCount += 4; | |
7fd59977 | 418 | |
4178b353 | 419 | aDataChunk[aByteCount] = 0; aByteCount += 1; |
420 | aDataChunk[aByteCount] = 0; aByteCount += 1; | |
7fd59977 | 421 | |
4178b353 | 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 | } | |
7fd59977 | 429 | |
4178b353 | 430 | aByteCount = 0; |
431 | } | |
7fd59977 | 432 | |
4178b353 | 433 | // update progress only per 1k triangles |
434 | if ((aTriIter % IND_THRESHOLD) == 0) | |
435 | { | |
1fc1a207 | 436 | if (!aPS.More()) |
437 | return Standard_False; | |
438 | aPS.Next(IND_THRESHOLD); | |
4178b353 | 439 | } |
7fd59977 | 440 | } |
441 | ||
4178b353 | 442 | // Write last part if necessary. |
443 | if (aByteCount != aChunkSize) | |
9c6afe19 | 444 | { |
4178b353 | 445 | if (fwrite (aDataChunk, 1, aByteCount, theFile) != aByteCount) |
446 | { | |
447 | return Standard_False; | |
448 | } | |
7fd59977 | 449 | } |
4178b353 | 450 | |
451 | return Standard_True; | |
7fd59977 | 452 | } |