19 #include "libmesh/stl_io.h" 21 #include "libmesh/distributed_mesh.h" 22 #include "libmesh/enum_elem_type.h" 23 #include "libmesh/face_tri3.h" 24 #include "libmesh/libmesh_config.h" 25 #include "libmesh/libmesh_logging.h" 26 #include "libmesh/mesh_base.h" 27 #include "libmesh/point.h" 28 #include "libmesh/utility.h" 31 #ifdef LIBMESH_HAVE_GZSTREAM 32 # include "gzstream.h" 41 #ifdef LIBMESH_HAVE_CXX11_REGEX 47 void test_and_add(std::unique_ptr<libMesh::Elem> e,
53 (
"Warning: STL file contained sliver element with volume " <<
55 mesh.add_elem(std::move(e));
66 _subdivide_second_order (true)
75 _subdivide_second_order (true)
83 LOG_SCOPE(
"write()",
"STLIO");
88 libmesh_parallel_only(mesh_ptr->
comm());
91 std::unique_ptr<DistributedMesh> mesh_copy;
92 std::unique_ptr<MeshSerializer> serializer;
95 mesh_copy = std::make_unique<DistributedMesh>(*mesh_ptr);
96 mesh_ptr = mesh_copy.get();
97 serializer = std::make_unique<MeshSerializer>(*mesh_copy,
true,
true);
106 std::ofstream out_stream (fname.c_str());
111 if (!out_stream.good())
112 libmesh_file_error(fname.c_str());
115 out_stream <<
"solid " << this->
_name <<
'\n';
119 for (
auto elem :
mesh.element_ptr_range())
122 if (elem->type() ==
TRI6 || elem->type() ==
TRI7)
125 libmesh_not_implemented();
127 libmesh_error_msg(
"Tried to write a non-linear triangle to an STL file");
130 libmesh_error_msg_if(elem->type() !=
TRI3,
131 "Tried to write a non-triangle to an STL file");
133 auto n = (elem->point(1)-elem->point(0)).cross(elem->point(2)-elem->point(0));
136 if (
auto length = n.norm())
139 out_stream <<
"facet normal " <<
145 elem->point(0)(0) <<
' ' <<
146 elem->point(0)(1) <<
' ' <<
147 elem->point(0)(2) <<
"\n" 149 elem->point(1)(0) <<
' ' <<
150 elem->point(1)(1) <<
' ' <<
151 elem->point(1)(2) <<
"\n" 153 elem->point(2)(0) <<
' ' <<
154 elem->point(2)(1) <<
' ' <<
155 elem->point(2)(2) <<
"\n" 160 out_stream <<
"endsolid" << std::endl;
167 LOG_SCOPE(
"read()",
"STLIO");
179 std::unique_ptr<std::istream> fstream =
183 const char * expected_header =
"solid!";
184 bool is_ascii_stl =
false,
185 is_binary_stl =
false;
186 while (fstream->get(c))
190 if (c == *expected_header)
193 if (*expected_header ==
'!')
201 is_binary_stl =
true;
211 else if (is_binary_stl)
213 fstream->seekg(0, std::ios_base::end);
214 std::size_t length = fstream->tellg();
219 libmesh_error_msg(
"Failed to read an STL header in " << filename);
226 const bool gzipped_file = (basename.rfind(
".gz") == basename.size() - 3);
228 std::unique_ptr<std::istream> file;
232 #ifdef LIBMESH_HAVE_GZSTREAM 233 auto inf = std::make_unique<igzstream>();
235 inf->open(filename.c_str(), std::ios::in);
236 file = std::move(inf);
238 libmesh_error_msg(
"ERROR: need gzstream to handle .gz files!!!");
243 auto inf = std::make_unique<std::ifstream>();
248 inf->open(new_name.c_str(), std::ios::in);
249 file = std::move(inf);
259 LOG_SCOPE(
"read_ascii()",
"STLIO");
261 #ifndef LIBMESH_HAVE_CXX11_REGEX 262 libmesh_not_implemented();
268 const std::regex all_expected_chars
269 (
"^[\\w\\d\\-\\.\\^\\$]*$");
271 const std::regex start_regex
273 const std::regex start_with_name_regex
274 (
"^\\s*solid\\s+(\\w+)");
276 const std::regex start_facet_regex
288 const std::regex start_loop_regex
289 (
"^\\s*outer\\s+loop");
291 const std::regex vertex_regex
293 "\\s+([+-]?(\\d+([.]\\d*)?([eE][+-]?\\d+)?|[.]\\d+([eE][+-]?\\d+)?)" 294 "\\s+[+-]?(\\d+([.]\\d*)?([eE][+-]?\\d+)?|[.]\\d+([eE][+-]?\\d+)?)" 295 "\\s+[+-]?(\\d+([.]\\d*)?([eE][+-]?\\d+)?|[.]\\d+([eE][+-]?\\d+)?))");
297 const std::regex end_facet_regex
298 (
"^\\s*end\\s*facet");
300 const std::regex end_loop_regex
301 (
"^\\s*end\\s*loop");
303 bool have_started =
false;
304 bool in_facet =
false;
305 bool in_vertex_loop =
false;
306 std::unique_ptr<Tri3> triangle;
310 std::unordered_map<Point, Node *> mesh_points;
312 for (std::string line; std::getline(file, line);)
317 for (
char & c : line)
320 (c !=
'\n' && c !=
'\r' &&
321 (c < ' ' || c >
'~'),
322 "Found non-ASCII character " <<
int(c) <<
" on line " <<
323 line_num <<
"\nSTLIO does not yet support binary files.");
326 if (std::regex_search(line, sm, start_regex))
328 if (std::regex_search(line, sm, start_with_name_regex))
332 "Found two 'solid' lines starting the same STL file?");
336 else if (std::regex_search(line, sm, start_facet_regex))
340 "Found a repeated 'facet' line with no 'endfacet' between them.");
358 else if (std::regex_search(line, end_facet_regex))
362 "Found an 'endfacet' line with no matching 'facet'.");
366 else if (std::regex_search(line, start_loop_regex))
370 "Found an 'outer loop' line with no matching 'facet'.");
373 "Found a repeated 'outer loop' line with no 'endloop' between them.");
374 in_vertex_loop =
true;
375 triangle = std::make_unique<Tri3>();
379 else if (std::regex_search(line, sm, vertex_regex))
381 const std::string normalvec = sm[1];
385 std::stringstream ss(normalvec);
392 if (
auto it = mesh_points.find(p); it != mesh_points.end())
399 mesh_points[p] = node;
404 "Found more than 3 vertices in a loop; STLIO only supports Tri3.");
405 triangle->set_node(next_vertex++, node);
408 else if (std::regex_search(line, end_loop_regex))
412 "Found an 'endloop' line after only seeing " << next_vertex <<
" vertices in 'loop'.");
415 "Found an 'endloop' line with no matching 'loop'.");
416 in_vertex_loop =
false;
418 test_and_add(std::move(triangle),
mesh);
424 "File ended without ending a facet first.");
428 "File ended without ending an outer loop first.");
429 #endif // LIBMESH_HAVE_CXX11_REGEX 435 std::size_t input_size)
437 LOG_SCOPE(
"read_binary()",
"STLIO");
441 char header_buffer[80];
444 file.read(header_buffer, 80);
449 uint32_t test_int = 0x87654321;
450 const bool big_endian = ((*
reinterpret_cast<char *
>(&test_int)) != 0x21);
456 if constexpr (
sizeof(
float) != 4)
457 libmesh_error_msg(
"Trying to read 4 byte floats without 4-byte float?");
461 file.read(reinterpret_cast<char*>(&
n_elem), 4);
466 (input_size < 84+
n_elem*50),
467 "Not enough data for " <<
n_elem <<
" STL triangles in " <<
468 input_size <<
" uncompressed bytes.");
470 std::unique_ptr<Tri3> triangle;
471 std::unordered_map<Point, Node *> mesh_points;
477 triangle = std::make_unique<Tri3>();
480 char ignored_buffer[12];
481 file.read(ignored_buffer, 12);
486 float point_buffer[3];
487 file.read(reinterpret_cast<char*>(point_buffer), 12);
488 endian_fix(point_buffer[0]);
489 endian_fix(point_buffer[1]);
490 endian_fix(point_buffer[2]);
491 const Point p {point_buffer[0],
496 if (
auto it = mesh_points.find(p); it != mesh_points.end())
503 mesh_points[p] = node;
506 triangle->set_node(i, node);
511 file.read(ignored_buffer, 2);
513 test_and_add(std::move(triangle),
mesh);
This Functor simply takes an object and reverses its byte representation.
std::unique_ptr< std::istream > open_file(const std::string &filename)
Helper to open possibly-zipped files.
A Node is like a Point, but with more information.
static constexpr Real TOLERANCE
virtual void read(const std::string &mesh_file) override
This method implements reading a mesh from a specified file.
unsigned int & ascii_precision()
Return/set the precision to use when writing ASCII files.
const Parallel::Communicator & comm() const
This class defines an abstract interface for Mesh output.
The libMesh namespace provides an interface to certain functionality in the library.
virtual Node * add_point(const Point &p, const dof_id_type id=DofObject::invalid_id, const processor_id_type proc_id=DofObject::invalid_processor_id)=0
Add a new Node at Point p to the end of the vertex array, with processor_id procid.
This is the MeshBase class.
virtual void read_ascii(std::istream &input)
This method implements reading a mesh from a specified ASCII input stream.
virtual bool is_serial() const
void libmesh_ignore(const Args &...)
virtual void write(const std::string &) override
This method implements writing a mesh to a specified file.
bool _subdivide_second_order
Flag to subdivide second order elements.
std::string unzip_file(std::string_view name)
Create an unzipped copy of a bz2 or xz file, returning the name of the now-unzipped file that can be ...
void set_mesh_dimension(unsigned char d)
Resets the logical dimension of the mesh.
virtual void read_binary(std::istream &input, std::size_t input_size=0)
This method implements reading a mesh from a specified binary input stream.
virtual void clear()
Deletes all the element and node data that is currently stored.
std::string_view basename_of(const std::string &fullname)
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
IntRange< T > make_range(T beg, T end)
The 2-parameter make_range() helper function returns an IntRange<T> when both input parameters are of...
STLIO(const MeshBase &)
Constructor.
processor_id_type processor_id() const
A Point defines a location in LIBMESH_DIM dimensional Real space.