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