STLCore.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration |
5  \\ / A nd | www.openfoam.com
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8  Copyright (C) 2011-2016 OpenFOAM Foundation
9  Copyright (C) 2016-2020 OpenCFD Ltd.
10 -------------------------------------------------------------------------------
11 License
12  This file is part of OpenFOAM.
13 
14  OpenFOAM is free software: you can redistribute it and/or modify it
15  under the terms of the GNU General Public License as published by
16  the Free Software Foundation, either version 3 of the License, or
17  (at your option) any later version.
18 
19  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
20  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22  for more details.
23 
24  You should have received a copy of the GNU General Public License
25  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
26 
27 \*---------------------------------------------------------------------------*/
28 
29 #include "STLCore.H"
30 #include "gzstream.h"
31 #include "OSspecific.H"
32 #include "IFstream.H"
33 
34 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
35 
37 
38 // The number of bytes in the STL binary header
39 static constexpr const unsigned STLHeaderSize = 80;
40 
41 // Check if "SOLID" or "solid" appears as the first non-space content.
42 // Assume that any leading space is less than 75 chars or so, otherwise
43 // it is really bad input.
44 static bool startsWithSolid(const char header[STLHeaderSize])
45 {
46  unsigned pos = 0;
47  while (std::isspace(header[pos]) && pos < STLHeaderSize)
48  {
49  ++pos;
50  }
51 
52  return
53  (
54  pos < (STLHeaderSize-5) // At least 5 chars remaining
55  && std::toupper(header[pos+0]) == 'S'
56  && std::toupper(header[pos+1]) == 'O'
57  && std::toupper(header[pos+2]) == 'L'
58  && std::toupper(header[pos+3]) == 'I'
59  && std::toupper(header[pos+4]) == 'D'
60  );
61 }
63 
64 
65 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
66 
68 (
69  const fileName& filename,
70  const STLFormat format
71 )
72 {
73  return
74  (
75  format == STLFormat::UNKNOWN
76  ? (filename.ext() == "stlb")
77  : format == STLFormat::BINARY
78  );
79 }
80 
81 
82 // Check binary by getting the header and number of facets
83 // this seems to work better than the old token-based method
84 // - using wordToken can cause an abort if non-word (binary) content
85 // is detected ... this is not exactly what we want.
86 // - some programs (eg, PROSTAR) have 'solid' as the first word in
87 // the binary header. This is just wrong and not our fault.
89 (
90  const fileName& filename
91 )
92 {
93  bool compressed = false;
94  std::unique_ptr<std::istream> streamPtr
95  {
96  new std::ifstream(filename, std::ios::binary)
97  };
98 
99  // If the file is compressed, decompress it before further checking.
100  if (!streamPtr->good() && isFile(filename + ".gz", false))
101  {
102  compressed = true;
103  streamPtr.reset(new igzstream((filename + ".gz").c_str()));
104  }
105  auto& is = *streamPtr;
106 
107  if (!is.good())
108  {
110  << "Cannot read file " << filename
111  << " or file " << filename + ".gz"
112  << exit(FatalError);
113  }
114 
115  // Read the STL header
116  char header[STLHeaderSize];
117  is.read(header, STLHeaderSize);
118 
119  // If the stream is bad, it can't be a binary STL
120  if (!is.good() || startsWithSolid(header))
121  {
122  return 0;
123  }
124 
125 
126  // Read the number of triangles in the STL file
127  // (note: read as signed so we can check whether >2^31)
128  int32_t nTris;
129  is.read(reinterpret_cast<char*>(&nTris), sizeof(int32_t));
130 
131  // Check that stream is OK and number of triangles is positive,
132  // if not this may be an ASCII file
133  //
134  // Also compare the file size with that expected from the number of tris
135  // If the comparison is still not sensible then it may be an ASCII file
136  if (!is || nTris < 0)
137  {
138  return 0;
139  }
140  else if (!compressed)
141  {
142  const off_t dataFileSize = Foam::fileSize(filename);
143 
144  if
145  (
146  nTris < int(dataFileSize - STLHeaderSize)/50
147  || nTris > int(dataFileSize - STLHeaderSize)/25
148  )
149  {
150  return 0;
151  }
152  }
153 
154  // looks like it might be BINARY, return number of triangles
155  return nTris;
156 }
157 
158 
159 std::unique_ptr<std::istream>
161 (
162  const fileName& filename,
163  label& nTrisEstimated
164 )
165 {
166  bool bad = false;
167  bool compressed = false;
168  nTrisEstimated = 0;
169 
170  std::unique_ptr<std::istream> streamPtr
171  {
172  new std::ifstream(filename, std::ios::binary)
173  };
174 
175  // If the file is compressed, decompress it before reading.
176  if (!streamPtr->good() && isFile(filename + ".gz", false))
177  {
178  compressed = true;
179  streamPtr.reset(new igzstream((filename + ".gz").c_str()));
180  }
181  auto& is = *streamPtr;
182 
183  if (!is.good())
184  {
186  << "Cannot read file " << filename
187  << " or file " << filename + ".gz"
188  << exit(FatalError);
189  }
190 
191 
192  // Read the STL header
193  char header[STLHeaderSize];
194  is.read(header, STLHeaderSize);
195 
196  // Check that stream is OK, if not this may be an ASCII file
197  if (!is.good()) // could check again: startsWithSolid(header)
198  {
200  << "problem reading header, perhaps file is not binary "
201  << exit(FatalError);
202  }
203 
204  // Read the number of triangles in the STl file
205  // (note: read as int so we can check whether >2^31)
206  int32_t nTris;
207  is.read(reinterpret_cast<char*>(&nTris), sizeof(int32_t));
208 
209  // Check that stream is OK and number of triangles is positive,
210  // if not this maybe an ASCII file
211  //
212  // Also compare the file size with that expected from the number of tris
213  // If the comparison is not sensible then it may be an ASCII file
214  if (!is || nTris < 0)
215  {
216  bad = true;
217  }
218  else if (!compressed)
219  {
220  const off_t dataFileSize = Foam::fileSize(filename);
221 
222  if
223  (
224  nTris < int(dataFileSize - STLHeaderSize)/50
225  || nTris > int(dataFileSize - STLHeaderSize)/25
226  )
227  {
228  bad = true;
229  }
230  }
231 
232  if (bad)
233  {
235  << "problem reading number of triangles, perhaps file is not binary"
236  << exit(FatalError);
237  }
238 
239  nTrisEstimated = nTris;
240  return streamPtr;
241 }
242 
243 
245 (
246  ostream& os,
247  uint32_t nTris
248 )
249 {
250  // STL header with extra information about nTris
251  char header[STLHeaderSize];
252  sprintf(header, "STL binary file %u facets", nTris);
253 
254  // avoid trailing junk
255  for (size_t i = strlen(header); i < STLHeaderSize; ++i)
256  {
257  header[i] = 0;
258  }
259 
260  os.write(header, STLHeaderSize);
261  os.write(reinterpret_cast<char*>(&nTris), sizeof(uint32_t));
262 }
263 
264 
265 // ************************************************************************* //
Foam::fileFormats::STLCore::isBinaryName
static bool isBinaryName(const fileName &filename, const STLFormat format)
Detect 'stlb' extension as binary when format = UNKNOWN.
Definition: STLCore.C:68
OSspecific.H
Functions used by OpenFOAM that are specific to POSIX compliant operating systems and need to be repl...
Foam::fileName
A class for handling file names.
Definition: fileName.H:69
Foam::isFile
bool isFile(const fileName &name, const bool checkGzip=true, const bool followLink=true)
Does the name exist as a FILE in the file system?
Definition: MSwindows.C:658
STLCore.H
format
word format(conversionProperties.get< word >("format"))
Foam::fileSize
off_t fileSize(const fileName &name, const bool followLink=true)
Return size of file or -1 on failure (normally follows symbolic links).
Definition: MSwindows.C:676
Foam::fileFormats::STLCore::writeBinaryHeader
static void writeBinaryHeader(ostream &os, uint32_t nTris)
Write STL binary file and number of triangles to stream.
Definition: STLCore.C:245
IFstream.H
Foam::FatalError
error FatalError
Foam::fileFormats::STLCore::readBinaryHeader
static std::unique_ptr< std::istream > readBinaryHeader(const fileName &filename, label &nTrisEstimated)
Read STL binary file header.
Definition: STLCore.C:161
Foam::fileName::ext
word ext() const
Return file name extension (part after last .)
Definition: fileNameI.H:228
Foam::exit
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:130
Foam::fileFormats::STLCore::STLFormat
STLFormat
Enumeration for the format of data in the stream.
Definition: STLCore.H:61
FatalErrorInFunction
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:372
Foam::fileFormats::STLCore::detectBinaryHeader
static int detectBinaryHeader(const fileName &filename)
Check contents to detect if the file is a binary STL.
Definition: STLCore.C:89
Foam::isspace
bool isspace(char c)
Test for horizontal whitespace.
Definition: char.H:63
Foam::pos
dimensionedScalar pos(const dimensionedScalar &ds)
Definition: dimensionedScalar.C:177