readSTL.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. // This file is part of libigl, a simple c++ geometry processing library.
  2. //
  3. // Copyright (C) 2014 Alec Jacobson <[email protected]>
  4. // Copyright (C) 2018 Qingnan Zhou <[email protected]>
  5. // Copyright (C) 2020 Jérémie Dumas <[email protected]>
  6. //
  7. // This Source Code Form is subject to the terms of the Mozilla Public License
  8. // v. 2.0. If a copy of the MPL was not distributed with this file, You can
  9. // obtain one at http://mozilla.org/MPL/2.0/.
  10. #include "readSTL.h"
  11. #include "IGL_ASSERT.h"
  12. #include "list_to_matrix.h"
  13. #include "string_utils.h"
  14. #include "read_file_binary.h"
  15. #include "FileMemoryStream.h"
  16. #include <iostream>
  17. #include <cstdint>
  18. namespace igl {
  19. template <typename DerivedV, typename DerivedF, typename DerivedN>
  20. IGL_INLINE bool readSTL(std::istream &input,
  21. Eigen::PlainObjectBase<DerivedV> &V,
  22. Eigen::PlainObjectBase<DerivedF> &F,
  23. Eigen::PlainObjectBase<DerivedN> &N) {
  24. std::vector<std::array<typename DerivedV::Scalar, 3>> vV;
  25. std::vector<std::array<typename DerivedN::Scalar, 3>> vN;
  26. std::vector<std::array<typename DerivedF::Scalar, 3>> vF;
  27. if (!readSTL(input, vV, vF, vN)) {
  28. return false;
  29. }
  30. if (!list_to_matrix(vV, V)) {
  31. return false;
  32. }
  33. if (!list_to_matrix(vF, F)) {
  34. return false;
  35. }
  36. if (!list_to_matrix(vN, N)) {
  37. return false;
  38. }
  39. return true;
  40. }
  41. IGL_INLINE bool is_stl_binary(std::istream &input) {
  42. std::streampos start_pos = input.tellg();
  43. constexpr size_t HEADER_SIZE = 80;
  44. char header[HEADER_SIZE];
  45. input.read(header, HEADER_SIZE);
  46. if (!starts_with(header, "solid")) {
  47. input.seekg(start_pos);
  48. return true;
  49. }
  50. if (!input.good()) {
  51. input.seekg(start_pos);
  52. return false;
  53. }
  54. // Check if filesize matches the number of faces claimed.
  55. char buf[4];
  56. input.read(buf, 4);
  57. size_t num_faces = *reinterpret_cast<std::uint32_t *>(buf);
  58. input.seekg(0, input.end);
  59. size_t file_size = input.tellg();
  60. input.seekg(start_pos);
  61. if (file_size == 80 + 4 + (4 * 12 + 2) * num_faces) {
  62. return true;
  63. } else {
  64. return false;
  65. }
  66. }
  67. template <typename TypeV, typename TypeF, typename TypeN>
  68. IGL_INLINE bool read_stl_ascii(std::istream &input,
  69. std::vector<std::array<TypeV, 3>> &V,
  70. std::vector<std::array<TypeF, 3>> &F,
  71. std::vector<std::array<TypeN, 3>> &N) {
  72. constexpr size_t LINE_SIZE = 256;
  73. char line[LINE_SIZE];
  74. bool success = true;
  75. if (!input) {
  76. throw std::runtime_error("Failed to open file");
  77. }
  78. // skip header line.
  79. input.getline(line, LINE_SIZE);
  80. auto parse_ascii_normal = [&N](const char *line) {
  81. double x, y, z;
  82. size_t n = sscanf(line, " facet normal %lf %lf %lf", &x, &y, &z);
  83. IGL_ASSERT(n == 3);
  84. if (n != 3) {
  85. return false;
  86. }
  87. N.push_back({{static_cast<TypeN>(x), static_cast<TypeN>(y),
  88. static_cast<TypeN>(z)}});
  89. return true;
  90. };
  91. auto parse_ascii_vertex = [&V](const char *line) {
  92. double x, y, z;
  93. size_t n = sscanf(line, " vertex %lf %lf %lf", &x, &y, &z);
  94. IGL_ASSERT(n == 3);
  95. if (n != 3) {
  96. return false;
  97. }
  98. V.push_back({{static_cast<TypeV>(x), static_cast<TypeV>(y),
  99. static_cast<TypeV>(z)}});
  100. return true;
  101. };
  102. auto parse_ascii_facet = [&parse_ascii_vertex, &parse_ascii_normal](std::istream &fin) {
  103. constexpr size_t LINE_SIZE = 256;
  104. constexpr size_t WORD_SIZE = 128;
  105. char line[LINE_SIZE];
  106. char first_word[WORD_SIZE];
  107. const char *face_begin = "facet";
  108. const char *face_end = "endfacet";
  109. const char *loop_begin = "outer";
  110. const char *loop_end = "endloop";
  111. const char *vertex_flag = "vertex";
  112. bool reading_facet = false;
  113. bool reading_loop = false;
  114. bool success = true;
  115. size_t num_vts = 0;
  116. while (!fin.eof()) {
  117. fin.getline(line, LINE_SIZE);
  118. size_t n = sscanf(line, " %s", first_word);
  119. if (n == 0)
  120. continue;
  121. if (starts_with(first_word, face_begin)) {
  122. success = parse_ascii_normal(line);
  123. IGL_ASSERT(success);
  124. reading_facet = true;
  125. } else if (starts_with(first_word, face_end)) {
  126. IGL_ASSERT(reading_facet);
  127. reading_facet = false;
  128. } else if (starts_with(first_word, loop_begin)) {
  129. reading_loop = true;
  130. } else if (starts_with(first_word, loop_end)) {
  131. IGL_ASSERT(reading_loop);
  132. reading_loop = false;
  133. } else if (starts_with(first_word, vertex_flag)) {
  134. IGL_ASSERT(reading_facet);
  135. IGL_ASSERT(reading_loop);
  136. success = parse_ascii_vertex(line);
  137. IGL_ASSERT(success);
  138. num_vts += 1;
  139. }
  140. if (!success) {
  141. return false;
  142. }
  143. if (!reading_facet) {
  144. break;
  145. }
  146. }
  147. if (num_vts == 0) {
  148. return true;
  149. }
  150. IGL_ASSERT(num_vts == 3);
  151. if (num_vts != 3) {
  152. std::cerr << "Warning: mesh contain face made of " << num_vts
  153. << " vertices" << std::endl;
  154. return false;
  155. }
  156. return true;
  157. };
  158. while (!input.eof()) {
  159. success = parse_ascii_facet(input);
  160. if (!success) {
  161. return false;
  162. }
  163. }
  164. F.resize(V.size() / 3);
  165. for (size_t f = 0; f < F.size(); ++f) {
  166. auto v = static_cast<TypeF>(f * 3);
  167. F[f] = {{v, v + 1, v + 2}};
  168. }
  169. return success;
  170. }
  171. template <typename TypeV, typename TypeF, typename TypeN>
  172. IGL_INLINE bool read_stl_binary(std::istream &input,
  173. std::vector<std::array<TypeV, 3>> &V,
  174. std::vector<std::array<TypeF, 3>> &F,
  175. std::vector<std::array<TypeN, 3>> &N) {
  176. if (!input) {
  177. throw std::runtime_error("Failed to open file");
  178. }
  179. constexpr size_t FLOAT_SIZE = sizeof(float);
  180. static_assert(FLOAT_SIZE == 4, "float type is not 4 bytes");
  181. constexpr size_t LINE_SIZE = 256;
  182. char buf[LINE_SIZE];
  183. // 80 bytes header, no data significance.
  184. input.read(buf, 80);
  185. if (!input.good()) {
  186. throw std::runtime_error("Unable to parse STL header.");
  187. }
  188. input.read(buf, 4);
  189. const size_t num_faces = *reinterpret_cast<std::uint32_t *>(buf);
  190. if (!input.good()) {
  191. throw std::runtime_error("Unable to parse STL number of faces.");
  192. }
  193. for (size_t i = 0; i < num_faces; i++) {
  194. // Parse normal
  195. input.read(buf, FLOAT_SIZE * 3);
  196. auto nx = static_cast<TypeN>(*reinterpret_cast<float *>(buf));
  197. auto ny = static_cast<TypeN>(*reinterpret_cast<float *>(buf + FLOAT_SIZE));
  198. auto nz =
  199. static_cast<TypeN>(*reinterpret_cast<float *>(buf + FLOAT_SIZE * 2));
  200. IGL_ASSERT(input.good());
  201. // vertex 1
  202. input.read(buf, FLOAT_SIZE * 3);
  203. auto v1x = static_cast<TypeV>(*reinterpret_cast<float *>(buf));
  204. auto v1y = static_cast<TypeV>(*reinterpret_cast<float *>(buf + FLOAT_SIZE));
  205. auto v1z =
  206. static_cast<TypeV>(*reinterpret_cast<float *>(buf + FLOAT_SIZE * 2));
  207. IGL_ASSERT(input.good());
  208. // vertex 2
  209. input.read(buf, FLOAT_SIZE * 3);
  210. auto v2x = static_cast<TypeV>(*reinterpret_cast<float *>(buf));
  211. auto v2y = static_cast<TypeV>(*reinterpret_cast<float *>(buf + FLOAT_SIZE));
  212. auto v2z =
  213. static_cast<TypeV>(*reinterpret_cast<float *>(buf + FLOAT_SIZE * 2));
  214. IGL_ASSERT(input.good());
  215. // vertex 3
  216. input.read(buf, FLOAT_SIZE * 3);
  217. auto v3x = static_cast<TypeV>(*reinterpret_cast<float *>(buf));
  218. auto v3y = static_cast<TypeV>(*reinterpret_cast<float *>(buf + FLOAT_SIZE));
  219. auto v3z =
  220. static_cast<TypeV>(*reinterpret_cast<float *>(buf + FLOAT_SIZE * 2));
  221. IGL_ASSERT(input.good());
  222. // attribute (2 bytes), not sure what purpose they serve.
  223. input.read(buf, 2);
  224. N.push_back({{nx, ny, nz}});
  225. V.push_back({{v1x, v1y, v1z}});
  226. V.push_back({{v2x, v2y, v2z}});
  227. V.push_back({{v3x, v3y, v3z}});
  228. IGL_ASSERT(input.good());
  229. if (!input.good()) {
  230. std::stringstream err_msg;
  231. err_msg << "Failed to parse face " << i << " from STL file";
  232. throw std::runtime_error(err_msg.str());
  233. }
  234. }
  235. std::for_each(V.begin(), V.end(), [](const std::array<TypeV, 3> &v) {
  236. for (auto x : v) {
  237. if (!std::isfinite(x)) {
  238. throw std::runtime_error("NaN or Inf detected in input file.");
  239. }
  240. }
  241. });
  242. if (!V.empty()) {
  243. F.resize(V.size() / 3);
  244. for (size_t f = 0; f < F.size(); ++f) {
  245. auto v = static_cast<TypeF>(f * 3);
  246. F[f] = {{v, v + 1, v + 2}};
  247. }
  248. }
  249. return true;
  250. }
  251. template <typename TypeV, typename TypeF, typename TypeN>
  252. IGL_INLINE bool readSTL(std::istream &input,
  253. std::vector<std::array<TypeV, 3>> &V,
  254. std::vector<std::array<TypeF, 3>> &F,
  255. std::vector<std::array<TypeN, 3>> &N) {
  256. bool success = false;
  257. if (is_stl_binary(input)) {
  258. success = read_stl_binary(input, V, F, N);
  259. } else {
  260. success = read_stl_ascii(input, V, F, N);
  261. }
  262. return success;
  263. }
  264. template <typename DerivedV, typename DerivedF, typename DerivedN>
  265. IGL_INLINE bool readSTL(
  266. FILE * fp,
  267. Eigen::PlainObjectBase<DerivedV> & V,
  268. Eigen::PlainObjectBase<DerivedF> & F,
  269. Eigen::PlainObjectBase<DerivedN> & N)
  270. {
  271. std::vector<std::uint8_t> fileBufferBytes;
  272. read_file_binary(fp,fileBufferBytes);
  273. FileMemoryStream stream((char*)fileBufferBytes.data(), fileBufferBytes.size());
  274. return readSTL(stream, V, F, N);
  275. }
  276. } // namespace igl
  277. #ifdef IGL_STATIC_LIBRARY
  278. // Explicit template instantiation
  279. // generated by autoexplicit.sh
  280. template bool igl::readSTL<Eigen::Matrix<double, -1, 3, 1, -1, 3>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1> >(FILE*, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 3, 1, -1, 3> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&);
  281. template bool igl::readSTL<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1> >(FILE*, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&);
  282. template bool igl::readSTL<Eigen::Matrix<double, -1, -1, 1, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1> >(FILE*, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 1, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&);
  283. template bool igl::readSTL<Eigen::Matrix<double, -1, 3, 0, -1, 3>, Eigen::Matrix<int, -1, 3, 0, -1, 3>, Eigen::Matrix<double, -1, -1, 0, -1, -1> >(FILE*, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 3, 0, -1, 3> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 3, 0, -1, 3> >&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&);
  284. template bool igl::readSTL<Eigen::Matrix<double, -1, 3, 1, -1, 3>, Eigen::Matrix<int, -1, 3, 1, -1, 3>, Eigen::Matrix<double, -1, -1, 0, -1, -1> >(FILE*, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 3, 1, -1, 3> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 3, 1, -1, 3> >&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&);
  285. template bool igl::readSTL<Eigen::Matrix<float, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1> >(FILE*, Eigen::PlainObjectBase<Eigen::Matrix<float, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&);
  286. template bool igl::readSTL<Eigen::Matrix<float, -1, 3, 0, -1, 3>, Eigen::Matrix<int, -1, 3, 0, -1, 3>, Eigen::Matrix<double, -1, -1, 0, -1, -1> >(FILE*, Eigen::PlainObjectBase<Eigen::Matrix<float, -1, 3, 0, -1, 3> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 3, 0, -1, 3> >&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&);
  287. template bool igl::readSTL<Eigen::Matrix<float, -1, 3, 1, -1, 3>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1> >(FILE*, Eigen::PlainObjectBase<Eigen::Matrix<float, -1, 3, 1, -1, 3> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&);
  288. template bool igl::readSTL<Eigen::Matrix<float, -1, 3, 1, -1, 3>, Eigen::Matrix<int, -1, 3, 1, -1, 3>, Eigen::Matrix<double, -1, -1, 0, -1, -1> >(FILE*, Eigen::PlainObjectBase<Eigen::Matrix<float, -1, 3, 1, -1, 3> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 3, 1, -1, 3> >&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&);
  289. template bool igl::readSTL<Eigen::Matrix<float, -1, 3, 1, -1, 3>, Eigen::Matrix<unsigned int, -1, 3, 1, -1, 3>, Eigen::Matrix<double, -1, -1, 0, -1, -1> >(FILE*, Eigen::PlainObjectBase<Eigen::Matrix<float, -1, 3, 1, -1, 3> >&, Eigen::PlainObjectBase<Eigen::Matrix<unsigned int, -1, 3, 1, -1, 3> >&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&);
  290. #endif