Salome HOME
Merge branch 'master' into gni/evolution
[modules/smesh.git] / src / DriverSTL / DriverSTL_R_SMDS_Mesh.cxx
1 // Copyright (C) 2007-2021  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22
23 #include "DriverSTL_R_SMDS_Mesh.h"
24
25 #include <Basics_Utils.hxx>
26
27 #include <gp_Pnt.hxx>
28 #include <NCollection_DataMap.hxx>
29 #include <Standard_NoMoreObject.hxx>
30
31 #include "SMDS_Mesh.hxx"
32 #include "SMDS_MeshElement.hxx"
33 #include "SMDS_MeshNode.hxx"
34 #include "SMESH_File.hxx"
35
36 namespace
37 {
38   struct Hasher
39   {
40     //=======================================================================
41     //function : HashCode
42     //purpose  :
43     //=======================================================================
44     inline static Standard_Integer HashCode
45     (const gp_Pnt& point,  Standard_Integer Upper)
46     {
47       union
48       {
49         Standard_Real    R[3];
50         Standard_Integer I[6];
51       } U;
52
53       point.Coord( U.R[0], U.R[1], U.R[2] );
54
55       return ::HashCode(U.I[0]/23+U.I[1]/19+U.I[2]/17+U.I[3]/13+U.I[4]/11+U.I[5]/7,Upper);
56     }
57     //=======================================================================
58     //function : IsEqual
59     //purpose  :
60     //=======================================================================
61     inline static Standard_Boolean IsEqual
62     (const gp_Pnt& point1, const gp_Pnt& point2)
63     {
64       static Standard_Real tab1[3], tab2[3];
65       point1.Coord(tab1[0],tab1[1],tab1[2]);
66       point2.Coord(tab2[0],tab2[1],tab2[2]);
67       return (memcmp(tab1,tab2,sizeof(tab1)) == 0);
68     }
69   };
70   typedef NCollection_DataMap<gp_Pnt,SMDS_MeshNode*,Hasher> TDataMapOfPntNodePtr;
71
72   const int HEADER_SIZE           = 84; // 80 chars + int
73   const int SIZEOF_STL_FACET      = 50;
74   const int ASCII_LINES_PER_FACET = 7;
75   const int SIZE_OF_FLOAT         = 4;
76   // const int STL_MIN_FILE_SIZE     = 284;
77 }
78
79 //=======================================================================
80 //function : DriverSTL_R_SMDS_Mesh
81 //purpose  :
82 //=======================================================================
83
84 DriverSTL_R_SMDS_Mesh::DriverSTL_R_SMDS_Mesh()
85 {
86   myIsCreateFaces = true;
87   myIsAscii = Standard_True;
88 }
89
90 //=======================================================================
91 //function : SetIsCreateFaces
92 //purpose  : 
93 //=======================================================================
94
95 void DriverSTL_R_SMDS_Mesh::SetIsCreateFaces( const bool theIsCreate )
96 { myIsCreateFaces = theIsCreate; }
97
98 //=======================================================================
99 //function : Perform
100 //purpose  : 
101 //=======================================================================
102
103 Driver_Mesh::Status DriverSTL_R_SMDS_Mesh::Perform()
104 {
105   Kernel_Utils::Localizer loc;
106
107   Status aResult = DRS_OK;
108
109   if ( myFile.empty() ) {
110     fprintf(stderr, ">> ERREOR : invalid file name \n");
111     return DRS_FAIL;
112   }
113
114   SMESH_File file( myFile, /*open=*/false );
115   if ( !file.open() ) {
116     fprintf(stderr, ">> ERROR : cannot open file %s \n", myFile.c_str());
117     if ( file.error().empty() )
118       fprintf(stderr, ">> ERROR : %s \n", file.error().c_str());
119     return DRS_FAIL;
120   }
121
122   // we skip the header which is in Ascii for both modes
123   const char* data = file;
124   data += HEADER_SIZE;
125
126   // we check 128 characters to detect if we have a non-ascii char
127   myIsAscii = Standard_True;
128   for (int i = 0; i < 128; ++i, ++data) {
129     if ( !isascii( *data ) && data < file.end() ) {
130       myIsAscii = Standard_False;
131       break;
132     }
133   }
134
135   if ( !myMesh ) {
136     fprintf(stderr, ">> ERREOR : cannot create mesh \n");
137     return DRS_FAIL;
138   }
139
140   if ( myIsAscii )
141     aResult = readAscii( file );
142   else
143     aResult = readBinary( file );
144
145   myMesh->Modified();
146   myMesh->CompactMesh();
147
148   return aResult;
149 }
150
151 // static methods
152
153 static Standard_Real readFloat(SMESH_File& theFile)
154 {
155   union {
156     int   i;
157     float f;
158   } u;
159
160   const char* c = theFile;
161   u.i  = 0;
162   u.i  =  c[0] & 0xFF;
163   u.i |= (c[1] & 0xFF) << 0x08;
164   u.i |= (c[2] & 0xFF) << 0x10;
165   u.i |= (c[3] & 0xFF) << 0x18;
166   theFile += SIZE_OF_FLOAT;
167
168   return u.f;
169 }
170
171 static SMDS_MeshNode* addNode(const gp_Pnt& P,
172                               TDataMapOfPntNodePtr& uniqnodes,
173                               SMDS_Mesh* theMesh)
174 {
175   SMDS_MeshNode* node = 0;
176   if ( uniqnodes.IsBound(P) ) {
177     node = uniqnodes.Find(P);
178   } else {
179     node = theMesh->AddNode(P.X(), P.Y(), P.Z() );
180     uniqnodes.Bind(P,node);
181   }
182   
183   return node;
184 }                                
185
186 static SMDS_MeshNode* readNode(FILE* file,
187                                TDataMapOfPntNodePtr& uniqnodes,
188                                SMDS_Mesh* theMesh)
189 {
190   Standard_ShortReal coord[3];
191   // reading vertex
192   fscanf(file,"%*s %f %f %f\n",&coord[0],&coord[1],&coord[2]);
193
194   gp_Pnt P(coord[0],coord[1],coord[2]);
195   return addNode( P, uniqnodes, theMesh );
196 }
197
198 static SMDS_MeshNode* readNode(SMESH_File& theFile,
199                                TDataMapOfPntNodePtr& uniqnodes,
200                                SMDS_Mesh* theMesh)
201 {
202   gp_Pnt coord;
203   coord.SetX( readFloat(theFile));
204   coord.SetY( readFloat(theFile));
205   coord.SetZ( readFloat(theFile));
206
207   return addNode( coord, uniqnodes, theMesh );
208 }
209
210 //=======================================================================
211 //function : readAscii
212 //purpose  :
213 //=======================================================================
214
215 Driver_Mesh::Status DriverSTL_R_SMDS_Mesh::readAscii(SMESH_File& theFile) const
216 {
217   Status aResult = DRS_OK;
218
219   // get a solid name
220   if ( strncmp( "solid ", theFile, strlen("solid ")) == 0 ) // not empty
221   {
222     const char * header = theFile;
223     std::string& name = const_cast<std::string&>( myName );
224     for ( header += strlen("solid "); !iscntrl( *header ); ++header )
225       name.push_back( *header );
226
227     std::string::iterator i = name.begin();
228     while ( i != name.end() && isspace( *i )) ++i;
229     name.erase( name.begin(), i );
230
231     size_t n = name.size();
232     while ( n > 0 && isspace( name[ n - 1 ] )) --n;
233     name.resize( n );
234   }
235
236   // get the file size
237   long filesize = theFile.size();
238   theFile.close();
239
240   // Open the file
241 #if defined(WIN32) && defined(UNICODE)
242   std::wstring aFile = Kernel_Utils::utf8_decode_s(myFile);
243   FILE* file = _wfopen( aFile.c_str(), L"r");
244 #else
245   FILE* file = fopen(myFile.c_str(), "r");  
246 #endif  
247
248   // count the number of lines
249   Standard_Integer nbLines = 0;
250   for (long ipos = 0; ipos < filesize; ++ipos)
251   {
252     if (getc(file) == '\n')
253       nbLines++;
254   }
255
256   // go back to the beginning of the file
257   rewind(file);
258
259   Standard_Integer nbTri = (nbLines / ASCII_LINES_PER_FACET);
260
261   TDataMapOfPntNodePtr uniqnodes;
262   // skip header
263   while (getc(file) != '\n');
264
265   // main reading
266   for (Standard_Integer iTri = 0; iTri < nbTri; ++iTri)
267   {
268     // skipping the facet normal
269     Standard_ShortReal normal[3];
270     fscanf(file,"%*s %*s %f %f %f\n",&normal[0],&normal[1],&normal[2]);
271
272     // skip the keywords "outer loop"
273     fscanf(file,"%*s %*s");
274
275     // reading nodes
276     SMDS_MeshNode* node1 = readNode( file, uniqnodes, myMesh );
277     SMDS_MeshNode* node2 = readNode( file, uniqnodes, myMesh );
278     SMDS_MeshNode* node3 = readNode( file, uniqnodes, myMesh );
279
280     if (myIsCreateFaces)
281       myMesh->AddFace(node1,node2,node3);
282
283     // skip the keywords "endloop"
284     fscanf(file,"%*s");
285
286     // skip the keywords "endfacet"
287     fscanf(file,"%*s");
288   }
289
290   fclose(file);
291   return aResult;
292 }
293
294 //=======================================================================
295 //function : readBinary
296 //purpose  : 
297 //=======================================================================
298
299 Driver_Mesh::Status DriverSTL_R_SMDS_Mesh::readBinary(SMESH_File& file) const
300 {
301   Status aResult = DRS_OK;
302
303   // the size of the file (minus the header size)
304   // must be a multiple of SIZEOF_STL_FACET
305
306   long filesize = file.size();
307
308   if ( (filesize - HEADER_SIZE) % SIZEOF_STL_FACET != 0 
309       // Commented to allow reading small files (ex: 1 face)
310       /*|| (filesize < STL_MIN_FILE_SIZE)*/) {
311     Standard_NoMoreObject::Raise("DriverSTL_R_SMDS_MESH::readBinary (wrong file size)");
312   }
313
314   // don't trust the number of triangles which is coded in the file
315   // sometimes it is wrong, and with this technique we don't need to swap endians for integer
316   Standard_Integer nbTri = ((filesize - HEADER_SIZE) / SIZEOF_STL_FACET);
317
318   // get a solid name
319   if ( strncmp( "name: ", file, strlen("name: ")) == 0 ) // name present
320   {
321     const char * header = file;
322     std::string&   name = const_cast<std::string&>( myName );
323     header += strlen("name: ");
324     name.assign( header, HEADER_SIZE - strlen("name: ") - 4);
325
326     size_t n = name.size();
327     while ( n > 0 && isspace( name[ n - 1 ] )) --n;
328     name.resize( n );
329   }
330
331   // skip the header
332   file += HEADER_SIZE;
333
334   TDataMapOfPntNodePtr uniqnodes;
335   
336   for (Standard_Integer iTri = 0; iTri < nbTri; ++iTri) {
337
338     // ignore normals
339     file += 3 * SIZE_OF_FLOAT;
340
341     // read vertices
342     SMDS_MeshNode* node1 = readNode( file, uniqnodes, myMesh );
343     SMDS_MeshNode* node2 = readNode( file, uniqnodes, myMesh );
344     SMDS_MeshNode* node3 = readNode( file, uniqnodes, myMesh );
345
346     if (myIsCreateFaces)
347       myMesh->AddFace(node1,node2,node3);
348
349     // skip extra bytes
350     file += 2;
351   }
352   return aResult;
353 }