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