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 "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.hasExt("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(IOstream::UNCOMPRESSED == isPtr.whichCompression());
96  auto& is = *isPtr;
97 
98  if (!is.good())
99  {
101  << "Cannot read file " << filename
102  << " or file " << filename + ".gz"
103  << exit(FatalError);
104  }
105 
106  // Read the STL header
107  char header[STLHeaderSize];
108  is.read(header, STLHeaderSize);
109 
110  // If the stream is bad, it can't be a binary STL
111  if (!is.good() || startsWithSolid(header))
112  {
113  return 0;
114  }
115 
116 
117  // Read the number of triangles in the STL file
118  // (note: read as signed so we can check whether >2^31)
119  int32_t nTris;
120  is.read(reinterpret_cast<char*>(&nTris), sizeof(int32_t));
121 
122  // Check that stream is OK and number of triangles is positive,
123  // if not this may be an ASCII file
124 
125  bool bad = (!is || nTris < 0);
126 
127  if (!bad && unCompressed)
128  {
129  // Compare file size with that expected from number of tris
130  // If this is not sensible, it may be an ASCII file
131 
132  const off_t dataFileSize = Foam::fileSize(filename);
133 
134  bad =
135  (
136  nTris < int(dataFileSize - STLHeaderSize)/50
137  || nTris > int(dataFileSize - STLHeaderSize)/25
138  );
139  }
140 
141  // Return number of triangles if it appears to be BINARY and good.
142  return (bad ? 0 : nTris);
143 }
144 
145 
146 std::unique_ptr<std::istream>
148 (
149  const fileName& filename,
150  label& nTrisEstimated
151 )
152 {
153  nTrisEstimated = 0;
154 
155  std::unique_ptr<std::istream> streamPtr;
156  bool unCompressed(true);
157 
158  // Handle compressed (.gz) or uncompressed input files
159  {
160  ifstreamPointer isPtr(filename);
161  unCompressed = (IOstream::UNCOMPRESSED == isPtr.whichCompression());
162 
163  // Take ownership
164  streamPtr.reset(isPtr.release());
165  }
166  auto& is = *streamPtr;
167 
168  if (!is.good())
169  {
171  << "Cannot read file " << filename
172  << " or file " << filename + ".gz"
173  << exit(FatalError);
174  }
175 
176 
177  // Read the STL header
178  char header[STLHeaderSize];
179  is.read(header, STLHeaderSize);
180 
181  // Check that stream is OK, if not this may be an ASCII file
182  if (!is.good()) // could check again: startsWithSolid(header)
183  {
185  << "problem reading header, perhaps file is not binary "
186  << exit(FatalError);
187  }
188 
189  // Read the number of triangles in the STl file
190  // (note: read as int so we can check whether >2^31)
191  int32_t nTris;
192  is.read(reinterpret_cast<char*>(&nTris), sizeof(int32_t));
193 
194  // Check that stream is OK and number of triangles is positive,
195  // if not this maybe an ASCII file
196 
197  bool bad = (!is || nTris < 0);
198 
199  if (!bad && unCompressed)
200  {
201  // Compare file size with that expected from number of tris
202  // If this is not sensible, it may be an ASCII file
203 
204  const off_t dataFileSize = Foam::fileSize(filename);
205 
206  bad =
207  (
208  nTris < int(dataFileSize - STLHeaderSize)/50
209  || nTris > int(dataFileSize - STLHeaderSize)/25
210  );
211  }
212 
213  if (bad)
214  {
216  << "problem reading number of triangles, perhaps file is not binary"
217  << exit(FatalError);
218  }
219 
220  nTrisEstimated = nTris;
221 
222  return streamPtr;
223 }
224 
225 
227 (
228  ostream& os,
229  uint32_t nTris
230 )
231 {
232  // STL header with extra information about nTris
233  char header[STLHeaderSize];
234  sprintf(header, "STL binary file %u facets", nTris);
235 
236  // avoid trailing junk
237  for (size_t i = strlen(header); i < STLHeaderSize; ++i)
238  {
239  header[i] = 0;
240  }
241 
242  os.write(header, STLHeaderSize);
243  os.write(reinterpret_cast<char*>(&nTris), sizeof(uint32_t));
244 }
245 
246 
247 // ************************************************************************* //
Foam::fileFormats::STLCore::isBinaryName
static bool isBinaryName(const fileName &filename, const STLFormat format)
Detect 'stlb' extension as binary when format = UNKNOWN.
Definition: STLCore.C:67
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:73
Foam::isspace
bool isspace(char c) noexcept
Test for whitespace (C-locale)
Definition: char.H:65
Foam::ifstreamPointer::release
std::istream * release() noexcept
Return managed pointer and release ownership.
Definition: fstreamPointer.H:135
STLCore.H
Foam::ifstreamPointer::whichCompression
IOstreamOption::compressionType whichCompression() const
Which compression type?
Definition: fstreamPointers.C:243
format
word format(conversionProperties.get< word >("format"))
STLHeaderSize
static constexpr const unsigned STLHeaderSize
Definition: STLCore.C:36
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::fileName::hasExt
bool hasExt() const
Various checks for extensions.
Definition: stringI.H:56
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:227
startsWithSolid
static bool startsWithSolid(const char header[STLHeaderSize])
Definition: STLCore.C:44
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:148
os
OBJstream os(runTime.globalPath()/outputName)
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:453
Foam::fileFormats::STLCore::detectBinaryHeader
static int detectBinaryHeader(const fileName &filename)
Check contents to detect if the file is a binary STL.
Definition: STLCore.C:88
Foam::ifstreamPointer
A wrapped std::ifstream with possible compression handling (igzstream) that behaves much like a std::...
Definition: fstreamPointer.H:71
Foam::pos
dimensionedScalar pos(const dimensionedScalar &ds)
Definition: dimensionedScalar.C:177