OCC22572 Support of progress indicator in RWStl and optimization of reading Ascii.
[occt.git] / src / RWStl / RWStl.cxx
1 // File:        RWStl.cxx
2 // Created:     Thu Oct 13 13:41:29 1994
3 // Author:      Marc LEGAY
4 //              <mle@bourdon>
5
6 // Copyright:    Matra Datavision
7
8 #include <RWStl.ixx>
9 #include <OSD_Protection.hxx>
10 #include <OSD_File.hxx>
11 #include <Message_ProgressSentry.hxx>
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
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;
30 static const int IND_THRESHOLD         = 1000; // increment the indicator every 1k triangles
31
32 //=======================================================================
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;
47
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;
71
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
113 Standard_Boolean RWStl::WriteBinary (const Handle(StlMesh_Mesh)& theMesh,
114                                      const OSD_Path& thePath,
115                                      const Handle(Message_ProgressIndicator)& theProgInd)
116 {
117   OSD_File aFile (thePath);
118   aFile.Build (OSD_WriteOnly, OSD_Protection());
119
120   Standard_Real x1, y1, z1;
121   Standard_Real x2, y2, z2;
122   Standard_Real x3, y3, z3;
123
124   // writing 80 bytes of the trash?
125   char sval[80];
126   aFile.Write ((Standard_Address)sval,80);
127   WriteInteger (aFile, theMesh->NbTriangles());
128
129   int dum=0;
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);
174
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();
183       }
184     }
185   }
186   aFile.Close();
187   Standard_Boolean isInterrupted = !aDPS.More();
188   return !isInterrupted;
189 }
190 //=======================================================================
191 //function : WriteAscii
192 //purpose  : write an ASCII STL file
193 //=======================================================================
194
195 Standard_Boolean RWStl::WriteAscii (const Handle(StlMesh_Mesh)& theMesh,
196                                     const OSD_Path& thePath,
197                                     const Handle(Message_ProgressIndicator)& theProgInd)
198 {
199   OSD_File theFile (thePath);
200   theFile.Build(OSD_WriteOnly,OSD_Protection());
201   TCollection_AsciiString buf ("solid\n");
202   theFile.Write (buf,buf.Length());buf.Clear();
203
204   Standard_Real x1, y1, z1;
205   Standard_Real x2, y2, z2;
206   Standard_Real x3, y3, z3;
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     {
221       aMexp.TriangleVertices (x1,y1,z1,x2,y2,z2,x3,y3,z3);
222
223 //      Standard_Real x, y, z;
224 //      aMexp.TriangleOrientation (x,y,z);
225
226       gp_XYZ Vect12 ((x2-x1), (y2-y1), (z2-z1));
227       gp_XYZ Vect23 ((x3-x2), (y3-y2), (z3-z2));
228       gp_XYZ Vnorm = Vect12 ^ Vect23;
229       Standard_Real Vmodul = Vnorm.Modulus ();
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.);
238       }
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);
251       theFile.Write (buf, buf.Length()); buf.Clear();
252
253       // update progress only per 1k triangles
254       if (++aTriangleInd % IND_THRESHOLD == 0)
255       {
256         if (!aTPS.More())
257             break;
258         aTPS.Next();
259       }
260     }
261   }
262
263   buf += "endsolid\n";
264   theFile.Write (buf, buf.Length()); buf.Clear();
265   theFile.Close();
266   Standard_Boolean isInterrupted = !aDPS.More();
267   return !isInterrupted;
268 }
269 //=======================================================================
270 //function : ReadFile
271 //Design   :
272 //Warning  :
273 //=======================================================================
274
275 Handle_StlMesh_Mesh RWStl::ReadFile (const OSD_Path& thePath,
276                                      const Handle(Message_ProgressIndicator)& theProgInd)
277 {
278   OSD_File file (thePath);
279   file.Open(OSD_ReadOnly,OSD_Protection(OSD_RWD,OSD_RWD,OSD_RWD,OSD_RWD));
280   Standard_Boolean IsAscii;
281   unsigned char str[128];
282   Standard_Integer lread,i;
283   Standard_Address ach;
284   ach = (Standard_Address)str;
285
286   // we skip the header which is in Ascii for both modes
287   file.Read(ach,HEADER_SIZE,lread);
288
289   // we read 128 characters to detect if we have a non-ascii char
290   file.Read(ach,sizeof(str),lread);
291
292   IsAscii = Standard_True;
293   for (i = 0; i< lread && IsAscii; ++i) {
294     if (str[i] > '~') {
295       IsAscii = Standard_False;
296     }
297   }
298 #ifdef DEB
299   cout << (IsAscii ? "ascii\n" : "binary\n");
300 #endif
301   file.Close();
302
303   return IsAscii ? RWStl::ReadAscii  (thePath, theProgInd)
304                  : RWStl::ReadBinary (thePath, theProgInd);
305 }
306
307 //=======================================================================
308 //function : ReadBinary
309 //Design   :
310 //Warning  :
311 //=======================================================================
312
313 Handle_StlMesh_Mesh RWStl::ReadBinary (const OSD_Path& thePath,
314                                        const Handle(Message_ProgressIndicator)& /*theProgInd*/)
315 {
316   Standard_Integer NBFACET;
317   Standard_Integer ifacet;
318   Standard_Real fx,fy,fz,fx1,fy1,fz1,fx2,fy2,fz2,fx3,fy3,fz3;
319   Standard_Integer i1,i2,i3,lread;
320   char buftest[5];
321   Standard_Address adr;
322   adr = (Standard_Address)buftest;
323
324   // Open the file
325   OSD_File theFile (thePath);
326   theFile.Open(OSD_ReadOnly,OSD_Protection(OSD_RWD,OSD_RWD,OSD_RWD,OSD_RWD));
327
328   // the size of the file (minus the header size)
329   // must be a multiple of SIZEOF_STL_FACET
330
331   // compute file size
332   Standard_Integer filesize = theFile.Size();
333
334   if ( (filesize - HEADER_SIZE) % SIZEOF_STL_FACET !=0
335         || (filesize < STL_MIN_FILE_SIZE)) {
336     Standard_NoMoreObject::Raise("RWStl::ReadBinary (wrong file size)");
337   }
338
339   // don't trust the number of triangles which is coded in the file
340   // sometimes it is wrong, and with this technique we don't need to swap endians for integer
341   NBFACET = ((filesize - HEADER_SIZE) / SIZEOF_STL_FACET);
342
343   // skip the header
344   theFile.Seek(HEADER_SIZE,OSD_FromBeginning);
345
346   // create the StlMesh_Mesh object
347   Handle(StlMesh_Mesh) ReadMesh = new StlMesh_Mesh ();
348   ReadMesh->AddDomain ();
349
350   for (ifacet=1; ifacet<=NBFACET; ++ifacet) {
351     // read normal coordinates
352     fx = ReadFloat2Double(theFile);
353     fy = ReadFloat2Double(theFile);
354     fz = ReadFloat2Double(theFile);
355
356     // read vertex 1
357     fx1 = ReadFloat2Double(theFile);
358     fy1 = ReadFloat2Double(theFile);
359     fz1 = ReadFloat2Double(theFile);
360
361     // read vertex 2
362     fx2 = ReadFloat2Double(theFile);
363     fy2 = ReadFloat2Double(theFile);
364     fz2 = ReadFloat2Double(theFile);
365
366     // read vertex 3
367     fx3 = ReadFloat2Double(theFile);
368     fy3 = ReadFloat2Double(theFile);
369     fz3 = ReadFloat2Double(theFile);
370
371     i1 = ReadMesh->AddOnlyNewVertex (fx1,fy1,fz1);
372     i2 = ReadMesh->AddOnlyNewVertex (fx2,fy2,fz2);
373     i3 = ReadMesh->AddOnlyNewVertex (fx3,fy3,fz3);
374     ReadMesh->AddTriangle (i1,i2,i3,fx,fy,fz);
375
376     // skip extra bytes
377     theFile.Read(adr,2,lread);
378   }
379
380   theFile.Close ();
381   return ReadMesh;
382
383 }
384 //=======================================================================
385 //function : ReadAscii
386 //Design   :
387 //Warning  :
388 //=======================================================================
389
390 Handle_StlMesh_Mesh RWStl::ReadAscii (const OSD_Path& thePath,
391                                       const Handle(Message_ProgressIndicator)& theProgInd)
392 {
393   TCollection_AsciiString filename;
394   long ipos;
395   Standard_Integer nbLines = 0;
396   Standard_Integer nbTris = 0;
397   Standard_Integer iTri;
398   Standard_ShortReal x[4],y[4],z[4];
399   Standard_Integer i1,i2,i3;
400   Handle(StlMesh_Mesh) ReadMesh;
401
402   thePath.SystemName (filename);
403
404   // Open the file
405   FILE* file = fopen(filename.ToCString(),"r");
406
407   fseek(file,0L,SEEK_END);
408
409   long filesize = ftell(file);
410
411   fclose(file);
412   file = fopen(filename.ToCString(),"r");
413
414
415
416   // count the number of lines
417   for (ipos = 0; ipos < filesize; ++ipos) {
418           if (getc(file) == '\n')
419         nbLines++;
420   }
421
422   // compute number of triangles
423   nbTris = (nbLines / ASCII_LINES_PER_FACET);
424
425   // go back to the beginning of the file
426 //  fclose(file);
427 //  file = fopen(filename.ToCString(),"r");
428   rewind(file);
429
430   // skip header
431   while (getc(file) != '\n');
432 #ifdef DEB
433   cout << "start mesh\n";
434 #endif
435   ReadMesh = new StlMesh_Mesh();
436   ReadMesh->AddDomain();
437
438   // main reading
439   Message_ProgressSentry aPS (theProgInd, "Triangles", 0, (nbTris - 1) * 1.0 / IND_THRESHOLD, 1);
440   for (iTri = 0; iTri < nbTris && aPS.More();)
441   {
442     // reading the facet normal
443     fscanf(file,"%*s %*s %f %f %f\n",&x[0],&y[0],&z[0]);
444
445     // skip the keywords "outer loop"
446     fscanf(file,"%*s %*s");
447
448     // reading vertex
449     fscanf(file,"%*s %f %f %f\n",&x[1],&y[1],&z[1]);
450     fscanf(file,"%*s %f %f %f\n",&x[2],&y[2],&z[2]);
451     fscanf(file,"%*s %f %f %f\n",&x[3],&y[3],&z[3]);
452
453     // here the facet must be built and put in the mesh datastructure
454
455     i1 = ReadMesh->AddOnlyNewVertex ((Standard_Real)x[1],(Standard_Real)y[1],(Standard_Real)z[1]);
456     i2 = ReadMesh->AddOnlyNewVertex ((Standard_Real)x[2],(Standard_Real)y[2],(Standard_Real)z[2]);
457     i3 = ReadMesh->AddOnlyNewVertex ((Standard_Real)x[3],(Standard_Real)y[3],(Standard_Real)z[3]);
458     ReadMesh->AddTriangle (i1,i2,i3,(Standard_Real)x[0],(Standard_Real)y[0],(Standard_Real)z[0]);
459
460     // skip the keywords "endloop"
461     fscanf(file,"%*s");
462
463     // skip the keywords "endfacet"
464     fscanf(file,"%*s");
465
466     // update progress only per 1k triangles
467     if (++iTri % IND_THRESHOLD == 0)
468       aPS.Next();
469   }
470 #ifdef DEB
471   cout << "end mesh\n";
472 #endif
473   fclose(file);
474   return ReadMesh;
475
476 }