Commit | Line | Data |
---|---|---|
7fd59977 | 1 | // File: RWStl.cxx |
2 | // Created: Thu Oct 13 13:41:29 1994 | |
3 | // Author: Marc LEGAY | |
4 | // <mle@bourdon> | |
5 | ||
ed482379 | 6 | // Copyright: Matra Datavision |
7fd59977 | 7 | |
ed482379 | 8 | #include <RWStl.ixx> |
7fd59977 | 9 | #include <OSD_Protection.hxx> |
10 | #include <OSD_File.hxx> | |
9c6afe19 | 11 | #include <Message_ProgressSentry.hxx> |
7fd59977 | 12 | #include <TCollection_AsciiString.hxx> |
13 | #include <Standard_NoMoreObject.hxx> | |
14 | #include <Standard_TypeMismatch.hxx> | |
15 | #include <Precision.hxx> | |
16 | #include <StlMesh_MeshExplorer.hxx> | |
17 | #include <OSD.hxx> | |
18 | #include <OSD_Host.hxx> | |
19 | #include <gp_XYZ.hxx> | |
20 | #include <gp.hxx> | |
21 | #include <stdio.h> | |
22 | #include <gp_Vec.hxx> | |
23 | ||
24 | ||
25 | // constants | |
ed482379 K |
26 | static const int HEADER_SIZE = 84; |
27 | static const int SIZEOF_STL_FACET = 50; | |
28 | static const int STL_MIN_FILE_SIZE = 284; | |
29 | static const int ASCII_LINES_PER_FACET = 7; | |
9c6afe19 | 30 | static const int IND_THRESHOLD = 1000; // increment the indicator every 1k triangles |
7fd59977 | 31 | |
ed482379 | 32 | //======================================================================= |
7fd59977 | 33 | //function : WriteInteger |
34 | //purpose : writing a Little Endian 32 bits integer | |
35 | //======================================================================= | |
36 | ||
37 | inline static void WriteInteger(OSD_File& ofile,const Standard_Integer value) | |
38 | { | |
39 | union { | |
40 | Standard_Integer i;// don't be afraid, this is just an unsigned int | |
41 | char c[4]; | |
42 | } bidargum; | |
43 | ||
44 | bidargum.i = value; | |
45 | ||
46 | Standard_Integer entier; | |
ed482379 | 47 | |
7fd59977 | 48 | entier = bidargum.c[0] & 0xFF; |
49 | entier |= (bidargum.c[1] & 0xFF) << 0x08; | |
50 | entier |= (bidargum.c[2] & 0xFF) << 0x10; | |
51 | entier |= (bidargum.c[3] & 0xFF) << 0x18; | |
52 | ||
53 | ofile.Write((char *)&entier,sizeof(bidargum.c)); | |
54 | } | |
55 | ||
56 | //======================================================================= | |
57 | //function : WriteDouble2Float | |
58 | //purpose : writing a Little Endian 32 bits float | |
59 | //======================================================================= | |
60 | ||
61 | inline static void WriteDouble2Float(OSD_File& ofile,Standard_Real value) | |
62 | { | |
63 | union { | |
64 | Standard_ShortReal f; | |
65 | char c[4]; | |
66 | } bidargum; | |
67 | ||
68 | bidargum.f = (Standard_ShortReal)value; | |
69 | ||
70 | Standard_Integer entier; | |
ed482379 | 71 | |
7fd59977 | 72 | entier = bidargum.c[0] & 0xFF; |
73 | entier |= (bidargum.c[1] & 0xFF) << 0x08; | |
74 | entier |= (bidargum.c[2] & 0xFF) << 0x10; | |
75 | entier |= (bidargum.c[3] & 0xFF) << 0x18; | |
76 | ||
77 | ofile.Write((char *)&entier,sizeof(bidargum.c)); | |
78 | } | |
79 | ||
80 | ||
81 | //======================================================================= | |
82 | //function : readFloat2Double | |
83 | //purpose : reading a Little Endian 32 bits float | |
84 | //======================================================================= | |
85 | ||
86 | inline static Standard_Real ReadFloat2Double(OSD_File &aFile) | |
87 | { | |
88 | union { | |
89 | Standard_Boolean i; // don't be afraid, this is just an unsigned int | |
90 | Standard_ShortReal f; | |
91 | }bidargum; | |
92 | ||
93 | char c[4]; | |
94 | Standard_Address adr; | |
95 | adr = (Standard_Address)c; | |
96 | Standard_Integer lread; | |
97 | aFile.Read(adr,4,lread); | |
98 | bidargum.i = c[0] & 0xFF; | |
99 | bidargum.i |= (c[1] & 0xFF) << 0x08; | |
100 | bidargum.i |= (c[2] & 0xFF) << 0x10; | |
101 | bidargum.i |= (c[3] & 0xFF) << 0x18; | |
102 | ||
103 | return (Standard_Real)(bidargum.f); | |
104 | } | |
105 | ||
106 | ||
107 | ||
108 | //======================================================================= | |
109 | //function : WriteBinary | |
110 | //purpose : write a binary STL file in Little Endian format | |
111 | //======================================================================= | |
112 | ||
9c6afe19 RK |
113 | Standard_Boolean RWStl::WriteBinary (const Handle(StlMesh_Mesh)& theMesh, |
114 | const OSD_Path& thePath, | |
115 | const Handle(Message_ProgressIndicator)& theProgInd) | |
7fd59977 | 116 | { |
9c6afe19 RK |
117 | OSD_File aFile (thePath); |
118 | aFile.Build (OSD_WriteOnly, OSD_Protection()); | |
7fd59977 | 119 | |
120 | Standard_Real x1, y1, z1; | |
121 | Standard_Real x2, y2, z2; | |
122 | Standard_Real x3, y3, z3; | |
ed482379 | 123 | |
9c6afe19 | 124 | // writing 80 bytes of the trash? |
7fd59977 | 125 | char sval[80]; |
9c6afe19 RK |
126 | aFile.Write ((Standard_Address)sval,80); |
127 | WriteInteger (aFile, theMesh->NbTriangles()); | |
128 | ||
7fd59977 | 129 | int dum=0; |
9c6afe19 RK |
130 | StlMesh_MeshExplorer aMexp (theMesh); |
131 | ||
132 | // create progress sentry for domains | |
133 | Standard_Integer aNbDomains = theMesh->NbDomains(); | |
134 | Message_ProgressSentry aDPS (theProgInd, "Mesh domains", 0, aNbDomains, 1); | |
135 | for (Standard_Integer nbd = 1; nbd <= aNbDomains && aDPS.More(); nbd++, aDPS.Next()) | |
136 | { | |
137 | // create progress sentry for triangles in domain | |
138 | Message_ProgressSentry aTPS (theProgInd, "Triangles", 0, | |
139 | theMesh->NbTriangles (nbd), IND_THRESHOLD); | |
140 | Standard_Integer aTriangleInd = 0; | |
141 | for (aMexp.InitTriangle (nbd); aMexp.MoreTriangle(); aMexp.NextTriangle()) | |
142 | { | |
143 | aMexp.TriangleVertices (x1,y1,z1,x2,y2,z2,x3,y3,z3); | |
144 | //pgo aMexp.TriangleOrientation (x,y,z); | |
145 | gp_XYZ Vect12 ((x2-x1), (y2-y1), (z2-z1)); | |
146 | gp_XYZ Vect13 ((x3-x1), (y3-y1), (z3-z1)); | |
147 | gp_XYZ Vnorm = Vect12 ^ Vect13; | |
148 | Standard_Real Vmodul = Vnorm.Modulus (); | |
149 | if (Vmodul > gp::Resolution()) | |
150 | { | |
151 | Vnorm.Divide(Vmodul); | |
152 | } | |
153 | else | |
154 | { | |
155 | // si Vnorm est quasi-nul, on le charge a 0 explicitement | |
156 | Vnorm.SetCoord (0., 0., 0.); | |
157 | } | |
158 | ||
159 | WriteDouble2Float (aFile, Vnorm.X()); | |
160 | WriteDouble2Float (aFile, Vnorm.Y()); | |
161 | WriteDouble2Float (aFile, Vnorm.Z()); | |
162 | ||
163 | WriteDouble2Float (aFile, x1); | |
164 | WriteDouble2Float (aFile, y1); | |
165 | WriteDouble2Float (aFile, z1); | |
166 | ||
167 | WriteDouble2Float (aFile, x2); | |
168 | WriteDouble2Float (aFile, y2); | |
169 | WriteDouble2Float (aFile, z2); | |
170 | ||
171 | WriteDouble2Float (aFile, x3); | |
172 | WriteDouble2Float (aFile, y3); | |
173 | WriteDouble2Float (aFile, z3); | |
ed482379 | 174 | |
9c6afe19 RK |
175 | aFile.Write (&dum, 2); |
176 | ||
177 | // update progress only per 1k triangles | |
178 | if (++aTriangleInd % IND_THRESHOLD == 0) | |
179 | { | |
180 | if (!aTPS.More()) | |
181 | break; | |
182 | aTPS.Next(); | |
7fd59977 | 183 | } |
9c6afe19 | 184 | } |
7fd59977 | 185 | } |
9c6afe19 RK |
186 | aFile.Close(); |
187 | Standard_Boolean isInterrupted = !aDPS.More(); | |
188 | return !isInterrupted; | |
7fd59977 | 189 | } |
190 | //======================================================================= | |
191 | //function : WriteAscii | |
192 | //purpose : write an ASCII STL file | |
193 | //======================================================================= | |
194 | ||
9c6afe19 RK |
195 | Standard_Boolean RWStl::WriteAscii (const Handle(StlMesh_Mesh)& theMesh, |
196 | const OSD_Path& thePath, | |
197 | const Handle(Message_ProgressIndicator)& theProgInd) | |
7fd59977 | 198 | { |
9c6afe19 | 199 | OSD_File theFile (thePath); |
7fd59977 | 200 | theFile.Build(OSD_WriteOnly,OSD_Protection()); |
9c6afe19 | 201 | TCollection_AsciiString buf ("solid\n"); |
7fd59977 | 202 | theFile.Write (buf,buf.Length());buf.Clear(); |
ed482379 | 203 | |
7fd59977 | 204 | Standard_Real x1, y1, z1; |
205 | Standard_Real x2, y2, z2; | |
206 | Standard_Real x3, y3, z3; | |
9c6afe19 RK |
207 | char sval[512]; |
208 | ||
209 | // create progress sentry for domains | |
210 | Standard_Integer aNbDomains = theMesh->NbDomains(); | |
211 | Message_ProgressSentry aDPS (theProgInd, "Mesh domains", 0, aNbDomains, 1); | |
212 | StlMesh_MeshExplorer aMexp (theMesh); | |
213 | for (Standard_Integer nbd = 1; nbd <= aNbDomains && aDPS.More(); nbd++, aDPS.Next()) | |
214 | { | |
215 | // create progress sentry for triangles in domain | |
216 | Message_ProgressSentry aTPS (theProgInd, "Triangles", 0, | |
217 | theMesh->NbTriangles (nbd), IND_THRESHOLD); | |
218 | Standard_Integer aTriangleInd = 0; | |
219 | for (aMexp.InitTriangle (nbd); aMexp.MoreTriangle(); aMexp.NextTriangle()) | |
220 | { | |
7fd59977 | 221 | aMexp.TriangleVertices (x1,y1,z1,x2,y2,z2,x3,y3,z3); |
ed482379 | 222 | |
7fd59977 | 223 | // Standard_Real x, y, z; |
224 | // aMexp.TriangleOrientation (x,y,z); | |
ed482379 | 225 | |
7fd59977 | 226 | gp_XYZ Vect12 ((x2-x1), (y2-y1), (z2-z1)); |
227 | gp_XYZ Vect23 ((x3-x2), (y3-y2), (z3-z2)); | |
9c6afe19 | 228 | gp_XYZ Vnorm = Vect12 ^ Vect23; |
7fd59977 | 229 | Standard_Real Vmodul = Vnorm.Modulus (); |
9c6afe19 RK |
230 | if (Vmodul > gp::Resolution()) |
231 | { | |
232 | Vnorm.Divide (Vmodul); | |
233 | } | |
234 | else | |
235 | { | |
236 | // si Vnorm est quasi-nul, on le charge a 0 explicitement | |
237 | Vnorm.SetCoord (0., 0., 0.); | |
7fd59977 | 238 | } |
9c6afe19 RK |
239 | sprintf (sval, |
240 | " facet normal % 12e % 12e % 12e\n" | |
241 | " outer loop\n" | |
242 | " vertex % 12e % 12e % 12e\n" | |
243 | " vertex % 12e % 12e % 12e\n" | |
244 | " vertex % 12e % 12e % 12e\n" | |
245 | " endloop\n" | |
246 | " endfacet\n", | |
247 | Vnorm.X(), Vnorm.Y(), Vnorm.Z(), | |
248 | x1, y1, z1, | |
249 | x2, y2, z2, | |
250 | x3, y3, z3); | |
68bc5ed7 | 251 | buf += sval; |
9c6afe19 RK |
252 | theFile.Write (buf, buf.Length()); buf.Clear(); |
253 | ||
254 | // update progress only per 1k triangles | |
255 | if (++aTriangleInd % IND_THRESHOLD == 0) | |
256 | { | |
257 | if (!aTPS.More()) | |
258 | break; | |
259 | aTPS.Next(); | |
7fd59977 | 260 | } |
7fd59977 | 261 | } |
262 | } | |
ed482379 | 263 | |
7fd59977 | 264 | buf += "endsolid\n"; |
9c6afe19 RK |
265 | theFile.Write (buf, buf.Length()); buf.Clear(); |
266 | theFile.Close(); | |
267 | Standard_Boolean isInterrupted = !aDPS.More(); | |
268 | return !isInterrupted; | |
7fd59977 | 269 | } |
270 | //======================================================================= | |
271 | //function : ReadFile | |
ed482379 K |
272 | //Design : |
273 | //Warning : | |
7fd59977 | 274 | //======================================================================= |
275 | ||
9c6afe19 RK |
276 | Handle_StlMesh_Mesh RWStl::ReadFile (const OSD_Path& thePath, |
277 | const Handle(Message_ProgressIndicator)& theProgInd) | |
7fd59977 | 278 | { |
9c6afe19 | 279 | OSD_File file (thePath); |
7fd59977 | 280 | file.Open(OSD_ReadOnly,OSD_Protection(OSD_RWD,OSD_RWD,OSD_RWD,OSD_RWD)); |
281 | Standard_Boolean IsAscii; | |
282 | unsigned char str[128]; | |
283 | Standard_Integer lread,i; | |
284 | Standard_Address ach; | |
285 | ach = (Standard_Address)str; | |
ed482379 | 286 | |
7fd59977 | 287 | // we skip the header which is in Ascii for both modes |
288 | file.Read(ach,HEADER_SIZE,lread); | |
289 | ||
290 | // we read 128 characters to detect if we have a non-ascii char | |
291 | file.Read(ach,sizeof(str),lread); | |
292 | ||
293 | IsAscii = Standard_True; | |
294 | for (i = 0; i< lread && IsAscii; ++i) { | |
295 | if (str[i] > '~') { | |
296 | IsAscii = Standard_False; | |
297 | } | |
298 | } | |
ed482379 K |
299 | #ifdef DEB |
300 | cout << (IsAscii ? "ascii\n" : "binary\n"); | |
301 | #endif | |
7fd59977 | 302 | file.Close(); |
ed482379 | 303 | |
9c6afe19 RK |
304 | return IsAscii ? RWStl::ReadAscii (thePath, theProgInd) |
305 | : RWStl::ReadBinary (thePath, theProgInd); | |
7fd59977 | 306 | } |
307 | ||
308 | //======================================================================= | |
309 | //function : ReadBinary | |
ed482379 K |
310 | //Design : |
311 | //Warning : | |
7fd59977 | 312 | //======================================================================= |
313 | ||
9c6afe19 RK |
314 | Handle_StlMesh_Mesh RWStl::ReadBinary (const OSD_Path& thePath, |
315 | const Handle(Message_ProgressIndicator)& /*theProgInd*/) | |
7fd59977 | 316 | { |
317 | Standard_Integer NBFACET; | |
318 | Standard_Integer ifacet; | |
319 | Standard_Real fx,fy,fz,fx1,fy1,fz1,fx2,fy2,fz2,fx3,fy3,fz3; | |
320 | Standard_Integer i1,i2,i3,lread; | |
321 | char buftest[5]; | |
322 | Standard_Address adr; | |
323 | adr = (Standard_Address)buftest; | |
324 | ||
ed482379 | 325 | // Open the file |
9c6afe19 | 326 | OSD_File theFile (thePath); |
7fd59977 | 327 | theFile.Open(OSD_ReadOnly,OSD_Protection(OSD_RWD,OSD_RWD,OSD_RWD,OSD_RWD)); |
328 | ||
329 | // the size of the file (minus the header size) | |
330 | // must be a multiple of SIZEOF_STL_FACET | |
331 | ||
332 | // compute file size | |
333 | Standard_Integer filesize = theFile.Size(); | |
334 | ||
ed482379 | 335 | if ( (filesize - HEADER_SIZE) % SIZEOF_STL_FACET !=0 |
7fd59977 | 336 | || (filesize < STL_MIN_FILE_SIZE)) { |
337 | Standard_NoMoreObject::Raise("RWStl::ReadBinary (wrong file size)"); | |
338 | } | |
339 | ||
340 | // don't trust the number of triangles which is coded in the file | |
341 | // sometimes it is wrong, and with this technique we don't need to swap endians for integer | |
342 | NBFACET = ((filesize - HEADER_SIZE) / SIZEOF_STL_FACET); | |
343 | ||
344 | // skip the header | |
345 | theFile.Seek(HEADER_SIZE,OSD_FromBeginning); | |
346 | ||
347 | // create the StlMesh_Mesh object | |
348 | Handle(StlMesh_Mesh) ReadMesh = new StlMesh_Mesh (); | |
349 | ReadMesh->AddDomain (); | |
350 | ||
351 | for (ifacet=1; ifacet<=NBFACET; ++ifacet) { | |
352 | // read normal coordinates | |
353 | fx = ReadFloat2Double(theFile); | |
354 | fy = ReadFloat2Double(theFile); | |
355 | fz = ReadFloat2Double(theFile); | |
356 | ||
357 | // read vertex 1 | |
358 | fx1 = ReadFloat2Double(theFile); | |
359 | fy1 = ReadFloat2Double(theFile); | |
360 | fz1 = ReadFloat2Double(theFile); | |
361 | ||
362 | // read vertex 2 | |
363 | fx2 = ReadFloat2Double(theFile); | |
364 | fy2 = ReadFloat2Double(theFile); | |
365 | fz2 = ReadFloat2Double(theFile); | |
366 | ||
367 | // read vertex 3 | |
368 | fx3 = ReadFloat2Double(theFile); | |
369 | fy3 = ReadFloat2Double(theFile); | |
370 | fz3 = ReadFloat2Double(theFile); | |
371 | ||
372 | i1 = ReadMesh->AddOnlyNewVertex (fx1,fy1,fz1); | |
373 | i2 = ReadMesh->AddOnlyNewVertex (fx2,fy2,fz2); | |
374 | i3 = ReadMesh->AddOnlyNewVertex (fx3,fy3,fz3); | |
375 | ReadMesh->AddTriangle (i1,i2,i3,fx,fy,fz); | |
376 | ||
377 | // skip extra bytes | |
378 | theFile.Read(adr,2,lread); | |
379 | } | |
380 | ||
381 | theFile.Close (); | |
382 | return ReadMesh; | |
ed482379 | 383 | |
7fd59977 | 384 | } |
385 | //======================================================================= | |
386 | //function : ReadAscii | |
ed482379 K |
387 | //Design : |
388 | //Warning : | |
7fd59977 | 389 | //======================================================================= |
390 | ||
9c6afe19 RK |
391 | Handle_StlMesh_Mesh RWStl::ReadAscii (const OSD_Path& thePath, |
392 | const Handle(Message_ProgressIndicator)& theProgInd) | |
7fd59977 | 393 | { |
394 | TCollection_AsciiString filename; | |
395 | long ipos; | |
396 | Standard_Integer nbLines = 0; | |
397 | Standard_Integer nbTris = 0; | |
398 | Standard_Integer iTri; | |
399 | Standard_ShortReal x[4],y[4],z[4]; | |
400 | Standard_Integer i1,i2,i3; | |
401 | Handle(StlMesh_Mesh) ReadMesh; | |
402 | ||
9c6afe19 | 403 | thePath.SystemName (filename); |
7fd59977 | 404 | |
ed482379 | 405 | // Open the file |
7fd59977 | 406 | FILE* file = fopen(filename.ToCString(),"r"); |
407 | ||
408 | fseek(file,0L,SEEK_END); | |
409 | ||
410 | long filesize = ftell(file); | |
411 | ||
412 | fclose(file); | |
413 | file = fopen(filename.ToCString(),"r"); | |
414 | ||
415 | ||
416 | ||
417 | // count the number of lines | |
418 | for (ipos = 0; ipos < filesize; ++ipos) { | |
419 | if (getc(file) == '\n') | |
420 | nbLines++; | |
421 | } | |
422 | ||
423 | // compute number of triangles | |
424 | nbTris = (nbLines / ASCII_LINES_PER_FACET); | |
425 | ||
426 | // go back to the beginning of the file | |
427 | // fclose(file); | |
428 | // file = fopen(filename.ToCString(),"r"); | |
429 | rewind(file); | |
430 | ||
431 | // skip header | |
432 | while (getc(file) != '\n'); | |
ed482379 K |
433 | #ifdef DEB |
434 | cout << "start mesh\n"; | |
435 | #endif | |
436 | ReadMesh = new StlMesh_Mesh(); | |
7fd59977 | 437 | ReadMesh->AddDomain(); |
438 | ||
439 | // main reading | |
9c6afe19 RK |
440 | Message_ProgressSentry aPS (theProgInd, "Triangles", 0, (nbTris - 1) * 1.0 / IND_THRESHOLD, 1); |
441 | for (iTri = 0; iTri < nbTris && aPS.More();) | |
442 | { | |
7fd59977 | 443 | // reading the facet normal |
444 | fscanf(file,"%*s %*s %f %f %f\n",&x[0],&y[0],&z[0]); | |
445 | ||
446 | // skip the keywords "outer loop" | |
447 | fscanf(file,"%*s %*s"); | |
448 | ||
449 | // reading vertex | |
450 | fscanf(file,"%*s %f %f %f\n",&x[1],&y[1],&z[1]); | |
451 | fscanf(file,"%*s %f %f %f\n",&x[2],&y[2],&z[2]); | |
452 | fscanf(file,"%*s %f %f %f\n",&x[3],&y[3],&z[3]); | |
453 | ||
ed482379 | 454 | // here the facet must be built and put in the mesh datastructure |
7fd59977 | 455 | |
456 | i1 = ReadMesh->AddOnlyNewVertex ((Standard_Real)x[1],(Standard_Real)y[1],(Standard_Real)z[1]); | |
457 | i2 = ReadMesh->AddOnlyNewVertex ((Standard_Real)x[2],(Standard_Real)y[2],(Standard_Real)z[2]); | |
458 | i3 = ReadMesh->AddOnlyNewVertex ((Standard_Real)x[3],(Standard_Real)y[3],(Standard_Real)z[3]); | |
459 | ReadMesh->AddTriangle (i1,i2,i3,(Standard_Real)x[0],(Standard_Real)y[0],(Standard_Real)z[0]); | |
460 | ||
461 | // skip the keywords "endloop" | |
462 | fscanf(file,"%*s"); | |
463 | ||
464 | // skip the keywords "endfacet" | |
465 | fscanf(file,"%*s"); | |
466 | ||
9c6afe19 RK |
467 | // update progress only per 1k triangles |
468 | if (++iTri % IND_THRESHOLD == 0) | |
469 | aPS.Next(); | |
7fd59977 | 470 | } |
ed482379 K |
471 | #ifdef DEB |
472 | cout << "end mesh\n"; | |
473 | #endif | |
7fd59977 | 474 | fclose(file); |
475 | return ReadMesh; | |
ed482379 | 476 | |
7fd59977 | 477 | } |