MshLoader.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497
  1. // based on MSH reader from PyMesh
  2. // Copyright (c) 2015 Qingnan Zhou <[email protected]>
  3. // Copyright (C) 2020 Vladimir Fonov <[email protected]>
  4. //
  5. // This Source Code Form is subject to the terms of the Mozilla
  6. // Public License v. 2.0. If a copy of the MPL was not distributed
  7. // with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
  8. #include "MshLoader.h"
  9. #include <cassert>
  10. #include <iostream>
  11. #include <sstream>
  12. #include <vector>
  13. #include <string.h>
  14. namespace igl {
  15. // helper function
  16. void inline _msh_eat_white_space(std::ifstream& fin) {
  17. char next = fin.peek();
  18. while (next == '\n' || next == ' ' || next == '\t' || next == '\r') {
  19. fin.get();
  20. next = fin.peek();
  21. }
  22. }
  23. }
  24. IGL_INLINE igl::MshLoader::MshLoader(const std::string &filename) {
  25. std::ifstream fin(filename, std::ios::in | std::ios::binary);
  26. if (!fin.is_open()) {
  27. std::stringstream err_msg;
  28. err_msg << "failed to open file \"" << filename << "\"";
  29. throw std::ios_base::failure(err_msg.str());
  30. }
  31. // Parse header
  32. std::string buf;
  33. double version;
  34. int type;
  35. fin >> buf;
  36. if (buf != "$MeshFormat") { throw std::runtime_error("Unexpected .msh format"); }
  37. fin >> version >> type >> m_data_size;
  38. m_binary = (type == 1);
  39. if(version>2.2 || version<2.0)
  40. {
  41. // probably unsupported version
  42. std::stringstream err_msg;
  43. err_msg << "Error: Unsupported file version:" << version << std::endl;
  44. throw std::runtime_error(err_msg.str());
  45. }
  46. // Some sanity check.
  47. if (m_data_size != 8) {
  48. std::stringstream err_msg;
  49. err_msg << "Error: data size must be 8 bytes." << std::endl;
  50. throw std::runtime_error(err_msg.str());
  51. }
  52. if (sizeof(int) != 4) {
  53. std::stringstream err_msg;
  54. err_msg << "Error: code must be compiled with int size 4 bytes." << std::endl;
  55. throw std::runtime_error(err_msg.str());
  56. }
  57. // Read in extra info from binary header.
  58. if (m_binary) {
  59. int one;
  60. igl::_msh_eat_white_space(fin);
  61. fin.read(reinterpret_cast<char*>(&one), sizeof(int));
  62. if (one != 1) {
  63. std::stringstream err_msg;
  64. err_msg << "Binary msh file " << filename
  65. << " is saved with different endianness than this machine."
  66. << std::endl;
  67. throw std::runtime_error(err_msg.str());
  68. }
  69. }
  70. fin >> buf;
  71. if (buf != "$EndMeshFormat")
  72. {
  73. std::stringstream err_msg;
  74. err_msg << "Unexpected contents in the file header." << std::endl;
  75. throw std::runtime_error(err_msg.str());
  76. }
  77. while (!fin.eof()) {
  78. buf.clear();
  79. fin >> buf;
  80. if (buf == "$Nodes") {
  81. parse_nodes(fin);
  82. fin >> buf;
  83. if (buf != "$EndNodes") { throw std::runtime_error("Unexpected tag"); }
  84. } else if (buf == "$Elements") {
  85. parse_elements(fin);
  86. fin >> buf;
  87. if (buf != "$EndElements") { throw std::runtime_error("Unexpected tag"); }
  88. } else if (buf == "$NodeData") {
  89. parse_node_field(fin);
  90. fin >> buf;
  91. if (buf != "$EndNodeData") { throw std::runtime_error("Unexpected tag"); }
  92. } else if (buf == "$ElementData") {
  93. parse_element_field(fin);
  94. fin >> buf;
  95. if (buf != "$EndElementData") { throw std::runtime_error("Unexpected tag"); }
  96. } else if (fin.eof()) {
  97. break;
  98. } else {
  99. parse_unknown_field(fin, buf);
  100. }
  101. }
  102. fin.close();
  103. }
  104. IGL_INLINE void igl::MshLoader::parse_nodes(std::ifstream& fin) {
  105. size_t num_nodes;
  106. fin >> num_nodes;
  107. m_nodes.resize(num_nodes*3);
  108. if (m_binary) {
  109. size_t stride = (4+3*m_data_size);
  110. size_t num_bytes = stride * num_nodes;
  111. char* data = new char[num_bytes];
  112. igl::_msh_eat_white_space(fin);
  113. fin.read(data, num_bytes);
  114. for (size_t i=0; i<num_nodes; i++) {
  115. int node_idx;
  116. memcpy(&node_idx, data+i*stride, sizeof(int));
  117. node_idx-=1;
  118. // directly move into vector storage
  119. // this works only when m_data_size==sizeof(Float)==sizeof(double)
  120. memcpy(&m_nodes[node_idx*3], data+i*stride + 4, m_data_size*3);
  121. }
  122. delete [] data;
  123. } else {
  124. int node_idx;
  125. for (size_t i=0; i<num_nodes; i++) {
  126. fin >> node_idx;
  127. node_idx -= 1;
  128. // here it's 3D node explicitly
  129. fin >> m_nodes[node_idx*3]
  130. >> m_nodes[node_idx*3+1]
  131. >> m_nodes[node_idx*3+2];
  132. }
  133. }
  134. }
  135. IGL_INLINE void igl::MshLoader::parse_elements(std::ifstream& fin) {
  136. m_elements_tags.resize(2); //hardcoded to have 2 tags
  137. size_t num_elements;
  138. fin >> num_elements;
  139. size_t nodes_per_element;
  140. if (m_binary) {
  141. igl::_msh_eat_white_space(fin);
  142. int elem_read = 0;
  143. while (elem_read < num_elements) {
  144. // Parse element header.
  145. int elem_type, num_elems, num_tags;
  146. fin.read((char*)&elem_type, sizeof(int));
  147. fin.read((char*)&num_elems, sizeof(int));
  148. fin.read((char*)&num_tags, sizeof(int));
  149. nodes_per_element = num_nodes_per_elem_type(elem_type);
  150. // store node info
  151. for (size_t i=0; i<num_elems; i++) {
  152. int elem_idx;
  153. // all elements in the segment share the same elem_type and number of nodes per element
  154. m_elements_types.push_back(elem_type);
  155. m_elements_lengths.push_back(nodes_per_element);
  156. fin.read((char*)&elem_idx, sizeof(int));
  157. elem_idx -= 1;
  158. m_elements_ids.push_back(elem_idx);
  159. // read first two tags
  160. for (size_t j=0; j<num_tags; j++) {
  161. int tag;
  162. fin.read((char*)&tag, sizeof(int));
  163. if(j<2) m_elements_tags[j].push_back(tag);
  164. }
  165. for (size_t j=num_tags; j<2; j++)
  166. m_elements_tags[j].push_back(-1); // fill up tags if less then 2
  167. m_elements_nodes_idx.push_back(m_elements.size());
  168. // Element values.
  169. for (size_t j=0; j<nodes_per_element; j++) {
  170. int idx;
  171. fin.read((char*)&idx, sizeof(int));
  172. m_elements.push_back(idx-1);
  173. }
  174. }
  175. elem_read += num_elems;
  176. }
  177. } else {
  178. for (size_t i=0; i<num_elements; i++) {
  179. // Parse per element header
  180. int elem_num, elem_type, num_tags;
  181. fin >> elem_num >> elem_type >> num_tags;
  182. // read tags.
  183. for (size_t j=0; j<num_tags; j++) {
  184. int tag;
  185. fin >> tag;
  186. if(j<2) m_elements_tags[j].push_back(tag);
  187. }
  188. for (size_t j=num_tags; j<2; j++)
  189. m_elements_tags[j].push_back(-1); // fill up tags if less then 2
  190. nodes_per_element = num_nodes_per_elem_type(elem_type);
  191. m_elements_types.push_back(elem_type);
  192. m_elements_lengths.push_back(nodes_per_element);
  193. elem_num -= 1;
  194. m_elements_ids.push_back(elem_num);
  195. m_elements_nodes_idx.push_back(m_elements.size());
  196. // Parse node idx.
  197. for (size_t j=0; j<nodes_per_element; j++) {
  198. int idx;
  199. fin >> idx;
  200. m_elements.push_back(idx-1); // msh index starts from 1.
  201. }
  202. }
  203. }
  204. // debug
  205. assert(m_elements_types.size() == m_elements_ids.size());
  206. assert(m_elements_tags[0].size() == m_elements_ids.size());
  207. assert(m_elements_tags[1].size() == m_elements_ids.size());
  208. assert(m_elements_lengths.size() == m_elements_ids.size());
  209. }
  210. IGL_INLINE void igl::MshLoader::parse_node_field( std::ifstream& fin ) {
  211. size_t num_string_tags;
  212. size_t num_real_tags;
  213. size_t num_int_tags;
  214. fin >> num_string_tags;
  215. std::vector<std::string> str_tags(num_string_tags);
  216. for (size_t i=0; i<num_string_tags; i++) {
  217. igl::_msh_eat_white_space(fin);
  218. if (fin.peek() == '\"') {
  219. // Handle field name between quotes.
  220. char buf[128];
  221. fin.get(); // remove the quote at the beginning.
  222. fin.getline(buf, 128, '\"');
  223. str_tags[i] = std::string(buf);
  224. } else {
  225. fin >> str_tags[i];
  226. }
  227. }
  228. fin >> num_real_tags;
  229. std::vector<Float> real_tags(num_real_tags);
  230. for (size_t i=0; i<num_real_tags; i++)
  231. fin >> real_tags[i];
  232. fin >> num_int_tags;
  233. std::vector<int> int_tags(num_int_tags);
  234. for (size_t i=0; i<num_int_tags; i++)
  235. fin >> int_tags[i];
  236. if (num_string_tags <= 0 || num_int_tags <= 2) {
  237. throw std::runtime_error("Unexpected number of field tags");
  238. }
  239. std::string fieldname = str_tags[0];
  240. int num_components = int_tags[1];
  241. int num_entries = int_tags[2];
  242. std::vector<Float> field( num_entries*num_components );
  243. if (m_binary) {
  244. size_t num_bytes = (num_components * m_data_size + 4) * num_entries;
  245. char* data = new char[num_bytes];
  246. igl::_msh_eat_white_space(fin);
  247. fin.read(data, num_bytes);
  248. for (size_t i=0; i<num_entries; i++) {
  249. int node_idx;
  250. memcpy(&node_idx,&data[i*(4+num_components*m_data_size)],4);
  251. if(node_idx<1) throw std::runtime_error("Negative or zero index");
  252. node_idx -= 1;
  253. if(node_idx>=num_entries) throw std::runtime_error("Index too big");
  254. size_t base_idx = i*(4+num_components*m_data_size) + 4;
  255. // TODO: make this work when m_data_size != sizeof(double) ?
  256. memcpy(&field[node_idx*num_components], &data[base_idx], num_components*m_data_size);
  257. }
  258. delete [] data;
  259. } else {
  260. int node_idx;
  261. for (size_t i=0; i<num_entries; i++) {
  262. fin >> node_idx;
  263. node_idx -= 1;
  264. for (size_t j=0; j<num_components; j++) {
  265. fin >> field[node_idx*num_components+j];
  266. }
  267. }
  268. }
  269. m_node_fields_names.push_back(fieldname);
  270. m_node_fields.push_back(field);
  271. m_node_fields_components.push_back(num_components);
  272. }
  273. IGL_INLINE void igl::MshLoader::parse_element_field(std::ifstream& fin) {
  274. size_t num_string_tags;
  275. size_t num_real_tags;
  276. size_t num_int_tags;
  277. fin >> num_string_tags;
  278. std::vector<std::string> str_tags(num_string_tags);
  279. for (size_t i=0; i<num_string_tags; i++) {
  280. igl::_msh_eat_white_space(fin);
  281. if (fin.peek() == '\"') {
  282. // Handle field name between quoates.
  283. char buf[128];
  284. fin.get(); // remove the quote at the beginning.
  285. fin.getline(buf, 128, '\"');
  286. str_tags[i] = buf;
  287. } else {
  288. fin >> str_tags[i];
  289. }
  290. }
  291. fin >> num_real_tags;
  292. std::vector<Float> real_tags(num_real_tags);
  293. for (size_t i=0; i<num_real_tags; i++)
  294. fin >> real_tags[i];
  295. fin >> num_int_tags;
  296. std::vector<int> int_tags(num_int_tags);
  297. for (size_t i=0; i<num_int_tags; i++)
  298. fin >> int_tags[i];
  299. if (num_string_tags <= 0 || num_int_tags <= 2) {
  300. throw std::runtime_error("Invalid file format");
  301. }
  302. std::string fieldname = str_tags[0];
  303. int num_components = int_tags[1];
  304. int num_entries = int_tags[2];
  305. std::vector<Float> field(num_entries*num_components);
  306. if (m_binary) {
  307. size_t num_bytes = (num_components * m_data_size + 4) * num_entries;
  308. char* data = new char[num_bytes];
  309. igl::_msh_eat_white_space(fin);
  310. fin.read(data, num_bytes);
  311. for (int i=0; i<num_entries; i++) {
  312. int elem_idx;
  313. // works with sizeof(int)==4
  314. memcpy(&elem_idx, &data[i*(4+num_components*m_data_size)],4);
  315. elem_idx -= 1;
  316. // directly copy data into vector storage space
  317. memcpy(&field[elem_idx*num_components], &data[i*(4+num_components*m_data_size) + 4], m_data_size*num_components);
  318. }
  319. delete [] data;
  320. } else {
  321. int elem_idx;
  322. for (size_t i=0; i<num_entries; i++) {
  323. fin >> elem_idx;
  324. elem_idx -= 1;
  325. for (size_t j=0; j<num_components; j++) {
  326. fin >> field[elem_idx*num_components+j];
  327. }
  328. }
  329. }
  330. m_element_fields_names.push_back(fieldname);
  331. m_element_fields.push_back(field);
  332. m_element_fields_components.push_back(num_components);
  333. }
  334. IGL_INLINE void igl::MshLoader::parse_unknown_field(std::ifstream& fin,
  335. const std::string& fieldname) {
  336. std::cerr << "Warning: \"" << fieldname << "\" not supported yet. Ignored." << std::endl;
  337. std::string endmark = fieldname.substr(0,1) + "End"
  338. + fieldname.substr(1,fieldname.size()-1);
  339. std::string buf("");
  340. while (buf != endmark && !fin.eof()) {
  341. fin >> buf;
  342. }
  343. }
  344. IGL_INLINE int igl::MshLoader::num_nodes_per_elem_type(int elem_type) {
  345. int nodes_per_element = 0;
  346. switch (elem_type) {
  347. case ELEMENT_LINE: // 2-node line
  348. nodes_per_element = 2;
  349. break;
  350. case ELEMENT_TRI:
  351. nodes_per_element = 3; // 3-node triangle
  352. break;
  353. case ELEMENT_QUAD:
  354. nodes_per_element = 4; // 5-node quad
  355. break;
  356. case ELEMENT_TET:
  357. nodes_per_element = 4; // 4-node tetrahedra
  358. break;
  359. case ELEMENT_HEX: // 8-node hexahedron
  360. nodes_per_element = 8;
  361. break;
  362. case ELEMENT_PRISM: // 6-node prism
  363. nodes_per_element = 6;
  364. break;
  365. case ELEMENT_LINE_2ND_ORDER:
  366. nodes_per_element = 3;
  367. break;
  368. case ELEMENT_TRI_2ND_ORDER:
  369. nodes_per_element = 6;
  370. break;
  371. case ELEMENT_QUAD_2ND_ORDER:
  372. nodes_per_element = 9;
  373. break;
  374. case ELEMENT_TET_2ND_ORDER:
  375. nodes_per_element = 10;
  376. break;
  377. case ELEMENT_HEX_2ND_ORDER:
  378. nodes_per_element = 27;
  379. break;
  380. case ELEMENT_PRISM_2ND_ORDER:
  381. nodes_per_element = 18;
  382. break;
  383. case ELEMENT_PYRAMID_2ND_ORDER:
  384. nodes_per_element = 14;
  385. break;
  386. case ELEMENT_POINT: // 1-node point
  387. nodes_per_element = 1;
  388. break;
  389. default:
  390. std::stringstream err_msg;
  391. err_msg << "Element type (" << elem_type << ") is not supported yet."
  392. << std::endl;
  393. throw std::runtime_error(err_msg.str());
  394. }
  395. return nodes_per_element;
  396. }
  397. IGL_INLINE bool igl::MshLoader::is_element_map_identity() const
  398. {
  399. for(int i=0;i<m_elements_ids.size();i++) {
  400. int id=m_elements_ids[i];
  401. if (id!=i) return false;
  402. }
  403. return true;
  404. }
  405. IGL_INLINE void igl::MshLoader::index_structures(int tag_column)
  406. {
  407. //cleanup
  408. m_structure_index.clear();
  409. m_structures.clear();
  410. m_structure_length.clear();
  411. //index structure tags
  412. for(auto i=0; i != m_elements_tags[tag_column].size(); ++i )
  413. {
  414. m_structure_index.insert(
  415. std::pair<msh_struct,int>(
  416. msh_struct( m_elements_tags[tag_column][i],
  417. m_elements_types[i]), i)
  418. );
  419. }
  420. // identify unique structures
  421. std::vector<StructIndex::value_type> _unique_structs;
  422. std::unique_copy(std::begin(m_structure_index),
  423. std::end(m_structure_index),
  424. std::back_inserter(_unique_structs),
  425. [](const StructIndex::value_type &c1, const StructIndex::value_type &c2)
  426. { return c1.first == c2.first; });
  427. std::for_each( _unique_structs.begin(), _unique_structs.end(),
  428. [this](const StructIndex::value_type &n){ this->m_structures.push_back(n.first); });
  429. for(auto t = m_structures.begin(); t != m_structures.end(); ++t)
  430. {
  431. // identify all elements corresponding to this tag
  432. auto structure_range = m_structure_index.equal_range( *t );
  433. int cnt=0;
  434. for(auto i=structure_range.first; i!=structure_range.second; i++)
  435. cnt++;
  436. m_structure_length.insert( std::pair<msh_struct,int>( *t, cnt));
  437. }
  438. }