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 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.
14 // Alternatively, this file may be used under the terms of Open CASCADE
15 // commercial license or contractual agreement.
18 #include <BRepBuilderAPI_CellFilter.hxx>
19 #include <BRepBuilderAPI_VertexInspector.hxx>
23 #include <Message_ProgressIndicator.hxx>
24 #include <Message_ProgressSentry.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>
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>
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,
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)
54 index = indices.First(); // it should be only one
55 inspector.ClearResList();
59 index = mesh->AddVertex(p.X(), p.Y(), p.Z());
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;
72 static const int IND_THRESHOLD = 1000; // increment the indicator every 1k triangles
74 //=======================================================================
75 //function : WriteInteger
76 //purpose : writing a Little Endian 32 bits integer
77 //=======================================================================
79 inline static void WriteInteger(OSD_File& ofile,const Standard_Integer value)
82 Standard_Integer i;// don't be afraid, this is just an unsigned int
88 Standard_Integer entier;
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;
95 ofile.Write((char *)&entier,sizeof(bidargum.c));
98 //=======================================================================
99 //function : WriteDouble2Float
100 //purpose : writing a Little Endian 32 bits float
101 //=======================================================================
103 inline static void WriteDouble2Float(OSD_File& ofile,Standard_Real value)
106 Standard_ShortReal f;
110 bidargum.f = (Standard_ShortReal)value;
112 Standard_Integer entier;
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;
119 ofile.Write((char *)&entier,sizeof(bidargum.c));
123 //=======================================================================
124 //function : readFloat2Double
125 //purpose : reading a Little Endian 32 bits float
126 //=======================================================================
128 inline static Standard_Real ReadFloat2Double(OSD_File &aFile)
131 Standard_Boolean i; // don't be afraid, this is just an unsigned int
132 Standard_ShortReal f;
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;
145 return (Standard_Real)(bidargum.f);
150 //=======================================================================
151 //function : WriteBinary
152 //purpose : write a binary STL file in Little Endian format
153 //=======================================================================
155 Standard_Boolean RWStl::WriteBinary (const Handle(StlMesh_Mesh)& theMesh,
156 const OSD_Path& thePath,
157 const Handle(Message_ProgressIndicator)& theProgInd)
159 OSD_File aFile (thePath);
160 aFile.Build (OSD_WriteOnly, OSD_Protection());
162 Standard_Real x1, y1, z1;
163 Standard_Real x2, y2, z2;
164 Standard_Real x3, y3, z3;
166 // writing 80 bytes of the trash?
168 aFile.Write ((Standard_Address)sval,80);
169 WriteInteger (aFile, theMesh->NbTriangles());
172 StlMesh_MeshExplorer aMexp (theMesh);
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())
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())
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())
193 Vnorm.Divide(Vmodul);
197 // si Vnorm est quasi-nul, on le charge a 0 explicitement
198 Vnorm.SetCoord (0., 0., 0.);
201 WriteDouble2Float (aFile, Vnorm.X());
202 WriteDouble2Float (aFile, Vnorm.Y());
203 WriteDouble2Float (aFile, Vnorm.Z());
205 WriteDouble2Float (aFile, x1);
206 WriteDouble2Float (aFile, y1);
207 WriteDouble2Float (aFile, z1);
209 WriteDouble2Float (aFile, x2);
210 WriteDouble2Float (aFile, y2);
211 WriteDouble2Float (aFile, z2);
213 WriteDouble2Float (aFile, x3);
214 WriteDouble2Float (aFile, y3);
215 WriteDouble2Float (aFile, z3);
217 aFile.Write (&dum, 2);
219 // update progress only per 1k triangles
220 if (++aTriangleInd % IND_THRESHOLD == 0)
229 Standard_Boolean isInterrupted = !aDPS.More();
230 return !isInterrupted;
232 //=======================================================================
233 //function : WriteAscii
234 //purpose : write an ASCII STL file
235 //=======================================================================
237 Standard_Boolean RWStl::WriteAscii (const Handle(StlMesh_Mesh)& theMesh,
238 const OSD_Path& thePath,
239 const Handle(Message_ProgressIndicator)& theProgInd)
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();
246 Standard_Real x1, y1, z1;
247 Standard_Real x2, y2, z2;
248 Standard_Real x3, y3, z3;
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())
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())
263 aMexp.TriangleVertices (x1,y1,z1,x2,y2,z2,x3,y3,z3);
265 // Standard_Real x, y, z;
266 // aMexp.TriangleOrientation (x,y,z);
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())
274 Vnorm.Divide (Vmodul);
278 // si Vnorm est quasi-nul, on le charge a 0 explicitement
279 Vnorm.SetCoord (0., 0., 0.);
282 " facet normal % 12e % 12e % 12e\n"
284 " vertex % 12e % 12e % 12e\n"
285 " vertex % 12e % 12e % 12e\n"
286 " vertex % 12e % 12e % 12e\n"
289 Vnorm.X(), Vnorm.Y(), Vnorm.Z(),
294 theFile.Write (buf, buf.Length()); buf.Clear();
296 // update progress only per 1k triangles
297 if (++aTriangleInd % IND_THRESHOLD == 0)
307 theFile.Write (buf, buf.Length()); buf.Clear();
309 Standard_Boolean isInterrupted = !aDPS.More();
310 return !isInterrupted;
312 //=======================================================================
313 //function : ReadFile
316 //=======================================================================
318 Handle(StlMesh_Mesh) RWStl::ReadFile (const OSD_Path& thePath,
319 const Handle(Message_ProgressIndicator)& theProgInd)
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;
329 // we skip the header which is in Ascii for both modes
330 file.Read(ach,HEADER_SIZE,lread);
332 // we read 128 characters to detect if we have a non-ascii char
333 file.Read(ach,sizeof(str),lread);
335 IsAscii = Standard_True;
336 for (i = 0; i< lread && IsAscii; ++i) {
338 IsAscii = Standard_False;
342 cout << (IsAscii ? "ascii\n" : "binary\n");
346 return IsAscii ? RWStl::ReadAscii (thePath, theProgInd)
347 : RWStl::ReadBinary (thePath, theProgInd);
350 //=======================================================================
351 //function : ReadBinary
354 //=======================================================================
356 Handle(StlMesh_Mesh) RWStl::ReadBinary (const OSD_Path& thePath,
357 const Handle(Message_ProgressIndicator)& /*theProgInd*/)
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;
364 Standard_Address adr;
365 adr = (Standard_Address)buftest;
368 OSD_File theFile (thePath);
369 theFile.Open(OSD_ReadOnly,OSD_Protection(OSD_RWD,OSD_RWD,OSD_RWD,OSD_RWD));
371 // the size of the file (minus the header size)
372 // must be a multiple of SIZEOF_STL_FACET
375 Standard_Size filesize = theFile.Size();
377 if ( (filesize - HEADER_SIZE) % SIZEOF_STL_FACET !=0
378 || (filesize < STL_MIN_FILE_SIZE)) {
379 Standard_NoMoreObject::Raise("RWStl::ReadBinary (wrong file size)");
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);
387 theFile.Seek(HEADER_SIZE,OSD_FromBeginning);
389 // create the StlMesh_Mesh object
390 Handle(StlMesh_Mesh) ReadMesh = new StlMesh_Mesh ();
391 ReadMesh->AddDomain ();
393 // Filter unique vertices to share the nodes of the mesh.
394 BRepBuilderAPI_CellFilter uniqueVertices(Precision::Confusion());
395 BRepBuilderAPI_VertexInspector inspector(Precision::Confusion());
397 for (ifacet=1; ifacet<=NBFACET; ++ifacet) {
398 // read normal coordinates
399 fx = ReadFloat2Double(theFile);
400 fy = ReadFloat2Double(theFile);
401 fz = ReadFloat2Double(theFile);
404 fx1 = ReadFloat2Double(theFile);
405 fy1 = ReadFloat2Double(theFile);
406 fz1 = ReadFloat2Double(theFile);
409 fx2 = ReadFloat2Double(theFile);
410 fy2 = ReadFloat2Double(theFile);
411 fz2 = ReadFloat2Double(theFile);
414 fx3 = ReadFloat2Double(theFile);
415 fy3 = ReadFloat2Double(theFile);
416 fz3 = ReadFloat2Double(theFile);
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));
424 ReadMesh->AddTriangle (i1,i2,i3,fx,fy,fz);
427 theFile.Read(adr,2,lread);
434 //=======================================================================
435 //function : ReadAscii
438 //=======================================================================
440 Handle(StlMesh_Mesh) RWStl::ReadAscii (const OSD_Path& thePath,
441 const Handle(Message_ProgressIndicator)& theProgInd)
443 TCollection_AsciiString filename;
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;
451 thePath.SystemName (filename);
454 FILE* file = OSD_OpenFile(filename.ToCString(),"r");
456 fseek(file,0L,SEEK_END);
458 long filesize = ftell(file);
462 // count the number of lines
463 for (ipos = 0; ipos < filesize; ++ipos) {
464 if (getc(file) == '\n')
468 // compute number of triangles
469 nbTris = (nbLines / ASCII_LINES_PER_FACET);
471 // go back to the beginning of the file
475 while (getc(file) != '\n');
477 cout << "start mesh\n";
479 ReadMesh = new StlMesh_Mesh();
480 ReadMesh->AddDomain();
482 // Filter unique vertices to share the nodes of the mesh.
483 BRepBuilderAPI_CellFilter uniqueVertices(Precision::Confusion());
484 BRepBuilderAPI_VertexInspector inspector(Precision::Confusion());
487 Message_ProgressSentry aPS (theProgInd, "Triangles", 0, (nbTris - 1) * 1.0 / IND_THRESHOLD, 1);
488 for (iTri = 0; iTri < nbTris && aPS.More();)
490 char x[256]="", y[256]="", z[256]="";
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));
497 // skip the keywords "outer loop"
498 fscanf(file,"%*s %*s");
501 if (3 != fscanf(file,"%*s %80s %80s %80s\n", x, y, z))
502 break; // error should be properly reported
503 gp_XYZ aV1 (Atof(x), Atof(y), Atof(z));
504 if (3 != fscanf(file,"%*s %80s %80s %80s\n", x, y, z))
505 break; // error should be properly reported
506 gp_XYZ aV2 (Atof(x), Atof(y), Atof(z));
507 if (3 != fscanf(file,"%*s %80s %80s %80s\n", x, y, z))
508 break; // error should be properly reported
509 gp_XYZ aV3 (Atof(x), Atof(y), Atof(z));
511 // here the facet must be built and put in the mesh datastructure
513 i1 = AddVertex(ReadMesh, uniqueVertices, inspector, aV1);
514 i2 = AddVertex(ReadMesh, uniqueVertices, inspector, aV2);
515 i3 = AddVertex(ReadMesh, uniqueVertices, inspector, aV3);
516 ReadMesh->AddTriangle (i1, i2, i3, aN.X(), aN.Y(), aN.Z());
518 // skip the keywords "endloop"
521 // skip the keywords "endfacet"
524 // update progress only per 1k triangles
525 if (++iTri % IND_THRESHOLD == 0)
529 cout << "end mesh\n";