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