diff --git a/Applications/FileIO/GocadIO/GocadTSurfaceReader.cpp b/Applications/FileIO/GocadIO/GocadTSurfaceReader.cpp new file mode 100644 index 0000000000000000000000000000000000000000..53bce760347b17e6e9eb6717cdb49674336ff369 --- /dev/null +++ b/Applications/FileIO/GocadIO/GocadTSurfaceReader.cpp @@ -0,0 +1,516 @@ +/** + * + * @copyright + * Copyright (c) 2012-2019, OpenGeoSys Community (http://www.opengeosys.org) + * Distributed under a Modified BSD License. + * See accompanying file LICENSE.txt or + * http://www.opengeosys.org/LICENSE.txt + */ + +#include "GocadTSurfaceReader.h" + +#include <logog/include/logog.hpp> + +#include "Applications/FileIO/GocadIO/CoordinateSystem.h" +#include "BaseLib/FileTools.h" +#include "BaseLib/StringTools.h" +#include "MeshLib/Elements/Tri.h" +#include "MeshLib/IO/VtkIO/VtuInterface.h" +#include "MeshLib/Mesh.h" +#include "MeshLib/Node.h" + +namespace FileIO +{ +namespace Gocad +{ +const std::string mat_id_name = "MaterialIDs"; +const std::string eof_error = "Error: Unexpected end of file."; + +GocadTSurfaceReader::GocadTSurfaceReader(std::string const& filename) + : _file_name(filename) +{ +} + +bool GocadTSurfaceReader::readFile() +{ + std::ifstream in(_file_name.c_str()); + if (!in.is_open()) + { + ERR("GocadTSurfaceReader::allSfc2Mesh(): Could not open file %s.", + _file_name.c_str()); + return false; + } + + while (TSurfaceFound(in)) + { + if (!readMesh(in)) + { + ERR("File parsing aborted...") + return false; + } + } + return true; +} + +bool GocadTSurfaceReader::readMesh(std::ifstream& in) +{ + std::string const mesh_cnt = "-" + std::to_string(_mesh_vec.size() + 1); + std::string mesh_name = BaseLib::dropFileExtension(_file_name) + mesh_cnt; + if (!parseHeader(in, mesh_name)) + return false; + + MeshLib::Properties mesh_prop; + mesh_prop.createNewPropertyVector<int>(mat_id_name, + MeshLib::MeshItemType::Cell, 1); + std::string line(""); + while (std::getline(in, line)) + { + std::vector<std::string> str = BaseLib::splitString(line); + if (line.empty() || isCommentLine(line)) + continue; + else if (str[0] == "GOCAD_ORIGINAL_COORDINATE_SYSTEM") + { + Gocad::CoordinateSystem coordinate_system; + if (!coordinate_system.parse(in)) + { + ERR("Error parsing coordinate system."); + return false; + } + } + else if (str[0] == "GEOLOGICAL_FEATURE" || + str[0] == "GEOLOGICAL_TYPE" || + str[0] == "STRATIGRAPHIC_POSITION") + { + // geological and stratigraphic information - currently ignored + } + else if (str[0] == "PROPERTY_CLASS_HEADER") + { + if (!parsePropertyClass(in)) + { + ERR("Error parsing PROPERTY_CLASS_HEADER."); + return false; + } + } + else if (str[0] == "PROPERTIES") + { + if (!parseProperties(in, str, mesh_prop)) + { + ERR("Error parsing PROPERTIES"); + return false; + } + } + else if (str[0] == "TFACE") + { + std::vector<MeshLib::Node*> nodes; + std::vector<MeshLib::Element*> elems; + std::map<std::size_t, std::size_t> node_id_map; + INFO ("Parsing surface %s", mesh_name.c_str()); + if (!parseSurface(in, nodes, elems, node_id_map, mesh_prop)) + { + ERR("Error parsing Surface %s.", mesh_name.c_str()); + clearData(nodes, elems); + return false; + } + _mesh_vec.push_back( + new MeshLib::Mesh(mesh_name, nodes, elems, mesh_prop)); + return true; + } + else + { + WARN("GocadTSurfaceReader::readMesh() - Unknown keyword found: %s", + line.c_str()); + } + } + ERR("%s", eof_error.c_str()); + return false; +} + +MeshLib::Mesh* GocadTSurfaceReader::getData(std::size_t const idx) const +{ + if (_mesh_vec.empty()) + { + ERR("Error: No mesh data available."); + return nullptr; + } + if (idx < _mesh_vec.size()) + return _mesh_vec[idx]; + ERR("Error: Mesh index (%d) out of range (0, %d).", idx, _mesh_vec.size()); + return nullptr; +} + +std::vector<MeshLib::Mesh*> GocadTSurfaceReader::getData() const +{ + if (_mesh_vec.empty()) + { + ERR("Error: No mesh data available."); + } + return _mesh_vec; +} + +std::string GocadTSurfaceReader::getMeshName(std::size_t idx) const +{ + if (_mesh_vec.empty()) + { + ERR("Error: No mesh data available."); + return std::string(); + } + if (idx < _mesh_vec.size()) + return _mesh_vec[idx]->getName(); + ERR("Error: Mesh index (%d) out of range (0, %d).", idx, _mesh_vec.size()); + return std::string(); +} + +void GocadTSurfaceReader::writeData(std::string const& file_name, + std::size_t const idx, + bool write_binary) const +{ + if (_mesh_vec.empty()) + { + ERR("Error: No mesh data available."); + return; + } + if (idx < _mesh_vec.size()) + { + int data_mode = (write_binary) ? 2 : 0; + bool compressed = (write_binary) ? true : false; + MeshLib::IO::VtuInterface vtu(_mesh_vec[idx], data_mode, compressed); + vtu.writeToFile(file_name); + return; + } + ERR("Error: Mesh index (%d) out of range (0, %d).", idx, _mesh_vec.size()); + return; +} + +void GocadTSurfaceReader::writeData(std::string const& dir, + bool write_binary) const +{ + if (_mesh_vec.empty()) + { + ERR("Error: No mesh data available."); + return; + } + std::size_t const n_meshes(_mesh_vec.size()); + for (std::size_t i = 0; i < n_meshes; ++i) + { + std::string const delim = (dir.back() == '/') ? "" : "/"; + writeData(dir + delim + _mesh_vec[i]->getName(), i, write_binary); + } +} + +bool GocadTSurfaceReader::TSurfaceFound(std::ifstream& in) const +{ + std::string line(""); + while (std::getline(in, line)) + { + if (line.empty() || isCommentLine(line)) + continue; + else if (line.substr(0, 11) == "GOCAD TSurf") + return true; + // No idea why this is allowed in a *.ts file. + // It should be a whole different file type. + else if (line.substr(0, 13) == "GOCAD Model3d") + { + if (!skipModel3d(in)) + { + ERR("Error parsing Model3d"); + return false; + } + } + else + { + ERR("No TSurf-identifier found..."); + return false; + } + } + return false; +} + +bool GocadTSurfaceReader::isCommentLine(std::string const& str) const +{ + return (str.substr(0, 1) == "#"); +} + +bool GocadTSurfaceReader::parseHeader(std::ifstream& in, std::string& mesh_name) +{ + std::string line(""); + while (std::getline(in, line)) + { + if (line.substr(0, 5) == "name:") + mesh_name = line.substr(5, line.length() - 5); + else if (line.substr(0, 1) == "}") + return true; + // ignore all other header parameters + } + ERR("%s", eof_error.c_str()); + return false; +} + +bool GocadTSurfaceReader::parsePropertyClass(std::ifstream& in) const +{ + std::string line(""); + while (std::getline(in, line)) + { + if (line.substr(0, 1) == "}") + return true; + } + ERR("%s", eof_error.c_str()); + return false; +} + +/// Checks if the current line starts with one of the allowed keywords +std::string propertyCheck(std::string const& strng) +{ + std::array<std::string, 7> const property_keywords = { + {"PROPERTY_CLASSES", "PROP_LEGAL_RANGES", "NO_DATA_VALUES", + "PROPERTY_KINDS", "PROPERTY_SUBCLASSES", "UNITS", "ESIZES"}}; + + bool key_found(false); + std::vector<std::string> str = BaseLib::splitString(strng); + for (std::string key : property_keywords) + { + if (str[0] == key) + return key; + } + return std::string(""); +} + +bool GocadTSurfaceReader::parseProperties(std::ifstream& in, + std::vector<std::string> const& names, + MeshLib::Properties& mesh_prop) +{ + // Because properties have no end-tag, the position of the last line is + // stored, so the stream can be set back if none of the allowed property- + // related keywords is found. + std::streampos pos = in.tellg(); + std::string line(""); + while (getline(in, line)) + { + std::string const key = propertyCheck(line); + // This is the intended way to exit this method: + // No property-related keyword has been found, so the stream is set + // back one line and the (unrelated) keyword can be read again in the + // parent method. + if (key.empty()) + { + in.seekg(pos); + return true; + } + + // Currently all property parameters except array name and size are + // ignored. + if (key == "ESIZES") + { + std::vector<std::string> prop_size = BaseLib::splitString(line); + + if (names.size() != prop_size.size()) + { + ERR("Error: Number of PROPERTY-names (%d) does not match " + "number of ESIZES (%d)", names.size(), prop_size.size()); + return false; + } + std::size_t const n_names (names.size()); + for (std::size_t i = 1; i < n_names; ++i) + { + mesh_prop.createNewPropertyVector<double>( + names[i], + MeshLib::MeshItemType::Node, + BaseLib::str2number<std::size_t>(prop_size[i])); + } + } + // Remember current position in case the properties black ends now. + pos = in.tellg(); + } + ERR("%s", eof_error.c_str()); + return false; +} + +bool GocadTSurfaceReader::parseSurface( + std::ifstream& in, + std::vector<MeshLib::Node*>& nodes, + std::vector<MeshLib::Element*>& elems, + std::map<std::size_t, std::size_t>& node_id_map, + MeshLib::Properties& mesh_prop) +{ + if (!parseNodes(in, nodes, node_id_map, mesh_prop)) + return false; + if (!parseElements(in, nodes, elems, node_id_map, mesh_prop)) + return false; + + std::string line(""); + while (std::getline(in, line)) + { + std::vector<std::string> str = BaseLib::splitString(line); + if (str[0] == "TFACE") + { + parseSurface(in, nodes, elems, node_id_map, mesh_prop); + return true; + } + else if (str[0] == "BSTONE") + { + // borderstone definition - currently ignored + } + else if (str[0] == "BORDER") + { + // border tracking direction - currently ignored + } + else if (line == "END") + { + return true; + } + else + { + WARN( + "GocadTSurfaceReader::parseSurface() - Unknown keyword found: " + "%s", + line.c_str()); + } + } + ERR("%s", eof_error.c_str()); + return false; +} + +MeshLib::Node* createNode(std::stringstream& sstr) +{ + std::string keyword; + std::size_t id; + std::array<double, 3> data; + sstr >> keyword >> id >> data[0] >> data[1] >> data[2]; + return new MeshLib::Node(data, id); +} + +bool GocadTSurfaceReader::parseNodes( + std::ifstream& in, + std::vector<MeshLib::Node*>& nodes, + std::map<std::size_t, std::size_t>& node_id_map, + MeshLib::Properties& mesh_prop) +{ + NODE_TYPE t = NODE_TYPE::UNSPECIFIED; + double value; + std::vector<std::string> const array_names = + mesh_prop.getPropertyVectorNames(); + std::streampos pos = in.tellg(); + std::string line(""); + while (std::getline(in, line)) + { + std::vector<std::string> str = BaseLib::splitString(line); + if (line.substr(0, 4) == "TRGL") + { + in.seekg(pos); + return true; + } + + if (line.empty() || isCommentLine(line)) + continue; + if (!(line.substr(0, 4) == "VRTX" || line.substr(0, 5) == "PVRTX" || + line.substr(0, 4) == "ATOM")) + { + WARN( + "GocadTSurfaceReader::parseNodes() - Unknown keyword found: %s", + line.c_str()); + continue; + } + + std::stringstream sstr(line); + if (line.substr(0, 4) == "VRTX" && t != NODE_TYPE::PVRTX) + { + nodes.push_back(createNode(sstr)); + } + else if (line.substr(0, 5) == "PVRTX" && t != NODE_TYPE::VRTX) + { + nodes.push_back(createNode(sstr)); + for (std::string name : array_names) + { + if (name == mat_id_name) + continue; + sstr >> value; + mesh_prop.getPropertyVector<double>(name)->push_back(value); + } + } + else if (line.substr(0, 4) == "ATOM") + { + std::size_t new_id, ref_id; + std::string keyword; + sstr >> keyword >> new_id >> ref_id; + nodes.push_back(new MeshLib::Node(nodes[ref_id]->getCoords(), new_id)); + } + node_id_map[nodes.back()->getID()] = nodes.size() - 1; + pos = in.tellg(); + } + ERR("%s", eof_error.c_str()); + return false; +} + +bool GocadTSurfaceReader::parseElements( + std::ifstream& in, + std::vector<MeshLib::Node*>& nodes, + std::vector<MeshLib::Element*>& elems, + std::map<std::size_t, std::size_t> const& node_id_map, + MeshLib::Properties& mesh_prop) +{ + std::string keyword; + std::array<std::size_t, 3> data; + MeshLib::PropertyVector<int>& mat_ids = + *mesh_prop.getPropertyVector<int>(mat_id_name); + int current_mat_id(0); + if (!mat_ids.empty()) + current_mat_id = (*std::max_element(mat_ids.begin(), mat_ids.end()))++; + std::streampos pos = in.tellg(); + std::size_t id(0); + std::string line(""); + while (std::getline(in, line)) + { + if (line.empty() || isCommentLine(line)) + continue; + if (line.substr(0, 4) == "TRGL") + { + std::stringstream sstr(line); + sstr >> keyword >> data[0] >> data[1] >> data[2]; + std::array<MeshLib::Node*, 3> elem_nodes; + for (std::size_t i = 0; i < 3; ++i) + { + auto const it = node_id_map.find(data[i]); + if (it == node_id_map.end() || it->second >= nodes.size()) + { + ERR("Error: Node ID (%d) out of range (0, %d).", data[i], + nodes.back()->getID()); + return false; + } + elem_nodes[i] = nodes[it->second]; + } + elems.push_back(new MeshLib::Tri(elem_nodes, id++)); + mat_ids.push_back(current_mat_id); + } + else + { + in.seekg(pos); + return true; + } + pos = in.tellg(); + } + ERR("%s", eof_error.c_str()); + return false; +} + +bool GocadTSurfaceReader::skipModel3d(std::ifstream& in) const +{ + std::string line(""); + while (std::getline(in, line)) + { + if (line == "END") + return true; + } + ERR("%s", eof_error.c_str()); + return false; +} + +void GocadTSurfaceReader::clearData(std::vector<MeshLib::Node*>& nodes, + std::vector<MeshLib::Element*>& elems) +{ + for (MeshLib::Element* e : elems) + delete e; + for (MeshLib::Node* n : nodes) + delete n; +} + +} // end namespace Gocad +} // end namespace FileIO diff --git a/Applications/FileIO/GocadIO/GocadTSurfaceReader.h b/Applications/FileIO/GocadIO/GocadTSurfaceReader.h new file mode 100644 index 0000000000000000000000000000000000000000..16c374ac95f8608891b6959a51eb0857dc6f437a --- /dev/null +++ b/Applications/FileIO/GocadIO/GocadTSurfaceReader.h @@ -0,0 +1,126 @@ +/** + * + * @copyright + * Copyright (c) 2012-2019, OpenGeoSys Community (http://www.opengeosys.org) + * Distributed under a Modified BSD License. + * See accompanying file LICENSE.txt or + * http://www.opengeosys.org/LICENSE.txt + */ + +#pragma once + +#include <iostream> +#include <memory> +#include <string> + +#include "MeshLib/Properties.h" + +namespace MeshLib +{ + class Mesh; + class Node; + class Element; +} + +namespace FileIO +{ +namespace Gocad +{ +class GocadTSurfaceReader final +{ +public: + /** + * Constructor takes as argument the Gocad .sg text file. + * @param fname file name + */ + explicit GocadTSurfaceReader(std::string const& fname); + + GocadTSurfaceReader() = delete; + GocadTSurfaceReader(GocadTSurfaceReader&& src) = delete; + GocadTSurfaceReader(GocadTSurfaceReader const& src) = delete; + GocadTSurfaceReader& operator=(GocadTSurfaceReader&& rhs) = delete; + GocadTSurfaceReader& operator=(GocadTSurfaceReader const& rhs) = delete; + + /// Reads the specified file and writes data into internal mesh vector + bool readFile(); + + /// Returns the specified mesh from the internal mesh vector + MeshLib::Mesh* getData(std::size_t const idx) const; + + /// Returns the complete mesh vector + std::vector<MeshLib::Mesh*> getData() const; + + /// Returns the name of the specified mesh + std::string getMeshName(std::size_t idx) const; + + /// Returns the number of meshes in the mesh vector + std::size_t getNumberOfMeshes() const { return _mesh_vec.size(); }; + + /// Writes one mesh to the specified file + void writeData(std::string const& file_name, std::size_t const idx, + bool write_binary) const; + + /// Writes all meshes into the specified directory + void writeData(std::string const& dir, bool write_binary = false) const; + +private: + /// Reads one mesh contained in the file (there may be more than one!) + bool readMesh(std::ifstream& in); + + /// Checks if the current line is a comment + bool isCommentLine(std::string const& str) const; + + /// Checks if a TSurf identifier is found at the current stream position. + bool TSurfaceFound(std::ifstream& in) const; + + /// Parses the HEADER section (everything except the name is ignored right now) + bool parseHeader(std::ifstream& in, std::string& mesh_name); + + /// Reads PROPERTY_CLASS_HEADER sections of the file. + /// All this information is currently ignored. + bool parsePropertyClass(std::ifstream& in) const; + + /// Parses information of node properties. + /// Only property names and array sizes are currently used. + bool parseProperties(std::ifstream& in, + std::vector<std::string> const& names, + MeshLib::Properties& mesh_prop); + + /// Parses the surface information (nodes, triangles, properties) + bool parseSurface(std::ifstream& in, std::vector<MeshLib::Node*>& nodes, + std::vector<MeshLib::Element*>& elems, + std::map<std::size_t, std::size_t>& node_id_map, + MeshLib::Properties& mesh_prop); + + /// Parses the node data for the current mesh + bool parseNodes(std::ifstream& in, std::vector<MeshLib::Node*>& nodes, + std::map<std::size_t, std::size_t>& node_id_map, + MeshLib::Properties& mesh_prop); + + /// Parses the element data for the current mesh + bool parseElements(std::ifstream& in, std::vector<MeshLib::Node*>& nodes, + std::vector<MeshLib::Element*>& elems, + std::map<std::size_t, std::size_t> const& node_id_map, + MeshLib::Properties& mesh_prop); + + /// Skips over the Model3d sections of the file, should there be any. + bool skipModel3d(std::ifstream& in) const; + + /// Clears the memory if an error occured + void clearData(std::vector<MeshLib::Node*>& nodes, + std::vector<MeshLib::Element*>& elems); + + enum class NODE_TYPE + { + UNSPECIFIED, + VRTX, + PVRTX + }; + + std::vector<MeshLib::Mesh*> _mesh_vec; + std::string _file_name; + +}; // end class GocadTSurfaceReader + +} // end namespace Gocad +} // end namespace FileIO