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