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
6 // This file is part of Open CASCADE Technology software library.
8 // This library is free software; you can redistribute it and / or modify it
9 // under the terms of the GNU Lesser General Public 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.
14 // Alternatively, this file may be used under the terms of Open CASCADE
15 // commercial license or contractual agreement.
18 #include <OSD_Protection.hxx>
19 #include <OSD_File.hxx>
20 #include <Message_ProgressSentry.hxx>
21 #include <TCollection_AsciiString.hxx>
22 #include <Standard_NoMoreObject.hxx>
23 #include <Standard_TypeMismatch.hxx>
24 #include <Precision.hxx>
25 #include <StlMesh_MeshExplorer.hxx>
27 #include <OSD_Host.hxx>
35 static const int HEADER_SIZE = 84;
36 static const int SIZEOF_STL_FACET = 50;
37 static const int STL_MIN_FILE_SIZE = 284;
38 static const int ASCII_LINES_PER_FACET = 7;
39 static const int IND_THRESHOLD = 1000; // increment the indicator every 1k triangles
41 //=======================================================================
42 //function : WriteInteger
43 //purpose : writing a Little Endian 32 bits integer
44 //=======================================================================
46 inline static void WriteInteger(OSD_File& ofile,const Standard_Integer value)
49 Standard_Integer i;// don't be afraid, this is just an unsigned int
55 Standard_Integer entier;
57 entier = bidargum.c[0] & 0xFF;
58 entier |= (bidargum.c[1] & 0xFF) << 0x08;
59 entier |= (bidargum.c[2] & 0xFF) << 0x10;
60 entier |= (bidargum.c[3] & 0xFF) << 0x18;
62 ofile.Write((char *)&entier,sizeof(bidargum.c));
65 //=======================================================================
66 //function : WriteDouble2Float
67 //purpose : writing a Little Endian 32 bits float
68 //=======================================================================
70 inline static void WriteDouble2Float(OSD_File& ofile,Standard_Real value)
77 bidargum.f = (Standard_ShortReal)value;
79 Standard_Integer entier;
81 entier = bidargum.c[0] & 0xFF;
82 entier |= (bidargum.c[1] & 0xFF) << 0x08;
83 entier |= (bidargum.c[2] & 0xFF) << 0x10;
84 entier |= (bidargum.c[3] & 0xFF) << 0x18;
86 ofile.Write((char *)&entier,sizeof(bidargum.c));
90 //=======================================================================
91 //function : readFloat2Double
92 //purpose : reading a Little Endian 32 bits float
93 //=======================================================================
95 inline static Standard_Real ReadFloat2Double(OSD_File &aFile)
98 Standard_Boolean i; // don't be afraid, this is just an unsigned int
103 Standard_Address adr;
104 adr = (Standard_Address)c;
105 Standard_Integer lread;
106 aFile.Read(adr,4,lread);
107 bidargum.i = c[0] & 0xFF;
108 bidargum.i |= (c[1] & 0xFF) << 0x08;
109 bidargum.i |= (c[2] & 0xFF) << 0x10;
110 bidargum.i |= (c[3] & 0xFF) << 0x18;
112 return (Standard_Real)(bidargum.f);
117 //=======================================================================
118 //function : WriteBinary
119 //purpose : write a binary STL file in Little Endian format
120 //=======================================================================
122 Standard_Boolean RWStl::WriteBinary (const Handle(StlMesh_Mesh)& theMesh,
123 const OSD_Path& thePath,
124 const Handle(Message_ProgressIndicator)& theProgInd)
126 OSD_File aFile (thePath);
127 aFile.Build (OSD_WriteOnly, OSD_Protection());
129 Standard_Real x1, y1, z1;
130 Standard_Real x2, y2, z2;
131 Standard_Real x3, y3, z3;
133 // writing 80 bytes of the trash?
135 aFile.Write ((Standard_Address)sval,80);
136 WriteInteger (aFile, theMesh->NbTriangles());
139 StlMesh_MeshExplorer aMexp (theMesh);
141 // create progress sentry for domains
142 Standard_Integer aNbDomains = theMesh->NbDomains();
143 Message_ProgressSentry aDPS (theProgInd, "Mesh domains", 0, aNbDomains, 1);
144 for (Standard_Integer nbd = 1; nbd <= aNbDomains && aDPS.More(); nbd++, aDPS.Next())
146 // create progress sentry for triangles in domain
147 Message_ProgressSentry aTPS (theProgInd, "Triangles", 0,
148 theMesh->NbTriangles (nbd), IND_THRESHOLD);
149 Standard_Integer aTriangleInd = 0;
150 for (aMexp.InitTriangle (nbd); aMexp.MoreTriangle(); aMexp.NextTriangle())
152 aMexp.TriangleVertices (x1,y1,z1,x2,y2,z2,x3,y3,z3);
153 //pgo aMexp.TriangleOrientation (x,y,z);
154 gp_XYZ Vect12 ((x2-x1), (y2-y1), (z2-z1));
155 gp_XYZ Vect13 ((x3-x1), (y3-y1), (z3-z1));
156 gp_XYZ Vnorm = Vect12 ^ Vect13;
157 Standard_Real Vmodul = Vnorm.Modulus ();
158 if (Vmodul > gp::Resolution())
160 Vnorm.Divide(Vmodul);
164 // si Vnorm est quasi-nul, on le charge a 0 explicitement
165 Vnorm.SetCoord (0., 0., 0.);
168 WriteDouble2Float (aFile, Vnorm.X());
169 WriteDouble2Float (aFile, Vnorm.Y());
170 WriteDouble2Float (aFile, Vnorm.Z());
172 WriteDouble2Float (aFile, x1);
173 WriteDouble2Float (aFile, y1);
174 WriteDouble2Float (aFile, z1);
176 WriteDouble2Float (aFile, x2);
177 WriteDouble2Float (aFile, y2);
178 WriteDouble2Float (aFile, z2);
180 WriteDouble2Float (aFile, x3);
181 WriteDouble2Float (aFile, y3);
182 WriteDouble2Float (aFile, z3);
184 aFile.Write (&dum, 2);
186 // update progress only per 1k triangles
187 if (++aTriangleInd % IND_THRESHOLD == 0)
196 Standard_Boolean isInterrupted = !aDPS.More();
197 return !isInterrupted;
199 //=======================================================================
200 //function : WriteAscii
201 //purpose : write an ASCII STL file
202 //=======================================================================
204 Standard_Boolean RWStl::WriteAscii (const Handle(StlMesh_Mesh)& theMesh,
205 const OSD_Path& thePath,
206 const Handle(Message_ProgressIndicator)& theProgInd)
208 OSD_File theFile (thePath);
209 theFile.Build(OSD_WriteOnly,OSD_Protection());
210 TCollection_AsciiString buf ("solid\n");
211 theFile.Write (buf,buf.Length());buf.Clear();
213 Standard_Real x1, y1, z1;
214 Standard_Real x2, y2, z2;
215 Standard_Real x3, y3, z3;
218 // create progress sentry for domains
219 Standard_Integer aNbDomains = theMesh->NbDomains();
220 Message_ProgressSentry aDPS (theProgInd, "Mesh domains", 0, aNbDomains, 1);
221 StlMesh_MeshExplorer aMexp (theMesh);
222 for (Standard_Integer nbd = 1; nbd <= aNbDomains && aDPS.More(); nbd++, aDPS.Next())
224 // create progress sentry for triangles in domain
225 Message_ProgressSentry aTPS (theProgInd, "Triangles", 0,
226 theMesh->NbTriangles (nbd), IND_THRESHOLD);
227 Standard_Integer aTriangleInd = 0;
228 for (aMexp.InitTriangle (nbd); aMexp.MoreTriangle(); aMexp.NextTriangle())
230 aMexp.TriangleVertices (x1,y1,z1,x2,y2,z2,x3,y3,z3);
232 // Standard_Real x, y, z;
233 // aMexp.TriangleOrientation (x,y,z);
235 gp_XYZ Vect12 ((x2-x1), (y2-y1), (z2-z1));
236 gp_XYZ Vect23 ((x3-x2), (y3-y2), (z3-z2));
237 gp_XYZ Vnorm = Vect12 ^ Vect23;
238 Standard_Real Vmodul = Vnorm.Modulus ();
239 if (Vmodul > gp::Resolution())
241 Vnorm.Divide (Vmodul);
245 // si Vnorm est quasi-nul, on le charge a 0 explicitement
246 Vnorm.SetCoord (0., 0., 0.);
249 " facet normal % 12e % 12e % 12e\n"
251 " vertex % 12e % 12e % 12e\n"
252 " vertex % 12e % 12e % 12e\n"
253 " vertex % 12e % 12e % 12e\n"
256 Vnorm.X(), Vnorm.Y(), Vnorm.Z(),
261 theFile.Write (buf, buf.Length()); buf.Clear();
263 // update progress only per 1k triangles
264 if (++aTriangleInd % IND_THRESHOLD == 0)
274 theFile.Write (buf, buf.Length()); buf.Clear();
276 Standard_Boolean isInterrupted = !aDPS.More();
277 return !isInterrupted;
279 //=======================================================================
280 //function : ReadFile
283 //=======================================================================
285 Handle_StlMesh_Mesh RWStl::ReadFile (const OSD_Path& thePath,
286 const Handle(Message_ProgressIndicator)& theProgInd)
288 OSD_File file (thePath);
289 file.Open(OSD_ReadOnly,OSD_Protection(OSD_RWD,OSD_RWD,OSD_RWD,OSD_RWD));
290 Standard_Boolean IsAscii;
291 unsigned char str[128];
292 Standard_Integer lread,i;
293 Standard_Address ach;
294 ach = (Standard_Address)str;
296 // we skip the header which is in Ascii for both modes
297 file.Read(ach,HEADER_SIZE,lread);
299 // we read 128 characters to detect if we have a non-ascii char
300 file.Read(ach,sizeof(str),lread);
302 IsAscii = Standard_True;
303 for (i = 0; i< lread && IsAscii; ++i) {
305 IsAscii = Standard_False;
309 cout << (IsAscii ? "ascii\n" : "binary\n");
313 return IsAscii ? RWStl::ReadAscii (thePath, theProgInd)
314 : RWStl::ReadBinary (thePath, theProgInd);
317 //=======================================================================
318 //function : ReadBinary
321 //=======================================================================
323 Handle_StlMesh_Mesh RWStl::ReadBinary (const OSD_Path& thePath,
324 const Handle(Message_ProgressIndicator)& /*theProgInd*/)
326 Standard_Integer NBFACET;
327 Standard_Integer ifacet;
328 Standard_Real fx,fy,fz,fx1,fy1,fz1,fx2,fy2,fz2,fx3,fy3,fz3;
329 Standard_Integer i1,i2,i3,lread;
331 Standard_Address adr;
332 adr = (Standard_Address)buftest;
335 OSD_File theFile (thePath);
336 theFile.Open(OSD_ReadOnly,OSD_Protection(OSD_RWD,OSD_RWD,OSD_RWD,OSD_RWD));
338 // the size of the file (minus the header size)
339 // must be a multiple of SIZEOF_STL_FACET
342 Standard_Size filesize = theFile.Size();
344 if ( (filesize - HEADER_SIZE) % SIZEOF_STL_FACET !=0
345 || (filesize < STL_MIN_FILE_SIZE)) {
346 Standard_NoMoreObject::Raise("RWStl::ReadBinary (wrong file size)");
349 // don't trust the number of triangles which is coded in the file
350 // sometimes it is wrong, and with this technique we don't need to swap endians for integer
351 NBFACET = (Standard_Integer)((filesize - HEADER_SIZE) / SIZEOF_STL_FACET);
354 theFile.Seek(HEADER_SIZE,OSD_FromBeginning);
356 // create the StlMesh_Mesh object
357 Handle(StlMesh_Mesh) ReadMesh = new StlMesh_Mesh ();
358 ReadMesh->AddDomain ();
360 for (ifacet=1; ifacet<=NBFACET; ++ifacet) {
361 // read normal coordinates
362 fx = ReadFloat2Double(theFile);
363 fy = ReadFloat2Double(theFile);
364 fz = ReadFloat2Double(theFile);
367 fx1 = ReadFloat2Double(theFile);
368 fy1 = ReadFloat2Double(theFile);
369 fz1 = ReadFloat2Double(theFile);
372 fx2 = ReadFloat2Double(theFile);
373 fy2 = ReadFloat2Double(theFile);
374 fz2 = ReadFloat2Double(theFile);
377 fx3 = ReadFloat2Double(theFile);
378 fy3 = ReadFloat2Double(theFile);
379 fz3 = ReadFloat2Double(theFile);
381 i1 = ReadMesh->AddOnlyNewVertex (fx1,fy1,fz1);
382 i2 = ReadMesh->AddOnlyNewVertex (fx2,fy2,fz2);
383 i3 = ReadMesh->AddOnlyNewVertex (fx3,fy3,fz3);
384 ReadMesh->AddTriangle (i1,i2,i3,fx,fy,fz);
387 theFile.Read(adr,2,lread);
394 //=======================================================================
395 //function : ReadAscii
398 //=======================================================================
400 Handle_StlMesh_Mesh RWStl::ReadAscii (const OSD_Path& thePath,
401 const Handle(Message_ProgressIndicator)& theProgInd)
403 TCollection_AsciiString filename;
405 Standard_Integer nbLines = 0;
406 Standard_Integer nbTris = 0;
407 Standard_Integer iTri;
408 Standard_Integer i1,i2,i3;
409 Handle(StlMesh_Mesh) ReadMesh;
411 thePath.SystemName (filename);
414 FILE* file = fopen(filename.ToCString(),"r");
416 fseek(file,0L,SEEK_END);
418 long filesize = ftell(file);
421 file = fopen(filename.ToCString(),"r");
423 // count the number of lines
424 for (ipos = 0; ipos < filesize; ++ipos) {
425 if (getc(file) == '\n')
429 // compute number of triangles
430 nbTris = (nbLines / ASCII_LINES_PER_FACET);
432 // go back to the beginning of the file
434 // file = fopen(filename.ToCString(),"r");
438 while (getc(file) != '\n');
440 cout << "start mesh\n";
442 ReadMesh = new StlMesh_Mesh();
443 ReadMesh->AddDomain();
446 Message_ProgressSentry aPS (theProgInd, "Triangles", 0, (nbTris - 1) * 1.0 / IND_THRESHOLD, 1);
447 for (iTri = 0; iTri < nbTris && aPS.More();)
449 char x[256]="", y[256]="", z[256]="";
451 // reading the facet normal
452 if (3 != fscanf(file,"%*s %*s %80s %80s %80s\n", x, y, z))
453 break; // error should be properly reported
454 gp_XYZ aN (Atof(x), Atof(y), Atof(z));
456 // skip the keywords "outer loop"
457 fscanf(file,"%*s %*s");
460 if (3 != fscanf(file,"%*s %80s %80s %80s\n", x, y, z))
461 break; // error should be properly reported
462 gp_XYZ aV1 (Atof(x), Atof(y), Atof(z));
463 if (3 != fscanf(file,"%*s %80s %80s %80s\n", x, y, z))
464 break; // error should be properly reported
465 gp_XYZ aV2 (Atof(x), Atof(y), Atof(z));
466 if (3 != fscanf(file,"%*s %80s %80s %80s\n", x, y, z))
467 break; // error should be properly reported
468 gp_XYZ aV3 (Atof(x), Atof(y), Atof(z));
470 // here the facet must be built and put in the mesh datastructure
472 i1 = ReadMesh->AddOnlyNewVertex (aV1.X(), aV1.Y(), aV1.Z());
473 i2 = ReadMesh->AddOnlyNewVertex (aV2.X(), aV2.Y(), aV2.Z());
474 i3 = ReadMesh->AddOnlyNewVertex (aV3.X(), aV3.Y(), aV3.Z());
475 ReadMesh->AddTriangle (i1, i2, i3, aN.X(), aN.Y(), aN.Z());
477 // skip the keywords "endloop"
480 // skip the keywords "endfacet"
483 // update progress only per 1k triangles
484 if (++iTri % IND_THRESHOLD == 0)
488 cout << "end mesh\n";