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
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
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 std::string name = sname.str();
391 info->solidNames[solidIndex] = name;
392
393 // Get solid info
394 inspectionError = inspectSolidASCII(info->solidFacetCount.data() + solidIndex, info->solidErrors.data() + solidIndex);
395 if (inspectionError != 0) {
396 break;
397 }
398
399 info->solidVertexCount[solidIndex] = 3 * info->solidFacetCount[solidIndex];
400 }
401 }
402
403 // Restore cursor position
404 m_fileHandle.clear();
405 m_fileHandle.seekg(start_pos);
406
407 return inspectionError;
408}
409
428int STLReader::inspectSolidASCII(std::size_t *nFacets, std::array<bool, 6> *errors)
429{
430 // Initialize solid information
431 (*nFacets) = 0;
432 errors->fill(false);
433
434 // Check stream status
435 if (!m_fileHandle.good()) {
436 return -1;
437 }
438
439 // Inspect the solid
440 std::string word;
441 std::streamoff last_valid_pos;
442
443 int inspectError = 0;
444 while (true) {
445 // Get next line
446 last_valid_pos = m_fileHandle.tellg();
447 m_lineStream.readLine(m_fileHandle);
448 if (!(m_lineStream >> word)) {
449 word = "";
450 }
451
452 // Exit conditions
453 if (m_fileHandle.eof()) {
454 break;
455 } else if (word.compare(ASCII_SOLID_END) == 0) {
456 break;
457 } else if (word.compare(ASCII_SOLID_BEGIN) == 0) {
458 break;
459 }
460
461 // Look for keyword "facet"
462 if (word.compare(ASCII_FACET_BEGIN) == 0) {
463 ++(*nFacets);
464
465 m_fileHandle.seekg(last_valid_pos);
466 inspectError = inspectFacetASCII(errors);
467 if (inspectError != 0) {
468 break;
469 }
470 }
471 }
472
473 // Check block temination
474 if (word.compare(ASCII_SOLID_END) != 0) {
475 (*errors)[0] = true;
476 m_fileHandle.clear();
477 m_fileHandle.seekg(last_valid_pos);
478 }
479
480 return inspectError;
481}
482
496int STLReader::inspectFacetASCII(std::array<bool, 6> *errors)
497{
498 // Check stream status
499 if (!m_fileHandle.good()) {
500 return -1;
501 }
502
503 // Check facet data
504 std::string word;
505 std::streamoff last_valid_pos;
506
507 last_valid_pos = m_fileHandle.tellg();
508 m_lineStream.readLine(m_fileHandle);
509 if ((!(m_lineStream >> word)) || (word.compare(ASCII_FACET_BEGIN) == 0)) {
510 word = "begin";
511 }
512
513 std::size_t nV = 0;
514 bool normal_found = false;
515 while ((!m_fileHandle.eof())
516 && ((word.compare(ASCII_FACET_END) != 0)
517 && (word.compare(ASCII_FACET_BEGIN) != 0)
518 && (word.compare(ASCII_SOLID_END) != 0)
519 && (word.compare(ASCII_SOLID_BEGIN) != 0))) {
520
521 // Check facet normal or facet vertices
522 if (word.compare("begin") == 0) {
523 if ((m_lineStream >> word) && (word.compare("normal") == 0)) {
524 normal_found = true;
525
526 int nxyz = 0;
527 while (m_lineStream >> word) {
528 nxyz++;
529 }
530
531 if (nxyz != 3) {
532 (*errors)[3] = true;
533 }
534 }
535 }
536 else if (word.compare("vertex") == 0) {
537 nV++;
538
539 int nxyz = 0;
540 while (m_lineStream >> word) {
541 nxyz++;
542 }
543
544 if (nxyz != 3) {
545 (*errors)[5] = true;
546 }
547 }
548
549 // Get next line
550 last_valid_pos = m_fileHandle.tellg();
551 m_lineStream.readLine(m_fileHandle);
552 if (!(m_lineStream >> word)) {
553 word = "";
554 }
555 }
556
557 // Check if facket section is properly closed
558 if (word.compare(ASCII_FACET_END) != 0) {
559 (*errors)[1] = true;
560 m_fileHandle.clear(),
561 m_fileHandle.seekg(last_valid_pos);
562 }
563
564 // Check if normal is valid
565 if (!normal_found) {
566 (*errors)[2] = true;
567 }
568
569 // Check if number of vertices is valid
570 if (nV != 3) {
571 (*errors)[4] = true;
572 }
573
574 return 0;
575}
576
586int STLReader::inspectBinary(InspectionInfo *info)
587{
588 // Initialize info
589 info->nSolids = 0;
590 info->solidErrors.clear();
591 info->solidNames.clear();
592 info->solidFacetCount.clear();
593 info->solidVertexCount.clear();
594
595 // Check stream status
596 if (!m_fileHandle.good()) {
597 return -1;
598 }
599
600 // Set cursor at file begin
601 m_fileHandle.clear();
602 std::streamoff start_pos = m_fileHandle.tellg();
603 m_fileHandle.seekg(0);
604
605 // Inspect header
606 for (std::size_t i = 0; i < BINARY_HEADER_SIZE / sizeof(BINARY_UINT8); ++i) {
607 BINARY_UINT8 headerCharacter;
608 m_fileHandle.read(reinterpret_cast<char*>(&headerCharacter), sizeof(BINARY_UINT8));
609 }
610
611 if (m_fileHandle.eof()) {
612 info->solidErrors[0][0] = true;
613 return 0;
614 }
615
616 // Binary files does not contain information about the solid name
617 info->solidNames.push_back("");
618
619 // Read number of facets
620 BINARY_UINT32 nFacets;
621 m_fileHandle.read(reinterpret_cast<char *>(&nFacets), sizeof(BINARY_UINT32));
622 info->solidFacetCount.push_back((std::size_t) nFacets);
623 info->solidVertexCount.push_back((std::size_t) (3 * nFacets));
624
625 if (m_fileHandle.eof()) {
626 info->solidErrors[0][0] = true;
627 return 0;
628 }
629
630 // Check facet data
631 std::size_t n = 0;
632 while ((!m_fileHandle.eof()) && (n < nFacets)) {
633 std::array<char, BINARY_FACET_SIZE> facetData;
634 m_fileHandle.read(facetData.data(), BINARY_FACET_SIZE);
635 n++;
636 }
637
638 // Check number of facets
639 if (n < nFacets) {
640 info->solidErrors[0][1] = true;
641 }
642
643 // Reset cursor position
644 m_fileHandle.clear();
645 m_fileHandle.seekg(start_pos);
646
647 return 0;
648}
649
656void STLReader::displayInspectionInfo(const InspectionInfo &info, std::ostream &out) const
657{
658 out << "Inspection info" << "\n";
659
660 out << " Filename : " << getFilename() << "\n";
661
662 Format format = getFormat();
663 if (format == FormatBinary) {
664 out << " Format : binary" << "\n";
665 } else {
666 out << " Format : ASCII" << "\n";
667 }
668
669 out << "\n";
670 if (info.nSolids > 0) {
671 out << " Solid count : " << info.nSolids << "\n";
672
673 for (int i = 0; i < info.nSolids; ++i) {
674 out << "\n";
675 out << " Solid index : " << i << "\n";
676 out << " Solid name : " << info.solidNames[i] << "\n";
677 out << " Solid facets : " << info.solidFacetCount[i] << "\n";
678 out << " Solid vertices : " << info.solidVertexCount[i] << "\n";
679
680 if (info.solidErrors[i][0]) {
681 out << " **ERROR** Unterminated solid block." << "\n";
682 }
683 if (info.solidErrors[i][1]) {
684 out << " **ERROR** Unterminated facet block." << "\n";
685 }
686 if (info.solidErrors[i][2]) {
687 out << " **ERROR** Normal data are missing." << "\n";
688 }
689 if (info.solidErrors[i][3]) {
690 out << " **ERROR** Wrong number of components for normal data." << "\n";
691 }
692 if (info.solidErrors[i][4]) {
693 out << " **ERROR** Wrong number of vertices in facet block." << "\n";
694 }
695 if (info.solidErrors[i][5]) {
696 out << " **ERROR** Wrong number of coordinates for vertice." << "\n";
697 }
698 }
699 } else {
700 out << " STL file contains no solids." << "\n";
701 }
702
703 out.flush();
704}
705
715{
716 if (m_fileHandle.is_open()) {
717 return -2;
718 }
719
720 m_fileHandle.open(getFilename(), std::ifstream::in | std::ifstream::binary);
721 if (!m_fileHandle.good()) {
722 return -1;
723 }
724
725 return 0;
726}
727
732{
733 m_fileHandle.close();
734
735 return 0;
736}
737
759int STLReader::readSolid(std::string *name, std::size_t *nV, std::size_t *nT,
760 std::vector<std::array<double, 3>> *V, std::vector<std::array<double, 3>> *N,
761 std::vector<std::array<std::size_t, 3>> *T)
762{
763 return readSolid("", name, nV, nT, V, N, T);
764}
765
790int STLReader::readSolid(const std::string &solid, std::string *name, std::size_t *nV, std::size_t *nT,
791 std::vector<std::array<double, 3>> *V, std::vector<std::array<double, 3>> *N,
792 std::vector<std::array<std::size_t, 3>> *T)
793{
794 // Read header
795 std::size_t nSolidFacets;
796 int headerError = readHeader(solid, name, &nSolidFacets);
797 if (headerError != 0) {
798 return headerError;
799 }
800
801 // Read facet data
802 V->resize(*nV + 3 * nSolidFacets, {{0., 0., 0.}});
803 N->resize(*nT + nSolidFacets, {{0., 0., 0.}});
804 T->resize(*nT + nSolidFacets, {{0, 0, 0}});
805
806 for (std::size_t i = 0; i < *nT; ++i) {
807 // Read facet data
808 std::array<double, 3> *V0 = V->data() + *nV + 3 * i;
809 std::array<double, 3> *V1 = V0 + 1;
810 std::array<double, 3> *V2 = V1 + 1;
811
812 int facetError = readFacet(V0, V1, V2, N->data() + i);
813 if (facetError != 0) {
814 return facetError;
815 }
816
817 // Update facet->vertex connectivity
818 (*T)[i][0] = *nV + 3 * i;
819 (*T)[i][1] = (*T)[i][0] + 1;
820 (*T)[i][2] = (*T)[i][1] + 1;
821 }
822
823 // Read footer
824 int footerError = readFooter(solid);
825 if (footerError != 0) {
826 return footerError;
827 }
828
829 return 0;
830}
831
845int STLReader::readHeader(std::string *name, std::size_t *nT)
846{
847 return readHeader("", name, nT);
848}
849
865int STLReader::readHeader(const std::string &solid, std::string *name, std::size_t *nT)
866{
867 Format format = getFormat();
868
869 int error;
870 if (format == FormatASCII) {
871 error = readHeaderASCII(solid, name, nT);
872 } else {
873 std::string trimmedSolid = solid;
874 utils::string::trim(trimmedSolid);
875 if (!trimmedSolid.empty()) {
876 log::cout() << "WARNING: loading solids with a specific name is only supported for ASCII files." << std::endl;
877 log::cout() << " The reader will read the next solid." << std::endl;
878 }
879
880 error = readHeaderBinary(name, nT);
881 }
882
883 return error;
884}
885
897{
898 return readFooter("");
899}
900
913int STLReader::readFooter(const std::string &solid)
914{
915 Format format = getFormat();
916
917 int error;
918 if (format == FormatASCII) {
919 error = readFooterASCII(solid);
920 } else {
921 error = readFooterBinary();
922 }
923
924 return error;
925}
926
942int STLReader::readFacet(std::array<double, 3> *V0, std::array<double, 3> *V1,
943 std::array<double, 3> *V2, std::array<double, 3> *N)
944{
945 Format format = getFormat();
946
947 int error;
948 if (format == FormatASCII) {
949 error = readFacetASCII(V0, V1, V2, N);
950 } else {
951 error = readFacetBinary(V0, V1, V2, N);
952 }
953
954 return error;
955}
956
972int STLReader::readHeaderASCII(const std::string &solid, std::string *name, std::size_t *nT)
973{
974 // Check stream status
975 if (!m_fileHandle.good()) {
976 return -1;
977 }
978
979 // Get solid key
980 std::string solidKey = solid;
981 utils::string::trim(solidKey);
982 solidKey = ASCII_SOLID_BEGIN + " " + solidKey;
983 utils::string::trim(solidKey);
984
985 // Scan file until solid is found
986 std::string word;
987 std::string line;
988
989 std::streamoff start_pos = m_fileHandle.tellg();
990 std::streamoff current_pos = start_pos + 1;
991
992 bool solidFound = false;
993 bool wrapAround = solidKey.compare(ASCII_SOLID_BEGIN) != 0;
994 while (!solidFound && (start_pos != current_pos)) {
995 // Get current line
996 m_lineStream.readLine(m_fileHandle);
997
998 // Check end of file
999 if (m_fileHandle.eof()) {
1000 if (wrapAround) {
1001 m_fileHandle.clear();
1002 m_fileHandle.seekg(0);
1003 wrapAround = false;
1004 } else {
1005 solidFound = false;
1006 break;
1007 }
1008 }
1009 current_pos = m_fileHandle.tellg();
1010
1011 // Look for keyword "solid"
1012 if ((m_lineStream >> word) && (word.compare(ASCII_SOLID_BEGIN) == 0)) {
1013 m_lineStream.copyLine(&line);
1014 if (solidKey.compare(ASCII_SOLID_BEGIN) == 0 || line.compare(solidKey) == 0) {
1015 *name = line.erase(0, ASCII_SOLID_BEGIN.size());
1016 *name = utils::string::trim(*name);
1017
1018 start_pos = current_pos;
1019
1020 solidFound = true;
1021 }
1022 }
1023 }
1024
1025 if (!solidFound) {
1026 return -2;
1027 }
1028
1029 // Read number of facets
1030 m_fileHandle.clear();
1031 m_fileHandle.seekg(start_pos);
1032
1033 std::array<bool, 6> solidErrors;
1034 inspectSolidASCII(nT, &solidErrors);
1035
1036 m_fileHandle.clear();
1037 m_fileHandle.seekg(start_pos);
1038
1039 return 0;
1040}
1041
1054int STLReader::readFooterASCII(const std::string &solid)
1055{
1056 // Check stream status
1057 if (!m_fileHandle.good()) {
1058 return -1;
1059 }
1060
1061 // Get solid key
1062 std::string solidKey = solid;
1063 utils::string::trim(solidKey);
1064 solidKey = ASCII_SOLID_END + " " + solidKey;
1065 utils::string::trim(solidKey);
1066
1067 // Look for the end of solid section
1068 std::string word;
1069 std::string line;
1070
1071 while (true) {
1072 // Get next line
1073 m_lineStream.readLine(m_fileHandle);
1074
1075 // Get next word
1076 if (!(m_lineStream >> word)) {
1077 word = "";
1078 }
1079
1080 // Handle the word
1081 if (word.compare(ASCII_SOLID_END) == 0) {
1082 m_lineStream.copyLine(&line);
1083 if (line.compare(solidKey) != 0) {
1084 log::cout() << "WARNING: end-solid key does not match the solid name." << std::endl;
1085 log::cout() << " Expected end-solid key : " << solidKey << std::endl;
1086 log::cout() << " Current end-solid key : " << line << std::endl;
1087 }
1088
1089 break;
1090 } else if (word.compare(ASCII_SOLID_BEGIN) == 0) {
1091 return -2;
1092 } else if (m_fileHandle.eof()) {
1093 return -2;
1094 }
1095 }
1096
1097 return 0;
1098}
1099
1115int STLReader::readFacetASCII(std::array<double, 3> *V0, std::array<double, 3> *V1,
1116 std::array<double, 3> *V2, std::array<double, 3> *N)
1117{
1118 // Read facet data
1119 std::string word;
1120 std::string value;
1121 std::streamoff last_valid_pos;
1122
1123 int error = 0;
1124 int nFacetVertices = 0;
1125 std::string target = "facet";
1126 while (true) {
1127 // Get next line
1128 last_valid_pos = m_fileHandle.tellg();
1129 m_lineStream.readLine(m_fileHandle);
1130 if (!(m_lineStream >> word)) {
1131 continue;
1132 }
1133
1134 // Handle the word
1135 //
1136 // Beginning of faacet section and normal are on the same line, after
1137 // finding the beginning of the facet section we have to extract the
1138 // next word without getting a new line.
1139 if (word.compare(ASCII_FACET_BEGIN) == 0) {
1140 if (word.compare(target) == 0) {
1141 target = "normal";
1142
1143 if (!(m_lineStream >> word)) {
1144 continue;
1145 }
1146 } else {
1147 error = -2;
1148 break;
1149 }
1150 }
1151
1152 if (word.compare(ASCII_FACET_END) == 0) {
1153 if (word.compare(target) == 0) {
1154 break;
1155 } else {
1156 error = -2;
1157 break;
1158 }
1159 } else if (word.compare("normal") == 0) {
1160 if (word.compare(target) == 0) {
1161 for (int k = 0; k < 3; ++k) {
1162 m_lineStream >> value;
1163 (*N)[k] = stod(value);
1164 }
1165 target = "vertex";
1166 } else {
1167 error = -2;
1168 break;
1169 }
1170 } else if (word.compare("vertex") == 0) {
1171 if (word.compare(target) == 0) {
1172 std::array<double, 3> *coords;
1173 if (nFacetVertices == 0) {
1174 coords = V0;
1175 } else if (nFacetVertices == 1) {
1176 coords = V1;
1177 } else if (nFacetVertices == 2) {
1178 coords = V2;
1179 target = ASCII_FACET_END;
1180 } else {
1181 error = -3;
1182 break;
1183 }
1184
1185 for (int k = 0; k < 3; ++k) {
1186 m_lineStream >> value;
1187 (*coords)[k] = stod(value);
1188 }
1189
1190 nFacetVertices++;
1191 } else {
1192 error = -2;
1193 break;
1194 }
1195 } else if (word.compare(ASCII_SOLID_BEGIN) == 0) {
1196 error = -2;
1197 break;
1198 } else if (word.compare(ASCII_SOLID_END) == 0) {
1199 error = -2;
1200 break;
1201 } else if (m_fileHandle.eof()) {
1202 error = -2;
1203 break;
1204 }
1205 }
1206
1207 // Restor cursor position
1208 if (error != 0) {
1209 m_fileHandle.clear();
1210 m_fileHandle.seekg(last_valid_pos);
1211 }
1212
1213 return error;
1214}
1215
1229int STLReader::readHeaderBinary(std::string *name, std::size_t *nT)
1230{
1231 // Check stream status
1232 if (!m_fileHandle.good()) {
1233 return -1;
1234 }
1235
1236 // Binary data has not information about solid names
1237 (*name) = "";
1238
1239 // Skip header
1240 for (std::size_t i = 0; i < BINARY_HEADER_SIZE / sizeof(BINARY_UINT8); ++i) {
1241 BINARY_UINT8 headerCharacter;
1242 m_fileHandle.read(reinterpret_cast<char *>(&headerCharacter), sizeof(BINARY_UINT8));
1243 }
1244
1245 // Read number of facets
1246 BINARY_UINT32 nSolidFacets;
1247 m_fileHandle.read(reinterpret_cast<char *>(&nSolidFacets), sizeof(BINARY_UINT32));
1248 *nT = nSolidFacets;
1249
1250 // Check if the end of file has been reached
1251 if (m_fileHandle.eof()) {
1252 return -2;
1253 }
1254
1255 return 0;
1256}
1257
1267int STLReader::readFooterBinary()
1268{
1269 // Check stream status
1270 if (!m_fileHandle.good()) {
1271 return -1;
1272 }
1273
1274 // Nothing to do
1275 return 0;
1276}
1277
1292int STLReader::readFacetBinary(std::array<double, 3> *V0, std::array<double, 3> *V1,
1293 std::array<double, 3> *V2, std::array<double, 3> *N)
1294{
1295 // Check stream status
1296 if (!m_fileHandle.good()) {
1297 return -1;
1298 }
1299
1300 // Read facet data
1301 std::array<char, BINARY_FACET_SIZE> facetData;
1302 m_fileHandle.read(facetData.data(), BINARY_FACET_SIZE);
1303 int facetDataOffset = 0;
1304
1305 // Store normal
1306 for (int k = 0; k < 3; ++k) {
1307 (*N)[k] = (double) *(reinterpret_cast<BINARY_REAL32 *>(facetData.data() + facetDataOffset));
1308 facetDataOffset += sizeof(BINARY_REAL32);
1309 }
1310
1311 // Store vertex coordinates
1312 for (int k = 0; k < 3; ++k) {
1313 (*V0)[k] = (double) *(reinterpret_cast<BINARY_REAL32 *>(facetData.data() + facetDataOffset));
1314 facetDataOffset += sizeof(BINARY_REAL32);
1315 }
1316
1317 for (int k = 0; k < 3; ++k) {
1318 (*V1)[k] = (double) *(reinterpret_cast<BINARY_REAL32 *>(facetData.data() + facetDataOffset));
1319 facetDataOffset += sizeof(BINARY_REAL32);
1320 }
1321
1322 for (int k = 0; k < 3; ++k) {
1323 (*V2)[k] = (double) *(reinterpret_cast<BINARY_REAL32 *>(facetData.data() + facetDataOffset));
1324 facetDataOffset += sizeof(BINARY_REAL32);
1325 }
1326
1327 // Check if the end of file has been reached
1328 if (m_fileHandle.eof()) {
1329 return -2;
1330 }
1331
1332 return 0;
1333}
1334
1346STLWriter::STLWriter(const std::string &filename, Format format)
1347 : STLBase(filename, format)
1348{
1349 if (format == FormatUnknown) {
1350 throw std::runtime_error("Invalid STL format.");
1351 }
1352}
1353
1362int STLWriter::writeBegin(WriteMode writeMode, bool partialWrite)
1363{
1364 if (m_fileHandle.is_open()) {
1365 return -2;
1366 }
1367
1368 Format format = getFormat();
1369
1370 std::ios_base::openmode openMode;
1371 if (format == FormatBinary) {
1372 if (writeMode == WriteOverwrite) {
1373 openMode = std::ofstream::out | std::ofstream::binary;
1374 } else if (partialWrite && writeMode == WriteAppend) {
1375 openMode = std::ofstream::app | std::ofstream::binary;
1376 } else {
1377 throw std::runtime_error("Specified write mode is not supported for binary files.");
1378 }
1379 } else {
1380 if (writeMode == WriteOverwrite) {
1381 openMode = std::ofstream::out;
1382 } else if (writeMode == WriteAppend) {
1383 openMode = std::ofstream::app;
1384 } else {
1385 throw std::runtime_error("Specified write mode is not supported for ASCII files.");
1386 }
1387 }
1388
1389 m_fileHandle.open(getFilename(), openMode);
1390 if (!m_fileHandle.good()) {
1391 return -1;
1392 }
1393
1394 // Set stream properties
1395 m_fileHandle << std::scientific;
1396
1397 return 0;
1398}
1399
1404{
1405 m_fileHandle.close();
1406
1407 return 0;
1408}
1409
1426int STLWriter::writeSolid(const std::string &name, std::size_t nV, std::size_t nT,
1427 const std::vector<std::array<double,3>> &V, const std::vector<std::array<double,3>> &N,
1428 const std::vector<std::array<std::size_t,3>> &T)
1429{
1430 // Check input variables
1431 if (V.size() < nV) {
1432 return -2;
1433 }
1434
1435 if (T.size() < nT) {
1436 return -2;
1437 }
1438
1439 if (N.size() < nT) {
1440 return -2;
1441 }
1442
1443 // Save stream flags
1444 std::ios::fmtflags streamFlags(m_fileHandle.flags());
1445
1446 // Write header
1447 int headerError = writeHeader(name, nT);
1448 if (headerError != 0) {
1449 return headerError;
1450 }
1451
1452 // Write facet data
1453 for (std::size_t i = 0; i < nT; ++i) {
1454 // Check connectivity
1455 for (int k = 0; k < 3; ++k) {
1456 if (T[i][k] >= nV) {
1457 return -2;
1458 }
1459 }
1460
1461 // Write data
1462 int facetError = writeFacet(V[T[i][0]], V[T[i][1]], V[T[i][2]], N[i]);
1463 if (facetError != 0) {
1464 return facetError;
1465 }
1466 }
1467
1468 // Write footer
1469 int footerError = writeFooter(name);
1470 if (footerError != 0) {
1471 return footerError;
1472 }
1473
1474 // Restore stream flags
1475 m_fileHandle.flags(streamFlags);
1476
1477 return 0;
1478}
1479
1491int STLWriter::writeHeader(const std::string &name, std::size_t nT)
1492{
1493 Format format = getFormat();
1494
1495 int error;
1496 if (format == FormatASCII) {
1497 error = writeHeaderASCII(name, nT);
1498 } else {
1499 error = writeHeaderBinary(name, nT);
1500 }
1501
1502 return error;
1503}
1504
1515int STLWriter::writeFooter(const std::string &name)
1516{
1517 Format format = getFormat();
1518
1519 int error;
1520 if (format == FormatASCII) {
1521 error = writeFooterASCII(name);
1522 } else {
1523 error = writeFooterBinary(name);
1524 }
1525
1526 return error;
1527}
1528
1542int STLWriter::writeFacet(const std::array<double, 3> &V0, const std::array<double, 3> &V1,
1543 const std::array<double, 3> &V2, const std::array<double, 3> &N)
1544{
1545 Format format = getFormat();
1546
1547 int error;
1548 if (format == FormatASCII) {
1549 error = writeFacetASCII(V0, V1, V2, N);
1550 } else {
1551 error = writeFacetBinary(V0, V1, V2, N);
1552 }
1553
1554 return error;
1555}
1556
1568int STLWriter::writeHeaderASCII(const std::string &name, std::size_t nT)
1569{
1570 BITPIT_UNUSED(nT);
1571
1572 // Check stream status
1573 if (!m_fileHandle.good()) {
1574 return -1;
1575 }
1576
1577 // Write header
1578 std::stringstream sheader;
1579 sheader << ASCII_SOLID_BEGIN << " " << name;
1580
1581 // Write number of facets
1582 std::string header = sheader.str();
1583 header = utils::string::trim(header);
1584 m_fileHandle << header << "\n";
1585
1586 return 0;
1587}
1588
1599int STLWriter::writeFooterASCII(const std::string &name)
1600{
1601 BITPIT_UNUSED(name);
1602
1603 // Check stream status
1604 if (!m_fileHandle.good()) {
1605 return -1;
1606 }
1607
1608 // Write footer
1609 std::stringstream sfooter;
1610 sfooter << ASCII_SOLID_END << " " << name;
1611
1612 std::string footer;
1613 footer = sfooter.str();
1614 footer = utils::string::trim(footer);
1615 m_fileHandle << footer << "\n";
1616
1617 return 0;
1618}
1619
1633int STLWriter::writeFacetASCII(const std::array<double, 3> &V0, const std::array<double, 3> &V1,
1634 const std::array<double, 3> &V2, const std::array<double, 3> &N)
1635{
1636 // Check stream status
1637 if (!m_fileHandle.good()) {
1638 return -1;
1639 }
1640
1641 // Facet header
1642 m_fileHandle << " " << ASCII_FACET_BEGIN;
1643
1644 // Facet normal
1645 m_fileHandle << " normal ";
1646 m_fileHandle << N[0] << " ";
1647 m_fileHandle << N[1] << " ";
1648 m_fileHandle << N[2];
1649 m_fileHandle << "\n";
1650
1651 // Facet vertices
1652 m_fileHandle << " outer loop" << "\n";
1653
1654 m_fileHandle << " vertex ";
1655 m_fileHandle << V0[0] << " ";
1656 m_fileHandle << V0[1] << " ";
1657 m_fileHandle << V0[2];
1658 m_fileHandle << "\n";
1659
1660 m_fileHandle << " vertex ";
1661 m_fileHandle << V1[0] << " ";
1662 m_fileHandle << V1[1] << " ";
1663 m_fileHandle << V1[2];
1664 m_fileHandle << "\n";
1665
1666 m_fileHandle << " vertex ";
1667 m_fileHandle << V2[0] << " ";
1668 m_fileHandle << V2[1] << " ";
1669 m_fileHandle << V2[2];
1670 m_fileHandle << "\n";
1671
1672 m_fileHandle << " endloop" << "\n";
1673
1674 // Facet footer
1675 m_fileHandle << " " << ASCII_FACET_END << "\n";
1676
1677 return 0;
1678}
1679
1691int STLWriter::writeHeaderBinary(const std::string &name, std::size_t nT)
1692{
1693 BITPIT_UNUSED(name);
1694
1695 // Check stream status
1696 if (!m_fileHandle.good()) {
1697 return -1;
1698 }
1699
1700 // Write header
1701 for (std::size_t i = 0; i < BINARY_HEADER_SIZE / sizeof(BINARY_UINT8); ++i) {
1702 BINARY_UINT8 headerCharacter = 0;
1703 m_fileHandle.write(reinterpret_cast<char*>(&headerCharacter), sizeof(BINARY_UINT8));
1704 }
1705
1706 // Write number of facets
1707 BINARY_UINT32 nFacets = (BINARY_UINT32) nT;
1708 m_fileHandle.write(reinterpret_cast<char *>(&nFacets), sizeof(BINARY_UINT32));
1709
1710 return 0;
1711}
1712
1723int STLWriter::writeFooterBinary(const std::string &name)
1724{
1725 BITPIT_UNUSED(name);
1726
1727 // Check stream status
1728 if (!m_fileHandle.good()) {
1729 return -1;
1730 }
1731
1732 // Nothing to do
1733 return 0;
1734}
1735
1749int STLWriter::writeFacetBinary(const std::array<double, 3> &V0, const std::array<double, 3> &V1,
1750 const std::array<double, 3> &V2, const std::array<double, 3> &N)
1751{
1752 // Check stream status
1753 if (!m_fileHandle.good()) {
1754 return -1;
1755 }
1756
1757 // Normals
1758 for (int k = 0; k < 3; ++k) {
1759 BINARY_REAL32 N_k = (BINARY_REAL32) N[k];
1760 m_fileHandle.write(reinterpret_cast<char *>(&N_k), sizeof(BINARY_REAL32));
1761 }
1762
1763 // Vertices
1764 for (int k = 0; k < 3; ++k) {
1765 BINARY_REAL32 V_k = (BINARY_REAL32) V0[k];
1766 m_fileHandle.write(reinterpret_cast<char *>(&V_k), sizeof(BINARY_REAL32));
1767 }
1768
1769 for (int k = 0; k < 3; ++k) {
1770 BINARY_REAL32 V_k = (BINARY_REAL32) V1[k];
1771 m_fileHandle.write(reinterpret_cast<char *>(&V_k), sizeof(BINARY_REAL32));
1772 }
1773
1774 for (int k = 0; k < 3; ++k) {
1775 BINARY_REAL32 V_k = (BINARY_REAL32) V2[k];
1776 m_fileHandle.write(reinterpret_cast<char *>(&V_k), sizeof(BINARY_REAL32));
1777 }
1778
1779 // Attribute byte count
1780 //
1781 // In the standard format, this should be zero because most software
1782 // does not understand anything else.
1783 // Attribute byte count
1784 BINARY_UINT16 attributeByteCount = 0;
1785 m_fileHandle.write(reinterpret_cast<char *>(&attributeByteCount), sizeof(BINARY_UINT16));
1786
1787 return 0;
1788}
1789
1790}
int readLine(std::ifstream &fileHandle)
void copyLine(std::string *line) const
Base class for the STL writer and the STL reader.
Definition STL.hpp:39
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:942
int readBegin()
Definition STL.cpp:714
int readFooter()
Definition STL.cpp:896
int inspect(InspectionInfo *info)
Definition STL.cpp:307
void displayInspectionInfo(const InspectionInfo &info, std::ostream &out) const
Definition STL.cpp:656
int readHeader(std::string *name, std::size_t *nT)
Definition STL.cpp:845
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:759
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:1542
int writeFooter(const std::string &name)
Definition STL.cpp:1515
int writeHeader(const std::string &name, std::size_t nT)
Definition STL.cpp:1491
STLWriter(const std::string &filename, Format format)
Definition STL.cpp:1346
int writeBegin(WriteMode writeMode, bool partialWrite=false)
Definition STL.cpp:1362
#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:1705
Logger & error(log::Visibility defaultVisibility)
Definition logger.cpp:1777
Logger & info(log::Visibility defaultVisibility)
Definition logger.cpp:1847
Structure holding inspection information.
Definition STL.hpp:91
--- layout: doxygen_footer ---