Loading...
Searching...
No Matches
STL.cpp
1/*---------------------------------------------------------------------------*\
2 *
3 * bitpit
4 *
5 * Copyright (C) 2015-2021 OPTIMAD engineering Srl
6 *
7 * -------------------------------------------------------------------------
8 * License
9 * This file is part of bitpit.
10 *
11 * bitpit is free software: you can redistribute it and/or modify it
12 * under the terms of the GNU Lesser General Public License v3 (LGPL)
13 * as published by the Free Software Foundation.
14 *
15 * bitpit is distributed in the hope that it will be useful, but WITHOUT
16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
18 * License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with bitpit. If not, see <http://www.gnu.org/licenses/>.
22 *
23\*---------------------------------------------------------------------------*/
24
25#include <cassert>
26#include <cstdint>
27#include <cstring>
28
29#include "bitpit_common.hpp"
30#include "bitpit_operators.hpp"
31
32#include "STL.hpp"
33#include "logger.hpp"
34
35namespace bitpit {
36
41
42// Header is 80 characters long
43const std::size_t STLBase::BINARY_HEADER_SIZE = 80 * sizeof(STLBase::BINARY_UINT8);
44
45// An empty binary file contains the header plus a float_32 stating that there
46// are zero trinagles in the file.
47const std::size_t STLBase::BINARY_MINIMUM_SIZE = STLBase::BINARY_HEADER_SIZE + sizeof(STLBase::BINARY_UINT32);
48
49// Each facet is defined by the following information:
50// - Normal: 3 float_32
51// - Vertices' coordinates: 3x float_32
52// - Attribute byte count: 1 unit_16
53const std::size_t STLBase::BINARY_FACET_SIZE = 3 * sizeof(BINARY_REAL32) + 3 * 3 * sizeof(BINARY_REAL32) + sizeof(BINARY_UINT16);
54
55const std::string STLBase::ASCII_SOLID_BEGIN = "solid";
56const std::string STLBase::ASCII_SOLID_END = "endsolid";
57const std::string STLBase::ASCII_FACET_BEGIN = "facet";
58const std::string STLBase::ASCII_FACET_END = "endfacet";
59const std::string STLBase::ASCII_FILE_BEGIN = STLBase::ASCII_SOLID_BEGIN;
60const std::string STLBase::ASCII_FILE_END = STLBase::ASCII_SOLID_END;
61const std::size_t STLBase::ASCII_MINIMUM_SIZE = STLBase::ASCII_FILE_BEGIN.length() + STLBase::ASCII_FILE_END.length();
62
68STLBase::STLBase(const std::string &filename)
69{
70 setFilename(filename);
71 setFormat(FormatUnknown);
72}
73
80STLBase::STLBase(const std::string &filename, Format format)
81{
82 setFilename(filename);
83 setFormat(format);
84}
85
91const std::string & STLBase::getFilename() const
92{
93 return m_filename;
94}
95
101void STLBase::setFilename(const std::string &filename)
102{
103 m_filename = filename;
104}
105
111STLBase::Format STLBase::getFormat() const
112{
113 return m_format;
114}
115
121void STLBase::setFormat(STLBase::Format format)
122{
123 m_format = format;
124}
125
130
137STLReader::STLReader(const std::string &filename, Format format)
138 : STLBase(filename)
139{
140 if (format == FormatUnknown) {
141 format = detectFormat(filename);
142 }
143
144 if (format == FormatUnknown) {
145 throw std::runtime_error("Invalid STL format.");
146 }
147
148 setFormat(format);
149}
150
167STLReader::Format STLReader::detectFormat(const std::string &filename)
168{
169 std::ifstream fileStream;
170
171 // Get the file size
172 fileStream.open(filename, std::ifstream::ate | std::ifstream::binary);
173 if (!fileStream.good()) {
174 fileStream.close();
175 throw std::runtime_error("Invalid STL.");
176 }
177
178 std::size_t fileSize = fileStream.tellg();
179 fileStream.close();
180 fileStream.clear();
181
182 //
183 // ASCII check
184 //
185
186 // Check if the size is compatible with an ASCII STL file.
187 //
188 // An ASCII contains at least the "solid " and "endsolid" markers, therefore
189 // the minimum size of an empty ASCII file is 14 bytes.
190 if (fileSize < ASCII_MINIMUM_SIZE) {
191 return FormatUnknown;
192 }
193
194 // If a files starts with "solid" and ends with "endsolid" is an ASCII file.
195 //
196 // Binary files should never start with "solid ", but that's not mandatory.
197 // We need to check both the beginning and the and of the file to be sure
198 // of the file format.
199 char c;
200 std::size_t bufferPos;
201
202 bufferPos = 0;
203 std::string beginString(ASCII_FILE_BEGIN.size(), ' ');
204 fileStream.open(filename, std::ifstream::binary);
205 while (fileStream.get(c)) {
206 if (bufferPos == 0 && (std::isblank(c) || std::isspace(c))) {
207 continue;
208 }
209
210 beginString.at(bufferPos) = tolower(c);
211 ++bufferPos;
212 if (bufferPos == ASCII_FILE_BEGIN.size()) {
213 break;
214 }
215 }
216 fileStream.close();
217 fileStream.clear();
218
219 bool maybeASCII = (beginString.compare(ASCII_FILE_BEGIN) == 0);
220 if (maybeASCII) {
221 // Open the file
222 fileStream.open(filename, std::ifstream::ate | std::ifstream::binary);
223
224 // Move the cursor at the beginning of the last non-empty line
225 bool empty = true;
226 fileStream.seekg(-1, std::ios_base::cur);
227 while (fileStream.get(c)) {
228 if (c == '\n') {
229 if (!empty) {
230 break;
231 }
232 }
233
234 if (empty) {
235 empty = (!std::isblank(c) && !std::isspace(c));
236 }
237
238 fileStream.seekg(-2, std::ios_base::cur);
239 }
240
241 // Search the end-line keyword
242 bufferPos = 0;
243 std::string endString(ASCII_SOLID_END.size(), ' ');
244 while (fileStream.get(c)) {
245 if (bufferPos == 0 && (std::isblank(c) || std::isspace(c))) {
246 continue;
247 }
248
249 endString.at(bufferPos) = tolower(c);
250 ++bufferPos;
251 if (bufferPos == ASCII_SOLID_END.size()) {
252 break;
253 }
254 }
255
256 // Close the file
257 fileStream.close();
258 fileStream.clear();
259
260 // Check if the end-solid keyword was found
261 bool isASCII = (endString.compare(ASCII_SOLID_END) == 0);
262 if (isASCII) {
263 return FormatASCII;
264 }
265 }
266
267 //
268 // Binary check
269 //
270
271 // Check if the size is compatible with a binary STL file.
272 //
273 // An empty binary file contains the header and the number of facets,
274 // therefore the minimum size of an empty binary file is 84 bytes.
275 if (fileSize < BINARY_MINIMUM_SIZE) {
276 return FormatUnknown;
277 }
278
279 // Read the number of facets
280 std::uint32_t nFacets;
281
282 fileStream.open(filename, std::ifstream::binary);
283 fileStream.seekg(BINARY_HEADER_SIZE);
284 fileStream.read(reinterpret_cast<char *>(&nFacets), sizeof(BINARY_UINT32));
285 fileStream.close();
286 fileStream.clear();
287
288 // Check that the size of the file is compatiblewith the number of facets
289 std::size_t expectedFileSize = BINARY_HEADER_SIZE + sizeof(BINARY_UINT32) + (nFacets * BINARY_FACET_SIZE);
290 if (fileSize == expectedFileSize) {
291 return FormatBinary;
292 }
293
294 return FormatUnknown;
295}
296
308{
309 // Begin read
310 int beginError = readBegin();
311 if (beginError != 0) {
312 return beginError;
313 }
314
315 // Get format
316 Format format = getFormat();
317
318 // Inspect file
319 int inspectionError;
320 if (format == FormatASCII) {
321 inspectionError = inspectASCII(info);
322 } else {
323 inspectionError = inspectBinary(info);
324 }
325
326 // End read
327 int endError = readEnd();
328
329 // Done
330 if (inspectionError != 0) {
331 return inspectionError;
332 } else if (endError != 0) {
333 return endError;
334 } else {
335 return 0;
336 }
337}
338
348int STLReader::inspectASCII(InspectionInfo *info)
349{
350 // Initialize info
351 info->nSolids = 0;
352 info->solidErrors.clear();
353 info->solidNames.clear();
354 info->solidFacetCount.clear();
355 info->solidVertexCount.clear();
356
357 // Check stream status
358 if (!m_fileHandle.good()) {
359 return -1;
360 }
361
362 // Set cursor at file begin
363 m_fileHandle.clear();
364 std::streamoff start_pos = m_fileHandle.tellg();
365 m_fileHandle.seekg(0);
366
367 // Scan file
368 std::string word;
369 std::stringstream sname;
370
371 int inspectionError = 0;
372 while (m_lineStream.readLine(m_fileHandle) >= 0) {
373 // Get keyword
374 if ((m_lineStream >> word) && (word.compare(ASCII_SOLID_BEGIN) == 0)) {
375 // Initialize inspection info
376 int solidIndex = info->nSolids;
377
378 ++info->nSolids;
379 info->solidErrors.emplace_back();
380 info->solidNames.emplace_back("");
381 info->solidFacetCount.emplace_back(0);
382 info->solidVertexCount.emplace_back(0);
383
384 // Get solid name
385 sname.str("");
386 while (m_lineStream >> word) {
387 sname << word << " ";
388 }
389 info->solidNames[solidIndex] = sname.str();
390 utils::string::trim(info->solidNames[solidIndex]);
391
392 // Get solid info
393 inspectionError = inspectSolidASCII(info->solidFacetCount.data() + solidIndex, info->solidErrors.data() + solidIndex);
394 if (inspectionError != 0) {
395 break;
396 }
397
398 info->solidVertexCount[solidIndex] = 3 * info->solidFacetCount[solidIndex];
399 }
400 }
401
402 // Restore cursor position
403 m_fileHandle.clear();
404 m_fileHandle.seekg(start_pos);
405
406 return inspectionError;
407}
408
427int STLReader::inspectSolidASCII(std::size_t *nFacets, std::array<bool, 6> *errors)
428{
429 // Initialize solid information
430 (*nFacets) = 0;
431 errors->fill(false);
432
433 // Check stream status
434 if (!m_fileHandle.good()) {
435 return -1;
436 }
437
438 // Inspect the solid
439 std::string word;
440 std::streamoff last_valid_pos;
441
442 int inspectError = 0;
443 while (true) {
444 // Get next line
445 last_valid_pos = m_fileHandle.tellg();
446 m_lineStream.readLine(m_fileHandle);
447 if (!(m_lineStream >> word)) {
448 word = "";
449 }
450
451 // Exit conditions
452 if (m_fileHandle.eof()) {
453 break;
454 } else if (word.compare(ASCII_SOLID_END) == 0) {
455 break;
456 } else if (word.compare(ASCII_SOLID_BEGIN) == 0) {
457 break;
458 }
459
460 // Look for keyword "facet"
461 if (word.compare(ASCII_FACET_BEGIN) == 0) {
462 ++(*nFacets);
463
464 m_fileHandle.seekg(last_valid_pos);
465 inspectError = inspectFacetASCII(errors);
466 if (inspectError != 0) {
467 break;
468 }
469 }
470 }
471
472 // Check block temination
473 if (word.compare(ASCII_SOLID_END) != 0) {
474 (*errors)[0] = true;
475 m_fileHandle.clear();
476 m_fileHandle.seekg(last_valid_pos);
477 }
478
479 return inspectError;
480}
481
495int STLReader::inspectFacetASCII(std::array<bool, 6> *errors)
496{
497 // Check stream status
498 if (!m_fileHandle.good()) {
499 return -1;
500 }
501
502 // Check facet data
503 std::string word;
504 std::streamoff last_valid_pos;
505
506 last_valid_pos = m_fileHandle.tellg();
507 m_lineStream.readLine(m_fileHandle);
508 if ((!(m_lineStream >> word)) || (word.compare(ASCII_FACET_BEGIN) == 0)) {
509 word = "begin";
510 }
511
512 std::size_t nV = 0;
513 bool normal_found = false;
514 while ((!m_fileHandle.eof())
515 && ((word.compare(ASCII_FACET_END) != 0)
516 && (word.compare(ASCII_FACET_BEGIN) != 0)
517 && (word.compare(ASCII_SOLID_END) != 0)
518 && (word.compare(ASCII_SOLID_BEGIN) != 0))) {
519
520 // Check facet normal or facet vertices
521 if (word.compare("begin") == 0) {
522 if ((m_lineStream >> word) && (word.compare("normal") == 0)) {
523 normal_found = true;
524
525 int nxyz = 0;
526 while (m_lineStream >> word) {
527 nxyz++;
528 }
529
530 if (nxyz != 3) {
531 (*errors)[3] = true;
532 }
533 }
534 }
535 else if (word.compare("vertex") == 0) {
536 nV++;
537
538 int nxyz = 0;
539 while (m_lineStream >> word) {
540 nxyz++;
541 }
542
543 if (nxyz != 3) {
544 (*errors)[5] = true;
545 }
546 }
547
548 // Get next line
549 last_valid_pos = m_fileHandle.tellg();
550 m_lineStream.readLine(m_fileHandle);
551 if (!(m_lineStream >> word)) {
552 word = "";
553 }
554 }
555
556 // Check if facket section is properly closed
557 if (word.compare(ASCII_FACET_END) != 0) {
558 (*errors)[1] = true;
559 m_fileHandle.clear(),
560 m_fileHandle.seekg(last_valid_pos);
561 }
562
563 // Check if normal is valid
564 if (!normal_found) {
565 (*errors)[2] = true;
566 }
567
568 // Check if number of vertices is valid
569 if (nV != 3) {
570 (*errors)[4] = true;
571 }
572
573 return 0;
574}
575
585int STLReader::inspectBinary(InspectionInfo *info)
586{
587 // Initialize info
588 info->nSolids = 0;
589 info->solidErrors.clear();
590 info->solidNames.clear();
591 info->solidFacetCount.clear();
592 info->solidVertexCount.clear();
593
594 // Check stream status
595 if (!m_fileHandle.good()) {
596 return -1;
597 }
598
599 // Set cursor at file begin
600 m_fileHandle.clear();
601 std::streamoff start_pos = m_fileHandle.tellg();
602 m_fileHandle.seekg(0);
603
604 // Inspect header
605 for (std::size_t i = 0; i < BINARY_HEADER_SIZE / sizeof(BINARY_UINT8); ++i) {
606 BINARY_UINT8 headerCharacter;
607 m_fileHandle.read(reinterpret_cast<char*>(&headerCharacter), sizeof(BINARY_UINT8));
608 }
609
610 if (m_fileHandle.eof()) {
611 info->solidErrors[0][0] = true;
612 return 0;
613 }
614
615 // Binary files does not contain information about the solid name
616 info->solidNames.push_back("");
617
618 // Read number of facets
619 BINARY_UINT32 nFacets;
620 m_fileHandle.read(reinterpret_cast<char *>(&nFacets), sizeof(BINARY_UINT32));
621 info->solidFacetCount.push_back((std::size_t) nFacets);
622 info->solidVertexCount.push_back((std::size_t) (3 * nFacets));
623
624 if (m_fileHandle.eof()) {
625 info->solidErrors[0][0] = true;
626 return 0;
627 }
628
629 // Check facet data
630 std::size_t n = 0;
631 while ((!m_fileHandle.eof()) && (n < nFacets)) {
632 std::array<char, BINARY_FACET_SIZE> facetData;
633 m_fileHandle.read(facetData.data(), BINARY_FACET_SIZE);
634 n++;
635 }
636
637 // Check number of facets
638 if (n < nFacets) {
639 info->solidErrors[0][1] = true;
640 }
641
642 // Reset cursor position
643 m_fileHandle.clear();
644 m_fileHandle.seekg(start_pos);
645
646 return 0;
647}
648
655void STLReader::displayInspectionInfo(const InspectionInfo &info, std::ostream &out) const
656{
657 out << "Inspection info" << "\n";
658
659 out << " Filename : " << getFilename() << "\n";
660
661 Format format = getFormat();
662 if (format == FormatBinary) {
663 out << " Format : binary" << "\n";
664 } else {
665 out << " Format : ASCII" << "\n";
666 }
667
668 out << "\n";
669 if (info.nSolids > 0) {
670 out << " Solid count : " << info.nSolids << "\n";
671
672 for (int i = 0; i < info.nSolids; ++i) {
673 out << "\n";
674 out << " Solid index : " << i << "\n";
675 out << " Solid name : " << info.solidNames[i] << "\n";
676 out << " Solid facets : " << info.solidFacetCount[i] << "\n";
677 out << " Solid vertices : " << info.solidVertexCount[i] << "\n";
678
679 if (info.solidErrors[i][0]) {
680 out << " **ERROR** Unterminated solid block." << "\n";
681 }
682 if (info.solidErrors[i][1]) {
683 out << " **ERROR** Unterminated facet block." << "\n";
684 }
685 if (info.solidErrors[i][2]) {
686 out << " **ERROR** Normal data are missing." << "\n";
687 }
688 if (info.solidErrors[i][3]) {
689 out << " **ERROR** Wrong number of components for normal data." << "\n";
690 }
691 if (info.solidErrors[i][4]) {
692 out << " **ERROR** Wrong number of vertices in facet block." << "\n";
693 }
694 if (info.solidErrors[i][5]) {
695 out << " **ERROR** Wrong number of coordinates for vertice." << "\n";
696 }
697 }
698 } else {
699 out << " STL file contains no solids." << "\n";
700 }
701
702 out.flush();
703}
704
714{
715 if (m_fileHandle.is_open()) {
716 return -2;
717 }
718
719 m_fileHandle.open(getFilename(), std::ifstream::in | std::ifstream::binary);
720 if (!m_fileHandle.good()) {
721 return -1;
722 }
723
724 return 0;
725}
726
731{
732 m_fileHandle.close();
733
734 return 0;
735}
736
758int STLReader::readSolid(std::string *name, std::size_t *nV, std::size_t *nT,
759 std::vector<std::array<double, 3>> *V, std::vector<std::array<double, 3>> *N,
760 std::vector<std::array<std::size_t, 3>> *T)
761{
762 return readSolid("", name, nV, nT, V, N, T);
763}
764
789int STLReader::readSolid(const std::string &solid, std::string *name, std::size_t *nV, std::size_t *nT,
790 std::vector<std::array<double, 3>> *V, std::vector<std::array<double, 3>> *N,
791 std::vector<std::array<std::size_t, 3>> *T)
792{
793 // Read header
794 std::size_t nSolidFacets;
795 int headerError = readHeader(solid, name, &nSolidFacets);
796 if (headerError != 0) {
797 return headerError;
798 }
799
800 // Read facet data
801 V->resize(*nV + 3 * nSolidFacets, {{0., 0., 0.}});
802 N->resize(*nT + nSolidFacets, {{0., 0., 0.}});
803 T->resize(*nT + nSolidFacets, {{0, 0, 0}});
804
805 for (std::size_t i = 0; i < *nT; ++i) {
806 // Read facet data
807 std::array<double, 3> *V0 = V->data() + *nV + 3 * i;
808 std::array<double, 3> *V1 = V0 + 1;
809 std::array<double, 3> *V2 = V1 + 1;
810
811 int facetError = readFacet(V0, V1, V2, N->data() + i);
812 if (facetError != 0) {
813 return facetError;
814 }
815
816 // Update facet->vertex connectivity
817 (*T)[i][0] = *nV + 3 * i;
818 (*T)[i][1] = (*T)[i][0] + 1;
819 (*T)[i][2] = (*T)[i][1] + 1;
820 }
821
822 // Read footer
823 int footerError = readFooter(solid);
824 if (footerError != 0) {
825 return footerError;
826 }
827
828 return 0;
829}
830
844int STLReader::readHeader(std::string *name, std::size_t *nT)
845{
846 return readHeader("", name, nT);
847}
848
864int STLReader::readHeader(const std::string &solid, std::string *name, std::size_t *nT)
865{
866 Format format = getFormat();
867
868 int error;
869 if (format == FormatASCII) {
870 error = readHeaderASCII(solid, name, nT);
871 } else {
872 std::string trimmedSolid = solid;
873 utils::string::trim(trimmedSolid);
874 if (!trimmedSolid.empty()) {
875 log::cout() << "WARNING: loading solids with a specific name is only supported for ASCII files." << std::endl;
876 log::cout() << " The reader will read the next solid." << std::endl;
877 }
878
879 error = readHeaderBinary(name, nT);
880 }
881
882 return error;
883}
884
896{
897 return readFooter("");
898}
899
912int STLReader::readFooter(const std::string &solid)
913{
914 Format format = getFormat();
915
916 int error;
917 if (format == FormatASCII) {
918 error = readFooterASCII(solid);
919 } else {
920 error = readFooterBinary();
921 }
922
923 return error;
924}
925
941int STLReader::readFacet(std::array<double, 3> *V0, std::array<double, 3> *V1,
942 std::array<double, 3> *V2, std::array<double, 3> *N)
943{
944 Format format = getFormat();
945
946 int error;
947 if (format == FormatASCII) {
948 error = readFacetASCII(V0, V1, V2, N);
949 } else {
950 error = readFacetBinary(V0, V1, V2, N);
951 }
952
953 return error;
954}
955
971int STLReader::readHeaderASCII(const std::string &solid, std::string *name, std::size_t *nT)
972{
973 // Check stream status
974 if (!m_fileHandle.good()) {
975 return -1;
976 }
977
978 // Get solid key
979 std::string solidKey = solid;
980 utils::string::trim(solidKey);
981 solidKey = ASCII_SOLID_BEGIN + " " + solidKey;
982 utils::string::trim(solidKey);
983
984 // Scan file until solid is found
985 std::string word;
986 std::string line;
987
988 std::streamoff start_pos = m_fileHandle.tellg();
989 std::streamoff current_pos = start_pos + 1;
990
991 bool solidFound = false;
992 bool wrapAround = solidKey.compare(ASCII_SOLID_BEGIN) != 0;
993 while (!solidFound && (start_pos != current_pos)) {
994 // Get current line
995 m_lineStream.readLine(m_fileHandle);
996
997 // Check end of file
998 if (m_fileHandle.eof()) {
999 if (wrapAround) {
1000 m_fileHandle.clear();
1001 m_fileHandle.seekg(0);
1002 wrapAround = false;
1003 } else {
1004 solidFound = false;
1005 break;
1006 }
1007 }
1008 current_pos = m_fileHandle.tellg();
1009
1010 // Look for keyword "solid"
1011 if ((m_lineStream >> word) && (word.compare(ASCII_SOLID_BEGIN) == 0)) {
1012 m_lineStream.copyLine(&line);
1013 if (solidKey.compare(ASCII_SOLID_BEGIN) == 0 || line.compare(solidKey) == 0) {
1014 *name = line.erase(0, ASCII_SOLID_BEGIN.size());
1015 *name = utils::string::trim(*name);
1016
1017 start_pos = current_pos;
1018
1019 solidFound = true;
1020 }
1021 }
1022 }
1023
1024 if (!solidFound) {
1025 return -2;
1026 }
1027
1028 // Read number of facets
1029 m_fileHandle.clear();
1030 m_fileHandle.seekg(start_pos);
1031
1032 std::array<bool, 6> solidErrors;
1033 inspectSolidASCII(nT, &solidErrors);
1034
1035 m_fileHandle.clear();
1036 m_fileHandle.seekg(start_pos);
1037
1038 return 0;
1039}
1040
1053int STLReader::readFooterASCII(const std::string &solid)
1054{
1055 // Check stream status
1056 if (!m_fileHandle.good()) {
1057 return -1;
1058 }
1059
1060 // Get solid key
1061 std::string solidKey = solid;
1062 utils::string::trim(solidKey);
1063 solidKey = ASCII_SOLID_END + " " + solidKey;
1064 utils::string::trim(solidKey);
1065
1066 // Look for the end of solid section
1067 std::string word;
1068 std::string line;
1069
1070 while (true) {
1071 // Get next line
1072 m_lineStream.readLine(m_fileHandle);
1073
1074 // Get next word
1075 if (!(m_lineStream >> word)) {
1076 word = "";
1077 }
1078
1079 // Handle the word
1080 if (word.compare(ASCII_SOLID_END) == 0) {
1081 m_lineStream.copyLine(&line);
1082 if (line.compare(solidKey) != 0) {
1083 log::cout() << "WARNING: end-solid key does not match the solid name." << std::endl;
1084 log::cout() << " Expected end-solid key : " << solidKey << std::endl;
1085 log::cout() << " Current end-solid key : " << line << std::endl;
1086 }
1087
1088 break;
1089 } else if (word.compare(ASCII_SOLID_BEGIN) == 0) {
1090 return -2;
1091 } else if (m_fileHandle.eof()) {
1092 return -2;
1093 }
1094 }
1095
1096 return 0;
1097}
1098
1114int STLReader::readFacetASCII(std::array<double, 3> *V0, std::array<double, 3> *V1,
1115 std::array<double, 3> *V2, std::array<double, 3> *N)
1116{
1117 // Read facet data
1118 std::string word;
1119 std::string value;
1120 std::streamoff last_valid_pos;
1121
1122 int error = 0;
1123 int nFacetVertices = 0;
1124 std::string target = "facet";
1125 while (true) {
1126 // Get next line
1127 last_valid_pos = m_fileHandle.tellg();
1128 m_lineStream.readLine(m_fileHandle);
1129 if (!(m_lineStream >> word)) {
1130 continue;
1131 }
1132
1133 // Handle the word
1134 //
1135 // Beginning of faacet section and normal are on the same line, after
1136 // finding the beginning of the facet section we have to extract the
1137 // next word without getting a new line.
1138 if (word.compare(ASCII_FACET_BEGIN) == 0) {
1139 if (word.compare(target) == 0) {
1140 target = "normal";
1141
1142 if (!(m_lineStream >> word)) {
1143 continue;
1144 }
1145 } else {
1146 error = -2;
1147 break;
1148 }
1149 }
1150
1151 if (word.compare(ASCII_FACET_END) == 0) {
1152 if (word.compare(target) == 0) {
1153 break;
1154 } else {
1155 error = -2;
1156 break;
1157 }
1158 } else if (word.compare("normal") == 0) {
1159 if (word.compare(target) == 0) {
1160 for (int k = 0; k < 3; ++k) {
1161 m_lineStream >> value;
1162 (*N)[k] = stod(value);
1163 }
1164 target = "vertex";
1165 } else {
1166 error = -2;
1167 break;
1168 }
1169 } else if (word.compare("vertex") == 0) {
1170 if (word.compare(target) == 0) {
1171 std::array<double, 3> *coords;
1172 if (nFacetVertices == 0) {
1173 coords = V0;
1174 } else if (nFacetVertices == 1) {
1175 coords = V1;
1176 } else if (nFacetVertices == 2) {
1177 coords = V2;
1178 target = ASCII_FACET_END;
1179 } else {
1180 error = -3;
1181 break;
1182 }
1183
1184 for (int k = 0; k < 3; ++k) {
1185 m_lineStream >> value;
1186 (*coords)[k] = stod(value);
1187 }
1188
1189 nFacetVertices++;
1190 } else {
1191 error = -2;
1192 break;
1193 }
1194 } else if (word.compare(ASCII_SOLID_BEGIN) == 0) {
1195 error = -2;
1196 break;
1197 } else if (word.compare(ASCII_SOLID_END) == 0) {
1198 error = -2;
1199 break;
1200 } else if (m_fileHandle.eof()) {
1201 error = -2;
1202 break;
1203 }
1204 }
1205
1206 // Restor cursor position
1207 if (error != 0) {
1208 m_fileHandle.clear();
1209 m_fileHandle.seekg(last_valid_pos);
1210 }
1211
1212 return error;
1213}
1214
1228int STLReader::readHeaderBinary(std::string *name, std::size_t *nT)
1229{
1230 // Check stream status
1231 if (!m_fileHandle.good()) {
1232 return -1;
1233 }
1234
1235 // Binary data has not information about solid names
1236 (*name) = "";
1237
1238 // Skip header
1239 for (std::size_t i = 0; i < BINARY_HEADER_SIZE / sizeof(BINARY_UINT8); ++i) {
1240 BINARY_UINT8 headerCharacter;
1241 m_fileHandle.read(reinterpret_cast<char *>(&headerCharacter), sizeof(BINARY_UINT8));
1242 }
1243
1244 // Read number of facets
1245 BINARY_UINT32 nSolidFacets;
1246 m_fileHandle.read(reinterpret_cast<char *>(&nSolidFacets), sizeof(BINARY_UINT32));
1247 *nT = nSolidFacets;
1248
1249 // Check if the end of file has been reached
1250 if (m_fileHandle.eof()) {
1251 return -2;
1252 }
1253
1254 return 0;
1255}
1256
1266int STLReader::readFooterBinary()
1267{
1268 // Check stream status
1269 if (!m_fileHandle.good()) {
1270 return -1;
1271 }
1272
1273 // Nothing to do
1274 return 0;
1275}
1276
1291int STLReader::readFacetBinary(std::array<double, 3> *V0, std::array<double, 3> *V1,
1292 std::array<double, 3> *V2, std::array<double, 3> *N)
1293{
1294 // Check stream status
1295 if (!m_fileHandle.good()) {
1296 return -1;
1297 }
1298
1299 // Read facet data
1300 std::array<char, BINARY_FACET_SIZE> facetData;
1301 m_fileHandle.read(facetData.data(), BINARY_FACET_SIZE);
1302 int facetDataOffset = 0;
1303
1304 // Store normal
1305 for (int k = 0; k < 3; ++k) {
1306 (*N)[k] = (double) *(reinterpret_cast<BINARY_REAL32 *>(facetData.data() + facetDataOffset));
1307 facetDataOffset += sizeof(BINARY_REAL32);
1308 }
1309
1310 // Store vertex coordinates
1311 for (int k = 0; k < 3; ++k) {
1312 (*V0)[k] = (double) *(reinterpret_cast<BINARY_REAL32 *>(facetData.data() + facetDataOffset));
1313 facetDataOffset += sizeof(BINARY_REAL32);
1314 }
1315
1316 for (int k = 0; k < 3; ++k) {
1317 (*V1)[k] = (double) *(reinterpret_cast<BINARY_REAL32 *>(facetData.data() + facetDataOffset));
1318 facetDataOffset += sizeof(BINARY_REAL32);
1319 }
1320
1321 for (int k = 0; k < 3; ++k) {
1322 (*V2)[k] = (double) *(reinterpret_cast<BINARY_REAL32 *>(facetData.data() + facetDataOffset));
1323 facetDataOffset += sizeof(BINARY_REAL32);
1324 }
1325
1326 // Check if the end of file has been reached
1327 if (m_fileHandle.eof()) {
1328 return -2;
1329 }
1330
1331 return 0;
1332}
1333
1338
1345STLWriter::STLWriter(const std::string &filename, Format format)
1346 : STLBase(filename, format)
1347{
1348 if (format == FormatUnknown) {
1349 throw std::runtime_error("Invalid STL format.");
1350 }
1351}
1352
1361int STLWriter::writeBegin(WriteMode writeMode, bool partialWrite)
1362{
1363 if (m_fileHandle.is_open()) {
1364 return -2;
1365 }
1366
1367 Format format = getFormat();
1368
1369 std::ios_base::openmode openMode;
1370 if (format == FormatBinary) {
1371 if (writeMode == WriteOverwrite) {
1372 openMode = std::ofstream::out | std::ofstream::binary;
1373 } else if (partialWrite && writeMode == WriteAppend) {
1374 openMode = std::ofstream::app | std::ofstream::binary;
1375 } else {
1376 throw std::runtime_error("Specified write mode is not supported for binary files.");
1377 }
1378 } else {
1379 if (writeMode == WriteOverwrite) {
1380 openMode = std::ofstream::out;
1381 } else if (writeMode == WriteAppend) {
1382 openMode = std::ofstream::app;
1383 } else {
1384 throw std::runtime_error("Specified write mode is not supported for ASCII files.");
1385 }
1386 }
1387
1388 m_fileHandle.open(getFilename(), openMode);
1389 if (!m_fileHandle.good()) {
1390 return -1;
1391 }
1392
1393 // Set stream properties
1394 m_fileHandle << std::scientific;
1395
1396 return 0;
1397}
1398
1403{
1404 m_fileHandle.close();
1405
1406 return 0;
1407}
1408
1425int STLWriter::writeSolid(const std::string &name, std::size_t nV, std::size_t nT,
1426 const std::vector<std::array<double,3>> &V, const std::vector<std::array<double,3>> &N,
1427 const std::vector<std::array<std::size_t,3>> &T)
1428{
1429 // Check input variables
1430 if (V.size() < nV) {
1431 return -2;
1432 }
1433
1434 if (T.size() < nT) {
1435 return -2;
1436 }
1437
1438 if (N.size() < nT) {
1439 return -2;
1440 }
1441
1442 // Save stream flags
1443 std::ios::fmtflags streamFlags(m_fileHandle.flags());
1444
1445 // Write header
1446 int headerError = writeHeader(name, nT);
1447 if (headerError != 0) {
1448 return headerError;
1449 }
1450
1451 // Write facet data
1452 for (std::size_t i = 0; i < nT; ++i) {
1453 // Check connectivity
1454 for (int k = 0; k < 3; ++k) {
1455 if (T[i][k] >= nV) {
1456 return -2;
1457 }
1458 }
1459
1460 // Write data
1461 int facetError = writeFacet(V[T[i][0]], V[T[i][1]], V[T[i][2]], N[i]);
1462 if (facetError != 0) {
1463 return facetError;
1464 }
1465 }
1466
1467 // Write footer
1468 int footerError = writeFooter(name);
1469 if (footerError != 0) {
1470 return footerError;
1471 }
1472
1473 // Restore stream flags
1474 m_fileHandle.flags(streamFlags);
1475
1476 return 0;
1477}
1478
1490int STLWriter::writeHeader(const std::string &name, std::size_t nT)
1491{
1492 Format format = getFormat();
1493
1494 int error;
1495 if (format == FormatASCII) {
1496 error = writeHeaderASCII(name, nT);
1497 } else {
1498 error = writeHeaderBinary(name, nT);
1499 }
1500
1501 return error;
1502}
1503
1514int STLWriter::writeFooter(const std::string &name)
1515{
1516 Format format = getFormat();
1517
1518 int error;
1519 if (format == FormatASCII) {
1520 error = writeFooterASCII(name);
1521 } else {
1522 error = writeFooterBinary(name);
1523 }
1524
1525 return error;
1526}
1527
1541int STLWriter::writeFacet(const std::array<double, 3> &V0, const std::array<double, 3> &V1,
1542 const std::array<double, 3> &V2, const std::array<double, 3> &N)
1543{
1544 Format format = getFormat();
1545
1546 int error;
1547 if (format == FormatASCII) {
1548 error = writeFacetASCII(V0, V1, V2, N);
1549 } else {
1550 error = writeFacetBinary(V0, V1, V2, N);
1551 }
1552
1553 return error;
1554}
1555
1567int STLWriter::writeHeaderASCII(const std::string &name, std::size_t nT)
1568{
1569 BITPIT_UNUSED(nT);
1570
1571 // Check stream status
1572 if (!m_fileHandle.good()) {
1573 return -1;
1574 }
1575
1576 // Write header
1577 std::stringstream sheader;
1578 sheader << ASCII_SOLID_BEGIN << " " << name;
1579
1580 // Write number of facets
1581 std::string header = sheader.str();
1582 header = utils::string::trim(header);
1583 m_fileHandle << header << "\n";
1584
1585 return 0;
1586}
1587
1598int STLWriter::writeFooterASCII(const std::string &name)
1599{
1600 BITPIT_UNUSED(name);
1601
1602 // Check stream status
1603 if (!m_fileHandle.good()) {
1604 return -1;
1605 }
1606
1607 // Write footer
1608 std::stringstream sfooter;
1609 sfooter << ASCII_SOLID_END << " " << name;
1610
1611 std::string footer;
1612 footer = sfooter.str();
1613 footer = utils::string::trim(footer);
1614 m_fileHandle << footer << "\n";
1615
1616 return 0;
1617}
1618
1632int STLWriter::writeFacetASCII(const std::array<double, 3> &V0, const std::array<double, 3> &V1,
1633 const std::array<double, 3> &V2, const std::array<double, 3> &N)
1634{
1635 // Check stream status
1636 if (!m_fileHandle.good()) {
1637 return -1;
1638 }
1639
1640 // Facet header
1641 m_fileHandle << " " << ASCII_FACET_BEGIN;
1642
1643 // Facet normal
1644 m_fileHandle << " normal ";
1645 m_fileHandle << N[0] << " ";
1646 m_fileHandle << N[1] << " ";
1647 m_fileHandle << N[2];
1648 m_fileHandle << "\n";
1649
1650 // Facet vertices
1651 m_fileHandle << " outer loop" << "\n";
1652
1653 m_fileHandle << " vertex ";
1654 m_fileHandle << V0[0] << " ";
1655 m_fileHandle << V0[1] << " ";
1656 m_fileHandle << V0[2];
1657 m_fileHandle << "\n";
1658
1659 m_fileHandle << " vertex ";
1660 m_fileHandle << V1[0] << " ";
1661 m_fileHandle << V1[1] << " ";
1662 m_fileHandle << V1[2];
1663 m_fileHandle << "\n";
1664
1665 m_fileHandle << " vertex ";
1666 m_fileHandle << V2[0] << " ";
1667 m_fileHandle << V2[1] << " ";
1668 m_fileHandle << V2[2];
1669 m_fileHandle << "\n";
1670
1671 m_fileHandle << " endloop" << "\n";
1672
1673 // Facet footer
1674 m_fileHandle << " " << ASCII_FACET_END << "\n";
1675
1676 return 0;
1677}
1678
1690int STLWriter::writeHeaderBinary(const std::string &name, std::size_t nT)
1691{
1692 BITPIT_UNUSED(name);
1693
1694 // Check stream status
1695 if (!m_fileHandle.good()) {
1696 return -1;
1697 }
1698
1699 // Write header
1700 for (std::size_t i = 0; i < BINARY_HEADER_SIZE / sizeof(BINARY_UINT8); ++i) {
1701 BINARY_UINT8 headerCharacter = 0;
1702 m_fileHandle.write(reinterpret_cast<char*>(&headerCharacter), sizeof(BINARY_UINT8));
1703 }
1704
1705 // Write number of facets
1706 BINARY_UINT32 nFacets = (BINARY_UINT32) nT;
1707 m_fileHandle.write(reinterpret_cast<char *>(&nFacets), sizeof(BINARY_UINT32));
1708
1709 return 0;
1710}
1711
1722int STLWriter::writeFooterBinary(const std::string &name)
1723{
1724 BITPIT_UNUSED(name);
1725
1726 // Check stream status
1727 if (!m_fileHandle.good()) {
1728 return -1;
1729 }
1730
1731 // Nothing to do
1732 return 0;
1733}
1734
1748int STLWriter::writeFacetBinary(const std::array<double, 3> &V0, const std::array<double, 3> &V1,
1749 const std::array<double, 3> &V2, const std::array<double, 3> &N)
1750{
1751 // Check stream status
1752 if (!m_fileHandle.good()) {
1753 return -1;
1754 }
1755
1756 // Normals
1757 for (int k = 0; k < 3; ++k) {
1758 BINARY_REAL32 N_k = (BINARY_REAL32) N[k];
1759 m_fileHandle.write(reinterpret_cast<char *>(&N_k), sizeof(BINARY_REAL32));
1760 }
1761
1762 // Vertices
1763 for (int k = 0; k < 3; ++k) {
1764 BINARY_REAL32 V_k = (BINARY_REAL32) V0[k];
1765 m_fileHandle.write(reinterpret_cast<char *>(&V_k), sizeof(BINARY_REAL32));
1766 }
1767
1768 for (int k = 0; k < 3; ++k) {
1769 BINARY_REAL32 V_k = (BINARY_REAL32) V1[k];
1770 m_fileHandle.write(reinterpret_cast<char *>(&V_k), sizeof(BINARY_REAL32));
1771 }
1772
1773 for (int k = 0; k < 3; ++k) {
1774 BINARY_REAL32 V_k = (BINARY_REAL32) V2[k];
1775 m_fileHandle.write(reinterpret_cast<char *>(&V_k), sizeof(BINARY_REAL32));
1776 }
1777
1778 // Attribute byte count
1779 //
1780 // In the standard format, this should be zero because most software
1781 // does not understand anything else.
1782 // Attribute byte count
1783 BINARY_UINT16 attributeByteCount = 0;
1784 m_fileHandle.write(reinterpret_cast<char *>(&attributeByteCount), sizeof(BINARY_UINT16));
1785
1786 return 0;
1787}
1788
1789}
int readLine(std::ifstream &fileHandle)
void setFormat(Format format)
Definition STL.cpp:121
void setFilename(const std::string &filename)
Definition STL.cpp:101
STLBase(const std::string &filename)
Definition STL.cpp:68
Format getFormat() const
Definition STL.cpp:111
const std::string & getFilename() const
Definition STL.cpp:91
int readFacet(std::array< double, 3 > *V0, std::array< double, 3 > *V1, std::array< double, 3 > *V2, std::array< double, 3 > *N)
Definition STL.cpp:941
int readBegin()
Definition STL.cpp:713
int readFooter()
Definition STL.cpp:895
int inspect(InspectionInfo *info)
Definition STL.cpp:307
void displayInspectionInfo(const InspectionInfo &info, std::ostream &out) const
Definition STL.cpp:655
int readHeader(std::string *name, std::size_t *nT)
Definition STL.cpp:844
STLReader(const std::string &filename, Format format=FormatUnknown)
Definition STL.cpp:137
int readSolid(std::string *name, std::size_t *nV, std::size_t *nT, std::vector< std::array< double, 3 > > *V, std::vector< std::array< double, 3 > > *N, std::vector< std::array< std::size_t, 3 > > *T)
Definition STL.cpp:758
static Format detectFormat(const std::string &filename)
Definition STL.cpp:167
int writeFacet(const std::array< double, 3 > &V0, const std::array< double, 3 > &V1, const std::array< double, 3 > &V2, const std::array< double, 3 > &N)
Definition STL.cpp:1541
int writeFooter(const std::string &name)
Definition STL.cpp:1514
int writeHeader(const std::string &name, std::size_t nT)
Definition STL.cpp:1490
STLWriter(const std::string &filename, Format format)
Definition STL.cpp:1345
int writeBegin(WriteMode writeMode, bool partialWrite=false)
Definition STL.cpp:1361
#define BITPIT_UNUSED(variable)
Definition compiler.hpp:63
std::string & trim(std::string &s)
Logger & cout(log::Level defaultSeverity, log::Visibility defaultVisibility)
Definition logger.cpp:1714
Logger & error(log::Visibility defaultVisibility)
Definition logger.cpp:1786
Logger & info(log::Visibility defaultVisibility)
Definition logger.cpp:1856
Structure holding inspection information.
Definition STL.hpp:91