// based on MSH reader from PyMesh // Copyright (c) 2015 Qingnan Zhou // Copyright (C) 2020 Vladimir Fonov // // This Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed // with this file, You can obtain one at http://mozilla.org/MPL/2.0/. #include "MshLoader.h" #include #include #include #include #include namespace igl { // helper function void inline _msh_eat_white_space(std::ifstream& fin) { char next = fin.peek(); while (next == '\n' || next == ' ' || next == '\t' || next == '\r') { fin.get(); next = fin.peek(); } } } IGL_INLINE igl::MshLoader::MshLoader(const std::string &filename) { std::ifstream fin(filename, std::ios::in | std::ios::binary); if (!fin.is_open()) { std::stringstream err_msg; err_msg << "failed to open file \"" << filename << "\""; throw std::ios_base::failure(err_msg.str()); } // Parse header std::string buf; double version; int type; fin >> buf; if (buf != "$MeshFormat") { throw std::runtime_error("Unexpected .msh format"); } fin >> version >> type >> m_data_size; m_binary = (type == 1); if(version>2.2 || version<2.0) { // probably unsupported version std::stringstream err_msg; err_msg << "Error: Unsupported file version:" << version << std::endl; throw std::runtime_error(err_msg.str()); } // Some sanity check. if (m_data_size != 8) { std::stringstream err_msg; err_msg << "Error: data size must be 8 bytes." << std::endl; throw std::runtime_error(err_msg.str()); } if (sizeof(int) != 4) { std::stringstream err_msg; err_msg << "Error: code must be compiled with int size 4 bytes." << std::endl; throw std::runtime_error(err_msg.str()); } // Read in extra info from binary header. if (m_binary) { int one; igl::_msh_eat_white_space(fin); fin.read(reinterpret_cast(&one), sizeof(int)); if (one != 1) { std::stringstream err_msg; err_msg << "Binary msh file " << filename << " is saved with different endianness than this machine." << std::endl; throw std::runtime_error(err_msg.str()); } } fin >> buf; if (buf != "$EndMeshFormat") { std::stringstream err_msg; err_msg << "Unexpected contents in the file header." << std::endl; throw std::runtime_error(err_msg.str()); } while (!fin.eof()) { buf.clear(); fin >> buf; if (buf == "$Nodes") { parse_nodes(fin); fin >> buf; if (buf != "$EndNodes") { throw std::runtime_error("Unexpected tag"); } } else if (buf == "$Elements") { parse_elements(fin); fin >> buf; if (buf != "$EndElements") { throw std::runtime_error("Unexpected tag"); } } else if (buf == "$NodeData") { parse_node_field(fin); fin >> buf; if (buf != "$EndNodeData") { throw std::runtime_error("Unexpected tag"); } } else if (buf == "$ElementData") { parse_element_field(fin); fin >> buf; if (buf != "$EndElementData") { throw std::runtime_error("Unexpected tag"); } } else if (fin.eof()) { break; } else { parse_unknown_field(fin, buf); } } fin.close(); } IGL_INLINE void igl::MshLoader::parse_nodes(std::ifstream& fin) { size_t num_nodes; fin >> num_nodes; m_nodes.resize(num_nodes*3); if (m_binary) { size_t stride = (4+3*m_data_size); size_t num_bytes = stride * num_nodes; char* data = new char[num_bytes]; igl::_msh_eat_white_space(fin); fin.read(data, num_bytes); for (size_t i=0; i> node_idx; node_idx -= 1; // here it's 3D node explicitly fin >> m_nodes[node_idx*3] >> m_nodes[node_idx*3+1] >> m_nodes[node_idx*3+2]; } } } IGL_INLINE void igl::MshLoader::parse_elements(std::ifstream& fin) { m_elements_tags.resize(2); //hardcoded to have 2 tags size_t num_elements; fin >> num_elements; size_t nodes_per_element; if (m_binary) { igl::_msh_eat_white_space(fin); int elem_read = 0; while (elem_read < num_elements) { // Parse element header. int elem_type, num_elems, num_tags; fin.read((char*)&elem_type, sizeof(int)); fin.read((char*)&num_elems, sizeof(int)); fin.read((char*)&num_tags, sizeof(int)); nodes_per_element = num_nodes_per_elem_type(elem_type); // store node info for (size_t i=0; i> elem_num >> elem_type >> num_tags; // read tags. for (size_t j=0; j> tag; if(j<2) m_elements_tags[j].push_back(tag); } for (size_t j=num_tags; j<2; j++) m_elements_tags[j].push_back(-1); // fill up tags if less then 2 nodes_per_element = num_nodes_per_elem_type(elem_type); m_elements_types.push_back(elem_type); m_elements_lengths.push_back(nodes_per_element); elem_num -= 1; m_elements_ids.push_back(elem_num); m_elements_nodes_idx.push_back(m_elements.size()); // Parse node idx. for (size_t j=0; j> idx; m_elements.push_back(idx-1); // msh index starts from 1. } } } // debug assert(m_elements_types.size() == m_elements_ids.size()); assert(m_elements_tags[0].size() == m_elements_ids.size()); assert(m_elements_tags[1].size() == m_elements_ids.size()); assert(m_elements_lengths.size() == m_elements_ids.size()); } IGL_INLINE void igl::MshLoader::parse_node_field( std::ifstream& fin ) { size_t num_string_tags; size_t num_real_tags; size_t num_int_tags; fin >> num_string_tags; std::vector str_tags(num_string_tags); for (size_t i=0; i> str_tags[i]; } } fin >> num_real_tags; std::vector real_tags(num_real_tags); for (size_t i=0; i> real_tags[i]; fin >> num_int_tags; std::vector int_tags(num_int_tags); for (size_t i=0; i> int_tags[i]; if (num_string_tags <= 0 || num_int_tags <= 2) { throw std::runtime_error("Unexpected number of field tags"); } std::string fieldname = str_tags[0]; int num_components = int_tags[1]; int num_entries = int_tags[2]; std::vector field( num_entries*num_components ); if (m_binary) { size_t num_bytes = (num_components * m_data_size + 4) * num_entries; char* data = new char[num_bytes]; igl::_msh_eat_white_space(fin); fin.read(data, num_bytes); for (size_t i=0; i=num_entries) throw std::runtime_error("Index too big"); size_t base_idx = i*(4+num_components*m_data_size) + 4; // TODO: make this work when m_data_size != sizeof(double) ? memcpy(&field[node_idx*num_components], &data[base_idx], num_components*m_data_size); } delete [] data; } else { int node_idx; for (size_t i=0; i> node_idx; node_idx -= 1; for (size_t j=0; j> field[node_idx*num_components+j]; } } } m_node_fields_names.push_back(fieldname); m_node_fields.push_back(field); m_node_fields_components.push_back(num_components); } IGL_INLINE void igl::MshLoader::parse_element_field(std::ifstream& fin) { size_t num_string_tags; size_t num_real_tags; size_t num_int_tags; fin >> num_string_tags; std::vector str_tags(num_string_tags); for (size_t i=0; i> str_tags[i]; } } fin >> num_real_tags; std::vector real_tags(num_real_tags); for (size_t i=0; i> real_tags[i]; fin >> num_int_tags; std::vector int_tags(num_int_tags); for (size_t i=0; i> int_tags[i]; if (num_string_tags <= 0 || num_int_tags <= 2) { throw std::runtime_error("Invalid file format"); } std::string fieldname = str_tags[0]; int num_components = int_tags[1]; int num_entries = int_tags[2]; std::vector field(num_entries*num_components); if (m_binary) { size_t num_bytes = (num_components * m_data_size + 4) * num_entries; char* data = new char[num_bytes]; igl::_msh_eat_white_space(fin); fin.read(data, num_bytes); for (int i=0; i> elem_idx; elem_idx -= 1; for (size_t j=0; j> field[elem_idx*num_components+j]; } } } m_element_fields_names.push_back(fieldname); m_element_fields.push_back(field); m_element_fields_components.push_back(num_components); } IGL_INLINE void igl::MshLoader::parse_unknown_field(std::ifstream& fin, const std::string& fieldname) { std::cerr << "Warning: \"" << fieldname << "\" not supported yet. Ignored." << std::endl; std::string endmark = fieldname.substr(0,1) + "End" + fieldname.substr(1,fieldname.size()-1); std::string buf(""); while (buf != endmark && !fin.eof()) { fin >> buf; } } IGL_INLINE int igl::MshLoader::num_nodes_per_elem_type(int elem_type) { int nodes_per_element = 0; switch (elem_type) { case ELEMENT_LINE: // 2-node line nodes_per_element = 2; break; case ELEMENT_TRI: nodes_per_element = 3; // 3-node triangle break; case ELEMENT_QUAD: nodes_per_element = 4; // 5-node quad break; case ELEMENT_TET: nodes_per_element = 4; // 4-node tetrahedra break; case ELEMENT_HEX: // 8-node hexahedron nodes_per_element = 8; break; case ELEMENT_PRISM: // 6-node prism nodes_per_element = 6; break; case ELEMENT_LINE_2ND_ORDER: nodes_per_element = 3; break; case ELEMENT_TRI_2ND_ORDER: nodes_per_element = 6; break; case ELEMENT_QUAD_2ND_ORDER: nodes_per_element = 9; break; case ELEMENT_TET_2ND_ORDER: nodes_per_element = 10; break; case ELEMENT_HEX_2ND_ORDER: nodes_per_element = 27; break; case ELEMENT_PRISM_2ND_ORDER: nodes_per_element = 18; break; case ELEMENT_PYRAMID_2ND_ORDER: nodes_per_element = 14; break; case ELEMENT_POINT: // 1-node point nodes_per_element = 1; break; default: std::stringstream err_msg; err_msg << "Element type (" << elem_type << ") is not supported yet." << std::endl; throw std::runtime_error(err_msg.str()); } return nodes_per_element; } IGL_INLINE bool igl::MshLoader::is_element_map_identity() const { for(int i=0;i( msh_struct( m_elements_tags[tag_column][i], m_elements_types[i]), i) ); } // identify unique structures std::vector _unique_structs; std::unique_copy(std::begin(m_structure_index), std::end(m_structure_index), std::back_inserter(_unique_structs), [](const StructIndex::value_type &c1, const StructIndex::value_type &c2) { return c1.first == c2.first; }); std::for_each( _unique_structs.begin(), _unique_structs.end(), [this](const StructIndex::value_type &n){ this->m_structures.push_back(n.first); }); for(auto t = m_structures.begin(); t != m_structures.end(); ++t) { // identify all elements corresponding to this tag auto structure_range = m_structure_index.equal_range( *t ); int cnt=0; for(auto i=structure_range.first; i!=structure_range.second; i++) cnt++; m_structure_length.insert( std::pair( *t, cnt)); } }