diff --git a/BaseLib/StringTools.cpp b/BaseLib/StringTools.cpp index fa9853ea76f5abfdb998e9d9550936994730770b..a6a89c78ca1a772bb36380b6aa2ea7b340546899 100644 --- a/BaseLib/StringTools.cpp +++ b/BaseLib/StringTools.cpp @@ -60,6 +60,15 @@ void trim(std::string &str, char ch) str.erase(str.begin(), str.end()); } +void simplify(std::string &str) +{ + trim (str); + str.erase( + std::unique(str.begin(), str.end(), [](char a, char b) { return a == ' ' && b == ' '; }), + str.end() + ); +} + std::string stringToUpper(std::string const& str) { std::string s = str; diff --git a/BaseLib/StringTools.h b/BaseLib/StringTools.h index cd1e2fdd6164d97adbdbc8160ed6585f74071d55..77a451f3c893392d954bae2c4f902cc594592705 100644 --- a/BaseLib/StringTools.h +++ b/BaseLib/StringTools.h @@ -69,9 +69,16 @@ template<typename T> T str2number (const std::string &str) /** * Strip whitespace (or other characters) from the beginning and end of a string. + * Equivalent functionality to Qt::QString::trim(). */ void trim(std::string &str, char ch=' '); +/** + * Removes multiple whitespaces (or other characters) from within a string. + * Equivalent functionality to Qt::QString::simplify(). + */ +void simplify(std::string &str); + /** * Returns same string with all characters in upper case. * diff --git a/FileIO/ImportFileTypes.h b/FileIO/ImportFileTypes.h index 5af5932c8d3a4b0d3af12017fd010b0793c21a83..6b7eb2ee6eba1f6af6c4915cb88831749fd1c4cc 100644 --- a/FileIO/ImportFileTypes.h +++ b/FileIO/ImportFileTypes.h @@ -84,7 +84,7 @@ public: else if (t==ImportFileType::SHAPE) return "ESRI Shape files (*.shp)"; else if (t==ImportFileType::TETGEN) - return "TetGen node files (*.node)"; + return "TetGen node files (*.node *.poly)"; else if (t==ImportFileType::VTK) return "VTK files (*.vtk *.vti *.vtr *.vts *.vtp *.vtu)"; else return "All files (*.*)"; diff --git a/FileIO/TetGenInterface.cpp b/FileIO/TetGenInterface.cpp index 98e1718ae70d31596f121c9a8af3834d525a8fbc..ad009c5e93b724430ddd8995a4ba680cfb4eb6e2 100644 --- a/FileIO/TetGenInterface.cpp +++ b/FileIO/TetGenInterface.cpp @@ -14,8 +14,10 @@ #include <cstddef> #include <string> +#include <fstream> // BaseLib +#include "FileTools.h" #include "StringTools.h" // ThirdParty/logog @@ -33,7 +35,7 @@ namespace FileIO { TetGenInterface::TetGenInterface() : - _nodes(), _elements(), _zero_based_idx (false) + _zero_based_idx (false) { } @@ -41,8 +43,153 @@ TetGenInterface::~TetGenInterface() { } +bool TetGenInterface::readTetGenPoly (std::string const& poly_fname, + GeoLib::GEOObjects &geo_objects) +{ + std::ifstream poly_stream (poly_fname.c_str()); + + if (!poly_stream) + { + ERR ("TetGenInterface::readTetGenPoly failed to open %s", poly_fname.c_str()); + return false; + } + + std::vector<MeshLib::Node*> nodes; + if (!readNodesFromStream (poly_stream, nodes)) + { + // remove nodes read until now + for (std::size_t k(0); k<nodes.size(); ++k) + delete nodes[k]; + return false; + } + const std::size_t nNodes (nodes.size()); + std::vector<GeoLib::Point*> *points = new std::vector<GeoLib::Point*>; + points->reserve(nNodes); + for (std::size_t k(0); k<nNodes; ++k) + { + points->push_back(new GeoLib::Point(nodes[k]->getCoords())); + delete nodes[k]; + } + std::vector<GeoLib::Surface*> *surfaces = new std::vector<GeoLib::Surface*>; + if (!parseFacets(poly_stream, *surfaces, *points)) + { + // remove surfaces read until now but keep the points + for (std::size_t k=0; k<surfaces->size(); k++) + delete (*surfaces)[k]; + delete surfaces; + surfaces = nullptr; + } + + std::string geo_name (BaseLib::extractBaseNameWithoutExtension(poly_fname)); + geo_objects.addPointVec(points, geo_name); + if (surfaces) + geo_objects.addSurfaceVec(surfaces, geo_name); + return true; +} + +std::size_t TetGenInterface::getNFacets(std::ifstream &input) const +{ + std::string line; + while (!input.fail()) + { + getline (input, line); + if (input.fail()) + { + ERR("TetGenInterface::getNFacets(): Error reading number of facets."); + return false; + } + + BaseLib::simplify(line); + if (line.empty() || line.compare(0,1,"#") == 0) + continue; + + const std::list<std::string> fields = BaseLib::splitString(line, ' '); + return BaseLib::str2number<size_t> (*fields.begin()); + // here this line also includes a flag for boundary markers which we ignore for now + } + return false; +} + +bool TetGenInterface::parseFacets(std::ifstream &input, + std::vector<GeoLib::Surface*> &surfaces, + std::vector<GeoLib::Point*> &points) const +{ + const std::size_t nFacets (this->getNFacets(input)); + std::size_t nMultPolys (0); + std::string line; + surfaces.reserve(nFacets); + std::list<std::string>::const_iterator it; + + const unsigned offset = (_zero_based_idx) ? 0 : 1; + for (std::size_t k(0); k<nFacets && !input.fail(); k++) + { + getline (input, line); + if (input.fail()) + { + ERR("TetGenInterface::parseFacets(): Error reading facet %d.", k); + return false; + } + + BaseLib::simplify(line); + if (line.empty() || line.compare(0,1,"#") == 0) + { + k--; + continue; + } + + // read facets + const std::list<std::string> poly_def_fields = BaseLib::splitString(line, ' '); + it = poly_def_fields.begin(); + const std::size_t nPolys = BaseLib::str2number<std::size_t>(*it); + const std::size_t nPolyHoles = (poly_def_fields.size()>1) ? BaseLib::str2number<std::size_t>(*(++it)) : 0; + // here this line also potentially includes a boundary marker which we ignore for now + nMultPolys += (nPolys-1); + + // read polys + for (std::size_t i(0); i<nPolys && !input.fail(); ++i) + { + getline (input, line); + BaseLib::simplify(line); + if (line.empty() || line.compare(0,1,"#") == 0) + { + i--; + continue; + } + + const std::list<std::string> point_fields = BaseLib::splitString(line, ' '); + it = point_fields.begin(); + const std::size_t nPoints = BaseLib::str2number<std::size_t>(*it); + if (point_fields.size() > nPoints) + { + GeoLib::Polyline polyline(points); + for (std::size_t j(0); j<nPoints; ++j) + polyline.addPoint(BaseLib::str2number<std::size_t>(*(++it))-offset); + + polyline.closePolyline(); + surfaces.push_back(GeoLib::Surface::createSurface(polyline)); + } + else + { + ERR("TetGenInterface::parseFacets(): Error reading points for polygon %d of facet %d.", i, k); + return false; + } + } + for (std::size_t j(0); j<nPolyHoles && !input.fail(); ++j) + getline(input, line); + // Here are points defined which are located in holes within the surface. We ignore these as they are not part of the actual geometry. + } + // here the poly-file potentially defines a number of points to mark holes within the volumes defined by the facets, these are ignored for now + // here the poly-file potentially defines a number of region attributes, these are ignored for now + + if (surfaces.size() == nFacets+nMultPolys) + return true; + + ERR ("TetGenInterface::parseFacets(): Number of expected surfaces (%d) does not match number of found surfaces (%d).", nFacets+nMultPolys, surfaces.size()); + return false; +} + MeshLib::Mesh* TetGenInterface::readTetGenMesh (std::string const& nodes_fname, - std::string const& ele_fname) + std::string const& ele_fname) { std::ifstream ins_nodes (nodes_fname.c_str()); std::ifstream ins_ele (ele_fname.c_str()); @@ -53,42 +200,45 @@ MeshLib::Mesh* TetGenInterface::readTetGenMesh (std::string const& nodes_fname, ERR ("TetGenInterface::readTetGenMesh failed to open %s", nodes_fname.c_str()); if (!ins_ele) ERR ("TetGenInterface::readTetGenMesh failed to open %s", ele_fname.c_str()); - return NULL; + return nullptr; } - if (!readNodesFromStream (ins_nodes)) { + std::vector<MeshLib::Node*> nodes; + if (!readNodesFromStream (ins_nodes, nodes)) { // remove nodes read until now - for (std::size_t k(0); k<_nodes.size(); k++) { - delete _nodes[k]; + for (std::size_t k(0); k<nodes.size(); k++) { + delete nodes[k]; } - return NULL; + return nullptr; } - if (!readElementsFromStream (ins_ele)) { + std::vector<MeshLib::Element*> elements; + if (!readElementsFromStream (ins_ele, elements, nodes)) { // remove elements read until now - for (std::size_t k(0); k<_elements.size(); k++) { - delete _elements[k]; + for (std::size_t k(0); k<elements.size(); k++) { + delete elements[k]; } // remove nodes - for (std::size_t k(0); k<_nodes.size(); k++) { - delete _nodes[k]; + for (std::size_t k(0); k<nodes.size(); k++) { + delete nodes[k]; } - return NULL; + return nullptr; } - return new MeshLib::Mesh(nodes_fname, _nodes, _elements); + const std::string mesh_name (BaseLib::extractBaseNameWithoutExtension(nodes_fname)); + return new MeshLib::Mesh(mesh_name, nodes, elements); } -bool TetGenInterface::readNodesFromStream (std::ifstream &ins) +bool TetGenInterface::readNodesFromStream (std::ifstream &ins, + std::vector<MeshLib::Node*> &nodes) { std::string line; getline (ins, line); size_t pos_beg (line.find_first_not_of(" ")); size_t n_nodes, dim, n_attributes; bool boundary_markers; - bool not_read_header (true); - while (!ins.fail() && not_read_header) + while (!ins.fail()) { line = line.substr(pos_beg); if (line.compare(0,1,"#") == 0) @@ -96,30 +246,26 @@ bool TetGenInterface::readNodesFromStream (std::ifstream &ins) // this line is a comment - skip getline (ins, line); pos_beg = line.find_first_not_of(" "); + continue; } - else - // read header line - not_read_header = !parseNodesFileHeader(line, - n_nodes, - dim, - n_attributes, - boundary_markers); + // read header line + bool header_okay = parseNodesFileHeader(line, n_nodes, dim, n_attributes, boundary_markers); + if (!header_okay) + return false; + if (!parseNodes(ins, nodes, n_nodes, dim)) + return false; + return true; } - if (not_read_header) - return false; - if (!parseNodes(ins, n_nodes, dim)) - return false; - - return true; + return false; } bool TetGenInterface::parseNodesFileHeader(std::string &line, - size_t& n_nodes, - size_t& dim, - size_t& n_attributes, - bool& boundary_markers) const + std::size_t &n_nodes, + std::size_t &dim, + std::size_t &n_attributes, + bool &boundary_markers) const { - size_t pos_beg, pos_end; + std::size_t pos_beg, pos_end; // number of nodes pos_beg = line.find_first_not_of (" "); @@ -128,7 +274,7 @@ bool TetGenInterface::parseNodesFileHeader(std::string &line, n_nodes = BaseLib::str2number<size_t> (line.substr(pos_beg, pos_end - pos_beg)); else { - ERR("TetGenInterface::parseNodesFileHeader(): could not correct read TetGen mesh header - number of nodes"); + ERR("TetGenInterface::parseNodesFileHeader(): could not read number of nodes specified in header."); return false; } // dimension @@ -152,66 +298,78 @@ bool TetGenInterface::parseNodesFileHeader(std::string &line, return true; } -bool TetGenInterface::parseNodes(std::ifstream& ins, size_t n_nodes, size_t dim) +bool TetGenInterface::parseNodes(std::ifstream &ins, + std::vector<MeshLib::Node*> &nodes, + std::size_t n_nodes, + std::size_t dim) { - std::size_t pos_beg, pos_end; std::string line; - double* coordinates (static_cast<double*> (alloca (sizeof(double) * dim))); + double* coordinates (new double[dim]); + nodes.reserve(n_nodes); - for (std::size_t k(0); k < n_nodes && !ins.fail(); k++) { + for (std::size_t k(0); k < n_nodes && !ins.fail(); k++) + { getline(ins, line); - if (!ins.fail()) { - if (!line.empty()) { - pos_end = 0; - // read id - size_t id; - pos_beg = line.find_first_not_of(" ", pos_end); - pos_end = line.find_first_of(" \n", pos_beg); - if (pos_beg != std::string::npos && pos_end != std::string::npos) { - id = BaseLib::str2number<size_t> (line.substr(pos_beg, pos_end - pos_beg)); - if (k == 0 && id == 0) - _zero_based_idx = true; - } else { - ERR("TetGenInterface::parseNodes(): error reading id of node %d", k); - return false; - } - // read coordinates - for (size_t i(0); i < dim; i++) { - pos_beg = line.find_first_not_of(" ", pos_end); - pos_end = line.find_first_of(" \n", pos_beg); - if (pos_end == std::string::npos) pos_end = line.size(); - if (pos_beg != std::string::npos) - coordinates[i] = BaseLib::str2number<double> ( - line.substr(pos_beg, pos_end - pos_beg)); - else { - ERR("TetGenInterface::parseNodes(): error reading coordinate %d of node %d", i, k); - return false; - } - } - if (!_zero_based_idx) id--; - // since CFEMesh is our friend we can access private data of mesh - _nodes.push_back(new MeshLib::Node(coordinates, id)); - // read attributes and boundary markers ... - at the moment we do not use this information - } + if (ins.fail()) + { + ERR("TetGenInterface::parseNodes(): Error reading node %d.", k); + return false; + } + + std::size_t id; + std::size_t pos_end = 0; + std::size_t pos_beg = line.find_first_not_of(" ", pos_end); + pos_end = line.find_first_of(" \n", pos_beg); + + if (line.empty() || pos_beg==pos_end || line.compare(pos_beg,1,"#") == 0) + { + k--; + continue; + } + + if (pos_beg != std::string::npos && pos_end != std::string::npos) { + id = BaseLib::str2number<size_t> (line.substr(pos_beg, pos_end - pos_beg)); + if (k == 0 && id == 0) + _zero_based_idx = true; } else { - ERR("TetGenInterface::parseNodes(): error reading node %d, stream error", k); + ERR("TetGenInterface::parseNodes(): Error reading ID of node %d.", k); + delete [] coordinates; return false; } + // read coordinates + const unsigned offset = (_zero_based_idx) ? 0 : 1; + for (std::size_t i(0); i < dim; i++) { + pos_beg = line.find_first_not_of(" ", pos_end); + pos_end = line.find_first_of(" \n", pos_beg); + if (pos_end == std::string::npos) pos_end = line.size(); + if (pos_beg != std::string::npos) + coordinates[i] = BaseLib::str2number<double>(line.substr(pos_beg, pos_end-pos_beg)); + else { + ERR("TetGenInterface::parseNodes(): error reading coordinate %d of node %d.", i, k); + delete [] coordinates; + return false; + } + } + + nodes.push_back(new MeshLib::Node(coordinates, id-offset)); + // read attributes and boundary markers ... - at the moment we do not use this information } + delete [] coordinates; return true; } -bool TetGenInterface::readElementsFromStream(std::ifstream &ins) +bool TetGenInterface::readElementsFromStream(std::ifstream &ins, + std::vector<MeshLib::Element*> &elements, + const std::vector<MeshLib::Node*> &nodes) const { std::string line; getline (ins, line); - size_t pos_beg (line.find_first_not_of(" ")); - size_t n_tets, n_nodes_per_tet; + std::size_t pos_beg (line.find_first_not_of(" ")); + std::size_t n_tets, n_nodes_per_tet; bool region_attributes; - bool not_read_header (true); - while (!ins.fail() && not_read_header) + while (!ins.fail()) { line = line.substr(pos_beg); if (line.compare(0,1,"#") == 0) @@ -219,28 +377,26 @@ bool TetGenInterface::readElementsFromStream(std::ifstream &ins) // this line is a comment - skip getline (ins, line); pos_beg = line.find_first_not_of(" "); + continue; } - else - // read header line - not_read_header = !parseElementsFileHeader(line, - n_tets, - n_nodes_per_tet, - region_attributes); + + // read header line + bool header_okay = parseElementsFileHeader(line, n_tets, n_nodes_per_tet, region_attributes); + if (!header_okay) + return false; + if (!parseElements(ins, elements, nodes, n_tets, n_nodes_per_tet, region_attributes)) + return false; + return true; } - if (not_read_header) - return false; - if (!parseElements(ins, n_tets, n_nodes_per_tet, region_attributes)) - return false; - - return true; + return false; } bool TetGenInterface::parseElementsFileHeader(std::string &line, - size_t& n_tets, - size_t& n_nodes_per_tet, + std::size_t& n_tets, + std::size_t& n_nodes_per_tet, bool& region_attribute) const { - size_t pos_beg, pos_end; + std::size_t pos_beg, pos_end; // number of tetrahedras pos_beg = line.find_first_not_of (" "); @@ -248,7 +404,7 @@ bool TetGenInterface::parseElementsFileHeader(std::string &line, if (pos_beg != std::string::npos && pos_end != std::string::npos) n_tets = BaseLib::str2number<size_t> (line.substr(pos_beg, pos_end - pos_beg)); else { - ERR("TetGenInterface::parseElementsFileHeader(): could not correct read TetGen mesh header - number of tetrahedras"); + ERR("TetGenInterface::parseElementsFileHeader(): Could not read number of tetrahedra specified in header."); return false; } // nodes per tet - either 4 or 10 @@ -268,81 +424,124 @@ bool TetGenInterface::parseElementsFileHeader(std::string &line, return true; } -bool TetGenInterface::parseElements(std::ifstream& ins, size_t n_tets, size_t n_nodes_per_tet, - bool region_attribute) +bool TetGenInterface::parseElements(std::ifstream& ins, + std::vector<MeshLib::Element*> &elements, + const std::vector<MeshLib::Node*> &nodes, + std::size_t n_tets, + std::size_t n_nodes_per_tet, + bool region_attribute) const { - size_t pos_beg, pos_end; std::string line; - size_t* ids (static_cast<size_t*>(alloca (sizeof (size_t) * n_nodes_per_tet))); + std::size_t* ids (static_cast<size_t*>(alloca (sizeof (size_t) * n_nodes_per_tet))); + elements.reserve(n_tets); - for (size_t k(0); k < n_tets && !ins.fail(); k++) + const unsigned offset = (_zero_based_idx) ? 0 : 1; + for (std::size_t k(0); k < n_tets && !ins.fail(); k++) { getline (ins, line); - if (!ins.fail()) + if (ins.fail()) { - if (!line.empty()) + ERR("TetGenInterface::parseElements(): Error reading tetrahedron %d.", k); + return false; + } + + std::size_t pos_end = 0; + std::size_t pos_beg = line.find_first_not_of(" ", pos_end); + pos_end = line.find_first_of(" \n", pos_beg); + + if (line.empty() || pos_beg==pos_end || line.compare(pos_beg,1,"#") == 0) + { + k--; + continue; + } + + if (pos_beg == std::string::npos || pos_end == std::string::npos) + { + ERR("TetGenInterface::parseElements(): Error reading id of tetrahedron %d.", k); + return false; + } + + // read node ids + for (std::size_t i(0); i < n_nodes_per_tet; i++) + { + pos_beg = line.find_first_not_of(" ", pos_end); + pos_end = line.find_first_of(" ", pos_beg); + if (pos_end == std::string::npos) + pos_end = line.size(); + if (pos_beg != std::string::npos && pos_end != std::string::npos) + ids[i] = BaseLib::str2number<std::size_t>(line.substr(pos_beg, pos_end - pos_beg)) - offset; + else { - pos_end = 0; - // read id - size_t id; - pos_beg = line.find_first_not_of(" ", pos_end); - pos_end = line.find_first_of(" \n", pos_beg); - if (pos_beg != std::string::npos && pos_end != std::string::npos) - id = BaseLib::str2number<size_t>(line.substr(pos_beg, pos_end - pos_beg)); - else { - ERR("TetGenInterface::parseElements(): error reading id of tetrahedra %d", k); - return false; - } - // read node ids - for (size_t i(0); i < n_nodes_per_tet; i++) - { - pos_beg = line.find_first_not_of(" ", pos_end); - pos_end = line.find_first_of(" ", pos_beg); - if (pos_end == std::string::npos) - pos_end = line.size(); - if (pos_beg != std::string::npos && pos_end != - std::string::npos) - ids[i] = BaseLib::str2number<std::size_t>(line.substr(pos_beg, pos_end - pos_beg)); - else - { - ERR("TetGenInterface::parseElements(): error reading node %d of tetrahedra %d", i, k); - return false; - } - } - if (!_zero_based_idx) { - id--; - for (size_t i(0); i < n_nodes_per_tet; i++) - ids[i]--; - } - - // read region attribute - this is something like material group - unsigned region (0); - if (region_attribute) { - pos_beg = line.find_first_not_of(" ", pos_end); - pos_end = line.find_first_of(" ", pos_beg); - if (pos_end == std::string::npos) pos_end = line.size(); - if (pos_beg != std::string::npos && pos_end != std::string::npos) - region = BaseLib::str2number<unsigned> (line.substr(pos_beg, pos_end - pos_beg)); - else { - ERR("TetGenInterface::parseElements(): error reading region attribute of tetrahedra %d", k); - return false; - } - } - // insert new element into vector - MeshLib::Node** tet_nodes = new MeshLib::Node*[4]; - for (unsigned k(0); k<4; k++) { - tet_nodes[k] = _nodes[ids[k]]; - } - _elements.push_back (new MeshLib::Tet(tet_nodes, region)); + ERR("TetGenInterface::parseElements(): Error reading node %d of tetrahedron %d.", i, k); + return false; + } + } + // read region attribute - this is something like material group + unsigned region (0); + if (region_attribute) { + pos_beg = line.find_first_not_of(" ", pos_end); + pos_end = line.find_first_of(" ", pos_beg); + if (pos_end == std::string::npos) pos_end = line.size(); + if (pos_beg != std::string::npos && pos_end != std::string::npos) + region = BaseLib::str2number<unsigned> (line.substr(pos_beg, pos_end - pos_beg)); + else { + ERR("TetGenInterface::parseElements(): Error reading region attribute of tetrahedron %d.", k); + return false; } } - else + // insert new element into vector + MeshLib::Node** tet_nodes = new MeshLib::Node*[4]; + for (unsigned k(0); k<4; k++) { + tet_nodes[k] = nodes[ids[k]]; + } + elements.push_back (new MeshLib::Tet(tet_nodes, region)); + } + return true; +} + +bool TetGenInterface::writeTetGenPoly(const std::string &file_name, + const GeoLib::GEOObjects &geo_objects, + const std::string &geo_name) const +{ + std::vector<GeoLib::Point*> const*const points = geo_objects.getPointVec(geo_name); + std::vector<GeoLib::Surface*> const*const surfaces = geo_objects.getSurfaceVec(geo_name); + + if (points==nullptr) + { + ERR ("Geometry %s not found.", geo_name.c_str()); + return false; + } + if (surfaces==nullptr) + WARN ("No surfaces found for geometry %s. Writing points only.", geo_name.c_str()); + + std::ofstream out( file_name.c_str(), std::ios::out ); + // the points header + const std::size_t nPoints (points->size()); + out << nPoints << " 3 0 0\n"; + // the point list + for (std::size_t i=0; i<nPoints; ++i) + out << i << " " << (*(*points)[i])[0] << " " << (*(*points)[i])[1] << " " << (*(*points)[i])[2] << "\n"; + // the surfaces header + const std::size_t nSurfaces = (surfaces) ? surfaces->size() : 0; + out << nSurfaces << " 0\n"; + // the facets list + for (std::size_t i=0; i<nSurfaces; ++i) + { + // the number of polys per facet + const std::size_t nTriangles ((*surfaces)[i]->getNTriangles()); + out << nTriangles << "\n"; + // the poly list + for (std::size_t j=0; j<nTriangles; ++j) { - ERR("TetGenInterface::parseElements(): error reading node %d", k); - return false; + const GeoLib::Triangle &tri = *(*(*surfaces)[i])[j]; + out << "3 " << tri[0] << " " << tri[1] << " " << tri[2] << "\n"; } } + out << "0\n"; // the polygon holes list + out << "0\n"; // the region attribues list + INFO ("TetGenInterface::writeTetGenPoly() - %d points and %d surfaces successfully written.", nPoints, nSurfaces); + out.close(); return true; } diff --git a/FileIO/TetGenInterface.h b/FileIO/TetGenInterface.h index 4535d956c2b79bbe051da28abfd6433eb143b369..72ca2c04618a2dabfe7e2134e9633a2879395eea 100644 --- a/FileIO/TetGenInterface.h +++ b/FileIO/TetGenInterface.h @@ -17,6 +17,8 @@ #include <vector> +#include "GEOObjects.h" + // forward declaration of class Node and Element namespace MeshLib { @@ -28,7 +30,11 @@ namespace MeshLib namespace FileIO { /** - * class TetGenInterface is used to read meshes created by <a href="http://tetgen.berlios.de/">TetGen</a> + * class TetGenInterface is used to read file formats used by <a href="http://tetgen.berlios.de/">TetGen</a>. + * Currently supported formats are: + * poly - Geometric point and surface definition + * node - mesh node / geometric point definition + * ele - mesh element definition */ class TetGenInterface { @@ -38,82 +44,124 @@ public: /** * Method reads the TetGen mesh from node file and element file. - * @param nodes_fname file name of the nodes file - * @param ele_fname file name of the elements file - * @return on success the method returns a (pointer to a) CFEMesh, else the method returns NULL + * @param poly_fname file name of the poly file + * @param geo_objects where the geometry is written to + * @return on success the method returns true, otherwise it returns false + */ + bool readTetGenPoly (std::string const& poly_fname, + GeoLib::GEOObjects &geo_objects); + + /** + * Method reads the TetGen mesh from node file and element file. + * @param nodes_fname file name of the nodes file + * @param ele_fname file name of the elements file + * @return on success the method returns a (pointer to a) CFEMesh, else the method returns nullptr */ MeshLib::Mesh* readTetGenMesh (std::string const& nodes_fname, std::string const& ele_fname); - /** in order to have a direct access to the - * data structures for nodes and elements we make - * class TetGenInterface a friend of the mesh class + + /** + * Writes the geometry of a given name to TetGen poly-file. + * @param file_name file name of the new poly file + * @param geo_objects the container for the geometry. + * @param geo_name the name for the geometry. + * @return returns true on success and false otherwise. */ - friend class MeshLib::Mesh; + bool writeTetGenPoly(const std::string &file_name, + const GeoLib::GEOObjects &geo_objects, + const std::string &geo_name) const; private: + /// Returns the declared number of facets in the poly file. + std::size_t getNFacets(std::ifstream &input) const; + /** - * Method reads the nodes from stream and stores them in the node vector of the mesh class. + * Method parses the lines reading the facets from TetGen poly file + * @param input the input stream (input) + * @param surfaces the vector of surfaces to be filled (output) + * @param points the point vector needed for creating surfaces (input) + * @return true, if the facets have been read correctly, false if the method detects an error + */ + bool parseFacets(std::ifstream &input, + std::vector<GeoLib::Surface*> &surfaces, + std::vector<GeoLib::Point*> &points) const; + + /** + * Method reads the nodes from stream and stores them in a node vector. * For this purpose it uses methods parseNodesFileHeader() and parseNodes(). - * @param input the input stream + * @param input the input stream * @return true, if all information is read, false if the method detects an error */ - bool readNodesFromStream(std::ifstream &input); + bool readNodesFromStream(std::ifstream &input, + std::vector<MeshLib::Node*> &nodes); + /** * Method parses the header of the nodes file created by TetGen - * @param line the header is in this string (input) - * @param n_nodes number of nodes in the file (output) - * @param dim the spatial dimension of the node (output) - * @param n_attributes the number of attributes for each node (output) - * @param boundary_markers have the nodes boundary information (output) + * @param line the header is in this string (input) + * @param n_nodes number of nodes in the file (output) + * @param dim the spatial dimension of the node (output) + * @param n_attributes the number of attributes for each node (output) + * @param boundary_markers have the nodes boundary information (output) * @return true, if the file header is read, false if the method detects an error */ - bool parseNodesFileHeader(std::string &line, std::size_t& n_nodes, std::size_t& dim, - std::size_t& n_attributes, bool& boundary_markers) const; + bool parseNodesFileHeader(std::string &line, + std::size_t &n_nodes, + std::size_t &dim, + std::size_t &n_attributes, + bool &boundary_markers) const; /** * method parses the lines reading the nodes from TetGen nodes file - * @param ins the input stream (input) - * @param n_nodes the number of nodes to read (input) - * @param dim the spatial dimension of the node (input) + * @param ins the input stream (input) + * @param nodes the nodes vector to be filled (input) + * @param n_nodes the number of nodes to read (input) + * @param dim the spatial dimension of the node (input) * @return true, if the nodes are read, false if the method detects an error */ - bool parseNodes(std::ifstream& ins, std::size_t n_nodes, std::size_t dim); + bool parseNodes(std::ifstream &ins, + std::vector<MeshLib::Node*> &nodes, + std::size_t n_nodes, + std::size_t dim); /** - * Method reads the elements from stream and stores them in the element vector of the mesh class. + * Method reads the elements from stream and stores them in an element vector. * For this purpose it uses methods parseElementsFileHeader() and parseElements(). - * @param input the input stream + * @param input the input stream + * @param elements the elements vector to be filled + * @param nodes the node information needed for creating elements * @return true, if all information is read, false if the method detects an error */ - bool readElementsFromStream(std::ifstream &input); + bool readElementsFromStream(std::ifstream &input, + std::vector<MeshLib::Element*> &elements, + const std::vector<MeshLib::Node*> &nodes) const; /** * Method parses the header of the elements file created by TetGen - * @param line - * @param n_tets - * @param n_nodes_per_tet - * @param region_attribute is on output true, if there + * @param line the header is in this string (input) + * @param n_tets the number of tets to read (input) + * @param n_nodes_per_tet the number of nodes per tets (input) + * @param region_attribute is on output true, if there * @return */ - bool parseElementsFileHeader(std::string &line, std::size_t& n_tets, std::size_t& n_nodes_per_tet, - bool& region_attribute) const; + bool parseElementsFileHeader(std::string &line, + std::size_t &n_tets, + std::size_t &n_nodes_per_tet, + bool ®ion_attribute) const; /** * Method parses the tetrahedras and put them in the element vector of the mesh class. * @param ins the input stream - * @param n_tets the number of tetrahedras that should be read - * @param n_nodes_per_tet the number of nodes per tetrahedron - * @param region_attribute if region attribute is true, region information is read + * @param elements the elements vector to be filled + * @param nodes the node information needed for creating elements + * @param n_tets the number of tetrahedras that should be read + * @param n_nodes_per_tet the number of nodes per tetrahedron + * @param region_attribute if region attribute is true, region information is read * @return true, if the tetrahedras are read, false if the method detects an error */ - bool parseElements(std::ifstream& ins, std::size_t n_tets, std::size_t n_nodes_per_tet, - bool region_attribute); + bool parseElements(std::ifstream& ins, + std::vector<MeshLib::Element*> &elements, + const std::vector<MeshLib::Node*> &nodes, + std::size_t n_tets, + std::size_t n_nodes_per_tet, + bool region_attribute) const; - /** - * the nodes later on handed over to the mesh are stored in this vector - */ - std::vector<MeshLib::Node*> _nodes; - /** - * the elements (tetrahedrons) later on handed over to the mesh are stored in this vector - */ - std::vector<MeshLib::Element*> _elements; /** * the value is true if the indexing is zero based, else false */ diff --git a/Gui/mainwindow.cpp b/Gui/mainwindow.cpp index 33a9dba8cd96cf5aeb3e2ca7be998759ca614644..8dade0950b9306867d5d61e43af2aeaba4364ea4 100644 --- a/Gui/mainwindow.cpp +++ b/Gui/mainwindow.cpp @@ -446,8 +446,7 @@ void MainWindow::open(int file_type) QSettings settings; ImportFileType::type t = static_cast<ImportFileType::type>(file_type); QString type_str = QString::fromStdString((ImportFileType::convertImportFileTypeToString(t))); - QString fileName = QFileDialog::getOpenFileName(this, - "Select " + type_str + " file to import", + QString fileName = QFileDialog::getOpenFileName(this, "Select " + type_str + " file to import", settings.value("lastOpenedFileDirectory").toString(), QString::fromStdString(ImportFileType::getFileSuffixString(t))); if (!fileName.isEmpty()) @@ -688,20 +687,25 @@ void MainWindow::loadFile(ImportFileType::type t, const QString &fileName) } else if (t == ImportFileType::TETGEN) { - QString element_fname = QFileDialog::getOpenFileName(this, "Select TetGen element file", - settings.value("lastOpenedTetgenFileDirectory").toString(), - "TetGen element files (*.ele);;"); - - if (!fileName.isEmpty() && !element_fname.isEmpty()) { + if (fi.suffix().toLower().compare("poly") == 0) + { FileIO::TetGenInterface tetgen; - MeshLib::Mesh* msh (tetgen.readTetGenMesh(fileName.toStdString(), element_fname.toStdString())); - if (msh) { - std::string name(fileName.toStdString()); - msh->setName(name); - _meshModels->addMesh(msh); - } else - OGSError::box("Failed to load a TetGen mesh."); - settings.setValue("lastOpenedFileDirectory", QDir(fileName).absolutePath()); + tetgen.readTetGenPoly(fileName.toStdString(), *(_project.getGEOObjects())); + } + else { + settings.setValue("lastOpenedTetgenFileDirectory", QFileInfo(fileName).absolutePath()); + QString element_fname = QFileDialog::getOpenFileName(this, "Select TetGen element file", + settings.value("lastOpenedTetgenFileDirectory").toString(), + "TetGen element files (*.ele);;"); + + if (!fileName.isEmpty() && !element_fname.isEmpty()) { + FileIO::TetGenInterface tetgen; + MeshLib::Mesh* mesh (tetgen.readTetGenMesh(fileName.toStdString(), element_fname.toStdString())); + if (mesh) + _meshModels->addMesh(mesh); + else + OGSError::box("Failed to load a TetGen mesh."); + } } } else if (t == ImportFileType::VTK)