| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497 |
- // based on MSH reader from PyMesh
- // Copyright (c) 2015 Qingnan Zhou <[email protected]>
- // Copyright (C) 2020 Vladimir Fonov <[email protected]>
- //
- // 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 <cassert>
- #include <iostream>
- #include <sstream>
- #include <vector>
- #include <string.h>
- 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<char*>(&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<num_nodes; i++) {
- int node_idx;
- memcpy(&node_idx, data+i*stride, sizeof(int));
- node_idx-=1;
- // directly move into vector storage
- // this works only when m_data_size==sizeof(Float)==sizeof(double)
- memcpy(&m_nodes[node_idx*3], data+i*stride + 4, m_data_size*3);
- }
- delete [] data;
- } else {
- int node_idx;
- for (size_t i=0; i<num_nodes; i++) {
- fin >> 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<num_elems; i++) {
- int elem_idx;
- // all elements in the segment share the same elem_type and number of nodes per element
- m_elements_types.push_back(elem_type);
- m_elements_lengths.push_back(nodes_per_element);
- fin.read((char*)&elem_idx, sizeof(int));
- elem_idx -= 1;
- m_elements_ids.push_back(elem_idx);
- // read first two tags
- for (size_t j=0; j<num_tags; j++) {
- int tag;
- fin.read((char*)&tag, sizeof(int));
- 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
- m_elements_nodes_idx.push_back(m_elements.size());
- // Element values.
- for (size_t j=0; j<nodes_per_element; j++) {
- int idx;
- fin.read((char*)&idx, sizeof(int));
-
- m_elements.push_back(idx-1);
- }
- }
- elem_read += num_elems;
- }
- } else {
- for (size_t i=0; i<num_elements; i++) {
- // Parse per element header
- int elem_num, elem_type, num_tags;
- fin >> elem_num >> elem_type >> num_tags;
- // read tags.
- for (size_t j=0; j<num_tags; j++) {
- int tag;
- fin >> 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<nodes_per_element; j++) {
- int idx;
- fin >> 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<std::string> str_tags(num_string_tags);
- for (size_t i=0; i<num_string_tags; i++) {
- igl::_msh_eat_white_space(fin);
- if (fin.peek() == '\"') {
- // Handle field name between quotes.
- char buf[128];
- fin.get(); // remove the quote at the beginning.
- fin.getline(buf, 128, '\"');
- str_tags[i] = std::string(buf);
- } else {
- fin >> str_tags[i];
- }
- }
- fin >> num_real_tags;
- std::vector<Float> real_tags(num_real_tags);
- for (size_t i=0; i<num_real_tags; i++)
- fin >> real_tags[i];
- fin >> num_int_tags;
- std::vector<int> int_tags(num_int_tags);
- for (size_t i=0; i<num_int_tags; i++)
- fin >> 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<Float> 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; i++) {
- int node_idx;
- memcpy(&node_idx,&data[i*(4+num_components*m_data_size)],4);
-
- if(node_idx<1) throw std::runtime_error("Negative or zero index");
- node_idx -= 1;
-
- if(node_idx>=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<num_entries; i++) {
- fin >> node_idx;
- node_idx -= 1;
- for (size_t j=0; j<num_components; j++) {
- fin >> 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<std::string> str_tags(num_string_tags);
- for (size_t i=0; i<num_string_tags; i++) {
- igl::_msh_eat_white_space(fin);
- if (fin.peek() == '\"') {
- // Handle field name between quoates.
- char buf[128];
- fin.get(); // remove the quote at the beginning.
- fin.getline(buf, 128, '\"');
- str_tags[i] = buf;
- } else {
- fin >> str_tags[i];
- }
- }
- fin >> num_real_tags;
- std::vector<Float> real_tags(num_real_tags);
- for (size_t i=0; i<num_real_tags; i++)
- fin >> real_tags[i];
- fin >> num_int_tags;
- std::vector<int> int_tags(num_int_tags);
- for (size_t i=0; i<num_int_tags; i++)
- fin >> 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<Float> 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<num_entries; i++) {
- int elem_idx;
- // works with sizeof(int)==4
- memcpy(&elem_idx, &data[i*(4+num_components*m_data_size)],4);
- elem_idx -= 1;
-
- // directly copy data into vector storage space
- memcpy(&field[elem_idx*num_components], &data[i*(4+num_components*m_data_size) + 4], m_data_size*num_components);
- }
- delete [] data;
- } else {
- int elem_idx;
- for (size_t i=0; i<num_entries; i++) {
- fin >> elem_idx;
- elem_idx -= 1;
- for (size_t j=0; j<num_components; j++) {
- fin >> 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<m_elements_ids.size();i++) {
- int id=m_elements_ids[i];
- if (id!=i) return false;
- }
- return true;
- }
- IGL_INLINE void igl::MshLoader::index_structures(int tag_column)
- {
- //cleanup
- m_structure_index.clear();
- m_structures.clear();
- m_structure_length.clear();
- //index structure tags
- for(auto i=0; i != m_elements_tags[tag_column].size(); ++i )
- {
- m_structure_index.insert(
- std::pair<msh_struct,int>(
- msh_struct( m_elements_tags[tag_column][i],
- m_elements_types[i]), i)
- );
- }
- // identify unique structures
- std::vector<StructIndex::value_type> _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<msh_struct,int>( *t, cnt));
- }
- }
|