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-2017 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  autoPtr<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  std::istream& 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 
161 (
162  const fileName& filename,
163  label& nTrisEstimated
164 )
165 {
166  bool bad = false;
167  bool compressed = false;
168  nTrisEstimated = 0;
169 
170  autoPtr<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  std::istream& is = streamPtr();
182 
183  if (!is.good())
184  {
185  streamPtr.clear();
186 
188  << "Cannot read file " << filename
189  << " or file " << filename + ".gz"
190  << exit(FatalError);
191  }
192 
193 
194  // Read the STL header
195  char header[STLHeaderSize];
196  is.read(header, STLHeaderSize);
197 
198  // Check that stream is OK, if not this may be an ASCII file
199  if (!is.good()) // could check again: startsWithSolid(header)
200  {
201  streamPtr.clear();
202 
204  << "problem reading header, perhaps file is not binary "
205  << exit(FatalError);
206  }
207 
208  // Read the number of triangles in the STl file
209  // (note: read as int so we can check whether >2^31)
210  int32_t nTris;
211  is.read(reinterpret_cast<char*>(&nTris), sizeof(int32_t));
212 
213  // Check that stream is OK and number of triangles is positive,
214  // if not this maybe an ASCII file
215  //
216  // Also compare the file size with that expected from the number of tris
217  // If the comparison is not sensible then it may be an ASCII file
218  if (!is || nTris < 0)
219  {
220  bad = true;
221  }
222  else if (!compressed)
223  {
224  const off_t dataFileSize = Foam::fileSize(filename);
225 
226  if
227  (
228  nTris < int(dataFileSize - STLHeaderSize)/50
229  || nTris > int(dataFileSize - STLHeaderSize)/25
230  )
231  {
232  bad = true;
233  }
234  }
235 
236  if (bad)
237  {
238  streamPtr.clear();
239 
241  << "problem reading number of triangles, perhaps file is not binary"
242  << exit(FatalError);
243  }
244 
245  nTrisEstimated = nTris;
246  return streamPtr;
247 }
248 
249 
251 (
252  ostream& os,
253  uint32_t nTris
254 )
255 {
256  // STL header with extra information about nTris
257  char header[STLHeaderSize];
258  sprintf(header, "STL binary file %u facets", nTris);
259 
260  // avoid trailing junk
261  for (size_t i = strlen(header); i < STLHeaderSize; ++i)
262  {
263  header[i] = 0;
264  }
265 
266  os.write(header, STLHeaderSize);
267  os.write(reinterpret_cast<char*>(&nTris), sizeof(uint32_t));
268 }
269 
270 
271 // ************************************************************************* //
Foam::autoPtr::reset
void reset(T *p=nullptr) noexcept
Delete managed object and set to new given pointer.
Definition: autoPtrI.H:158
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::label
intWM_LABEL_SIZE_t label
A label is an int32_t or int64_t as specified by the pre-processor macro WM_LABEL_SIZE.
Definition: label.H:62
Foam::fileFormats::STLCore::readBinaryHeader
static autoPtr< std::istream > readBinaryHeader(const fileName &filename, label &nTrisEstimated)
Read STL binary file header.
Definition: STLCore.C:161
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:251
IFstream.H
Foam::FatalError
error FatalError
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::autoPtr
Pointer management similar to std::unique_ptr, with some additional methods and type checking.
Definition: HashPtrTable.H:53
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:355
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::autoPtr::clear
void clear() noexcept
Delete managed object and set pointer to nullptr.
Definition: autoPtrI.H:151
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