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-2023 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 "OSspecific.H"
31 #include "IFstream.H"
32 
33 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
34 
35 // The number of bytes in the STL binary header
36 static constexpr const unsigned STLHeaderSize = 80;
37 
38 
39 // * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
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 }
62 
63 
64 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
65 
67 (
68  const fileName& filename,
69  const STLFormat format
70 )
71 {
72  return
73  (
74  format == STLFormat::UNKNOWN
75  ? filename.has_ext("stlb")
76  : format == STLFormat::BINARY
77  );
78 }
79 
80 
81 // Check binary by getting the header and number of facets
82 // this seems to work better than the old token-based method
83 // - using wordToken can cause an abort if non-word (binary) content
84 // is detected ... this is not exactly what we want.
85 // - some programs (eg, PROSTAR) have 'solid' as the first word in
86 // the binary header. This is just wrong and not our fault.
88 (
89  const fileName& filename
90 )
91 {
92  // Handle compressed (.gz) or uncompressed input files
93 
94  ifstreamPointer isPtr(filename);
95  const bool unCompressed =
97 
98  auto& is = *isPtr;
99 
100  if (!is.good())
101  {
103  << "Cannot read file " << filename
104  << " or file " << filename + ".gz"
105  << exit(FatalError);
106  }
107 
108  // Read the STL header
109  char header[STLHeaderSize];
110  is.read(header, STLHeaderSize);
111 
112  // If the stream is bad, it can't be a binary STL
113  if (!is.good() || startsWithSolid(header))
114  {
115  return 0;
116  }
117 
118 
119  // Read the number of triangles in the STL file
120  // (note: read as signed so we can check whether >2^31)
121  int32_t nTris;
122  is.read(reinterpret_cast<char*>(&nTris), sizeof(int32_t));
123 
124  // Check that stream is OK and number of triangles is positive,
125  // if not this may be an ASCII file
126 
127  bool bad = (!is || nTris < 0);
128 
129  if (!bad && unCompressed)
130  {
131  // Compare file size with that expected from number of tris
132  // If this is not sensible, it may be an ASCII file
133 
134  const off_t dataFileSize = Foam::fileSize(filename);
135 
136  bad =
137  (
138  nTris < int(dataFileSize - STLHeaderSize)/50
139  || nTris > int(dataFileSize - STLHeaderSize)/25
140  );
141  }
142 
143  // Return number of triangles if it appears to be BINARY and good.
144  return (bad ? 0 : nTris);
145 }
146 
147 
148 std::unique_ptr<std::istream>
150 (
151  const fileName& filename,
152  label& nTrisEstimated
153 )
154 {
155  nTrisEstimated = 0;
156 
157  std::unique_ptr<std::istream> streamPtr;
158  bool unCompressed(true);
159 
160  // Handle compressed (.gz) or uncompressed input files
161  {
162  ifstreamPointer isPtr(filename);
163  unCompressed =
165 
166  // Take ownership
167  streamPtr.reset(isPtr.release());
168  }
169  auto& is = *streamPtr;
170 
171  if (!is.good())
172  {
174  << "Cannot read file " << filename
175  << " or file " << filename + ".gz"
176  << exit(FatalError);
177  }
178 
179 
180  // Read the STL header
181  char header[STLHeaderSize];
182  is.read(header, STLHeaderSize);
183 
184  // Check that stream is OK, if not this may be an ASCII file
185  if (!is.good()) // could check again: startsWithSolid(header)
186  {
188  << "problem reading header, perhaps file is not binary "
189  << exit(FatalError);
190  }
191 
192  // Read the number of triangles in the STl file
193  // (note: read as int so we can check whether >2^31)
194  int32_t nTris;
195  is.read(reinterpret_cast<char*>(&nTris), sizeof(int32_t));
196 
197  // Check that stream is OK and number of triangles is positive,
198  // if not this maybe an ASCII file
199 
200  bool bad = (!is || nTris < 0);
201 
202  if (!bad && unCompressed)
203  {
204  // Compare file size with that expected from number of tris
205  // If this is not sensible, it may be an ASCII file
206 
207  const off_t dataFileSize = Foam::fileSize(filename);
208 
209  bad =
210  (
211  nTris < int(dataFileSize - STLHeaderSize)/50
212  || nTris > int(dataFileSize - STLHeaderSize)/25
213  );
214  }
215 
216  if (bad)
217  {
219  << "problem reading number of triangles, perhaps file is not binary"
220  << exit(FatalError);
221  }
222 
223  nTrisEstimated = nTris;
224 
225  return streamPtr;
226 }
227 
228 
230 (
231  ostream& os,
232  uint32_t nTris
233 )
234 {
235  // STL header with extra information about nTris
236  char header[STLHeaderSize];
237  ::snprintf(header, STLHeaderSize, "STL binary file %u facets", nTris);
238 
239  // Fill trailing with zeroes (to avoid writing junk)
240  for (size_t i = strlen(header); i < STLHeaderSize; ++i)
241  {
242  header[i] = 0;
243  }
244 
245  os.write(header, STLHeaderSize);
246  os.write(reinterpret_cast<char*>(&nTris), sizeof(uint32_t));
247 }
248 
249 
250 // ************************************************************************* //
A wrapped std::ifstream with possible compression handling (igzstream) that behaves much like a std::...
A class for handling file names.
Definition: fileName.H:72
off_t fileSize(const fileName &name, const bool followLink=true)
Return size of file or -1 on failure (normally follows symbolic links).
Definition: POSIX.C:905
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:125
error FatalError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL ERROR&#39; header text and sta...
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:598
IOstreamOption::compressionType whichCompression() const
Which compression type?
static void writeBinaryHeader(ostream &os, uint32_t nTris)
Write STL binary file and number of triangles to stream.
Definition: STLCore.C:223
std::istream * release() noexcept
Return managed pointer and release ownership.
Functions used by OpenFOAM that are specific to POSIX compliant operating systems and need to be repl...
dimensionedScalar pos(const dimensionedScalar &ds)
static bool isBinaryName(const fileName &filename, const STLFormat format)
Detect &#39;stlb&#39; extension as binary when format = UNKNOWN.
Definition: STLCore.C:60
static constexpr const unsigned STLHeaderSize
Definition: STLCore.C:29
static bool startsWithSolid(const char header[STLHeaderSize])
Definition: STLCore.C:37
OBJstream os(runTime.globalPath()/outputName)
word format(conversionProperties.get< word >("format"))
bool isspace(char c) noexcept
Test for whitespace (C-locale)
Definition: char.H:69
bool has_ext() const
Various checks for extensions.
Definition: stringI.H:43
static std::unique_ptr< std::istream > readBinaryHeader(const fileName &filename, label &nTrisEstimated)
Read STL binary file header.
Definition: STLCore.C:143
static int detectBinaryHeader(const fileName &filename)
Check contents to detect if the file is a binary STL.
Definition: STLCore.C:81