1
/***************************************************************************
2
* Copyright (c) 2005 Imetric 3D GmbH *
4
* This file is part of the FreeCAD CAx development system. *
6
* This library is free software; you can redistribute it and/or *
7
* modify it under the terms of the GNU Library General Public *
8
* License as published by the Free Software Foundation; either *
9
* version 2 of the License, or (at your option) any later version. *
11
* This library is distributed in the hope that it will be useful, *
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14
* GNU Library General Public License for more details. *
16
* You should have received a copy of the GNU Library General Public *
17
* License along with this library; see the file COPYING.LIB. If not, *
18
* write to the Free Software Foundation, Inc., 59 Temple Place, *
19
* Suite 330, Boston, MA 02111-1307, USA *
21
***************************************************************************/
23
#include "PreCompiled.h"
33
#include <boost/algorithm/string.hpp>
34
#include <boost/convert.hpp>
35
#include <boost/convert/spirit.hpp>
36
#include <boost/lexical_cast.hpp>
37
#include <boost/regex.hpp>
39
#include "IO/Reader3MF.h"
40
#include "IO/ReaderOBJ.h"
41
#include "IO/Writer3MF.h"
42
#include "IO/WriterInventor.h"
43
#include "IO/WriterOBJ.h"
44
#include <Base/Builder3D.h>
45
#include <Base/Console.h>
46
#include <Base/Exception.h>
47
#include <Base/FileInfo.h>
48
#include <Base/Placement.h>
49
#include <Base/Reader.h>
50
#include <Base/Sequencer.h>
51
#include <Base/Stream.h>
52
#include <Base/Tools.h>
53
#include <Base/Writer.h>
54
#include <zipios++/gzipoutputstream.h>
55
#include <zipios++/zipoutputstream.h>
58
#include "Definitions.h"
59
#include "Degeneration.h"
62
#include "MeshKernel.h"
65
using namespace MeshCore;
70
std::string& ltrim(std::string& str)
72
std::string::size_type pos = 0;
74
if (it != 0x20 && it != 0x09) {
80
str = str.substr(pos);
85
int numDigits(int number)
87
number = std::abs(number);
90
while (step <= number) {
97
/* Usage by CMeshNastran, CMeshCadmouldFE. Added by Sergey Sukhov (26.04.2002)*/
111
} // namespace MeshCore
113
// --------------------------------------------------------------
115
bool Material::operator==(const Material& mat) const
117
if (binding != mat.binding) {
120
if (ambientColor != mat.ambientColor) {
123
if (diffuseColor != mat.diffuseColor) {
126
if (specularColor != mat.specularColor) {
129
if (emissiveColor != mat.emissiveColor) {
132
if (shininess != mat.shininess) {
135
if (transparency != mat.transparency) {
141
bool Material::operator!=(const Material& mat) const
143
return !operator==(mat);
146
// --------------------------------------------------------------
148
std::vector<std::string> MeshInput::supportedMeshFormats()
150
std::vector<std::string> fmt;
151
fmt.emplace_back("bms");
152
fmt.emplace_back("ply");
153
fmt.emplace_back("stl");
154
fmt.emplace_back("ast");
155
fmt.emplace_back("obj");
156
fmt.emplace_back("nas");
157
fmt.emplace_back("bdf");
158
fmt.emplace_back("off");
159
fmt.emplace_back("smf");
163
MeshIO::Format MeshInput::getFormat(const char* FileName)
165
Base::FileInfo fi(FileName);
166
if (fi.hasExtension("bms")) {
167
return MeshIO::Format::BMS;
169
else if (fi.hasExtension("ply")) {
170
return MeshIO::Format::PLY;
172
else if (fi.hasExtension("stl")) {
173
return MeshIO::Format::STL;
175
else if (fi.hasExtension("ast")) {
176
return MeshIO::Format::ASTL;
178
else if (fi.hasExtension("obj")) {
179
return MeshIO::Format::OBJ;
181
else if (fi.hasExtension("off")) {
182
return MeshIO::Format::OFF;
184
else if (fi.hasExtension("smf")) {
185
return MeshIO::Format::SMF;
188
throw Base::FileException("File extension not supported", FileName);
192
bool MeshInput::LoadAny(const char* FileName)
194
// ask for read permission
195
Base::FileInfo fi(FileName);
196
if (!fi.exists() || !fi.isFile()) {
197
throw Base::FileException("File does not exist", FileName);
199
if (!fi.isReadable()) {
200
throw Base::FileException("No permission on the file", FileName);
203
Base::ifstream str(fi, std::ios::in | std::ios::binary);
205
if (fi.hasExtension("bms")) {
212
if (fi.hasExtension({"stl", "ast"})) {
215
else if (fi.hasExtension("iv")) {
216
ok = LoadInventor(str);
217
if (ok && _rclMesh.CountFacets() == 0) {
218
Base::Console().Warning("No usable mesh found in file '%s'", FileName);
221
else if (fi.hasExtension({"nas", "bdf"})) {
222
ok = LoadNastran(str);
224
else if (fi.hasExtension("obj")) {
225
ok = LoadOBJ(str, FileName);
227
else if (fi.hasExtension("smf")) {
230
else if (fi.hasExtension("3mf")) {
233
else if (fi.hasExtension("off")) {
236
else if (fi.hasExtension("ply")) {
240
throw Base::FileException("File extension not supported", FileName);
247
bool MeshInput::LoadFormat(std::istream& str, MeshIO::Format fmt)
257
return LoadAsciiSTL(str);
259
return LoadBinarySTL(str);
266
case MeshIO::ThreeMF:
271
return LoadInventor(str);
273
return LoadNastran(str);
275
throw Base::FileException("Unsupported file format");
279
/** Loads an STL file either in binary or ASCII format.
280
* Therefore the file header gets checked to decide if the file is binary or not.
282
bool MeshInput::LoadSTL(std::istream& rstrIn)
286
if (!rstrIn || rstrIn.bad()) {
290
// Read in 50 characters from position 80 on and check for keywords like 'SOLID', 'FACET',
291
// 'NORMAL', 'VERTEX', 'ENDFACET' or 'ENDLOOP'. As the file can be binary with one triangle only
292
// we must not read in more than (max.) 54 bytes because the file size has only 134 bytes in
293
// this case. On the other hand we must overread the first 80 bytes because it can happen that
294
// the file is binary but contains one of these keywords.
295
std::streambuf* buf = rstrIn.rdbuf();
299
buf->pubseekoff(80, std::ios::beg, std::ios::in);
300
uint32_t ulCt {}, ulBytes = 50;
301
rstrIn.read((char*)&ulCt, sizeof(ulCt));
302
// if we have a binary STL with a single triangle we can only read-in 50 bytes
306
// Either it's really an invalid STL file or it's just empty. In this case the number of facets
308
if (!rstrIn.read(szBuf, ulBytes)) {
312
boost::algorithm::to_upper(szBuf);
315
if (!strstr(szBuf, "SOLID") && !strstr(szBuf, "FACET") && !strstr(szBuf, "NORMAL")
316
&& !strstr(szBuf, "VERTEX") && !strstr(szBuf, "ENDFACET")
317
&& !strstr(szBuf, "ENDLOOP")) {
318
// probably binary STL
319
buf->pubseekoff(0, std::ios::beg, std::ios::in);
320
return LoadBinarySTL(rstrIn);
324
buf->pubseekoff(0, std::ios::beg, std::ios::in);
325
return LoadAsciiSTL(rstrIn);
328
catch (const Base::MemoryException&) {
330
throw; // Throw the same instance of Base::MemoryException
332
catch (const Base::AbortException&) {
336
catch (const Base::Exception&) {
338
throw; // Throw the same instance of Base::Exception
348
/** Loads an OBJ file. */
349
bool MeshInput::LoadOBJ(std::istream& rstrIn)
351
ReaderOBJ reader(this->_rclMesh, this->_material);
352
if (reader.Load(rstrIn)) {
353
_groupNames = reader.GetGroupNames();
360
bool MeshInput::LoadOBJ(std::istream& str, const char* filename)
362
ReaderOBJ reader(this->_rclMesh, this->_material);
363
if (reader.Load(str)) {
364
_groupNames = reader.GetGroupNames();
365
if (this->_material && this->_material->binding == MeshCore::MeshIO::PER_FACE) {
366
Base::FileInfo fi(filename);
367
std::string fn = fi.dirPath() + "/" + this->_material->library;
369
Base::ifstream mtl(fi, std::ios::in | std::ios::binary);
370
reader.LoadMaterial(mtl);
380
/** Loads an SMF file. */
381
bool MeshInput::LoadSMF(std::istream& rstrIn)
383
boost::regex rx_p("^v\\s+([-+]?[0-9]*)\\.?([0-9]+([eE][-+]?[0-9]+)?)"
384
"\\s+([-+]?[0-9]*)\\.?([0-9]+([eE][-+]?[0-9]+)?)"
385
"\\s+([-+]?[0-9]*)\\.?([0-9]+([eE][-+]?[0-9]+)?)\\s*$");
386
boost::regex rx_f3("^f\\s+([-+]?[0-9]+)"
388
"\\s+([-+]?[0-9]+)\\s*$");
391
unsigned long segment = 0;
392
MeshPointArray meshPoints;
393
MeshFacetArray meshFacets;
396
float fX {}, fY {}, fZ {};
397
int i1 = 1, i2 = 1, i3 = 1;
400
if (!rstrIn || rstrIn.bad()) {
404
std::streambuf* buf = rstrIn.rdbuf();
409
while (std::getline(rstrIn, line)) {
410
if (boost::regex_match(line.c_str(), what, rx_p)) {
411
fX = (float)std::atof(what[1].first);
412
fY = (float)std::atof(what[4].first);
413
fZ = (float)std::atof(what[7].first);
414
meshPoints.push_back(MeshPoint(Base::Vector3f(fX, fY, fZ)));
416
else if (boost::regex_match(line.c_str(), what, rx_f3)) {
418
i1 = std::atoi(what[1].first);
419
i1 = i1 > 0 ? i1 - 1 : i1 + static_cast<int>(meshPoints.size());
420
i2 = std::atoi(what[2].first);
421
i2 = i2 > 0 ? i2 - 1 : i2 + static_cast<int>(meshPoints.size());
422
i3 = std::atoi(what[3].first);
423
i3 = i3 > 0 ? i3 - 1 : i3 + static_cast<int>(meshPoints.size());
424
item.SetVertices(i1, i2, i3);
425
item.SetProperty(segment);
426
meshFacets.push_back(item);
430
this->_rclMesh.Clear(); // remove all data before
432
MeshCleanup meshCleanup(meshPoints, meshFacets);
433
meshCleanup.RemoveInvalids();
434
MeshPointFacetAdjacency meshAdj(meshPoints.size(), meshFacets);
435
meshAdj.SetFacetNeighbourhood();
436
this->_rclMesh.Adopt(meshPoints, meshFacets);
441
/** Loads an OFF file. */
442
bool MeshInput::LoadOFF(std::istream& rstrIn)
444
// http://edutechwiki.unige.ch/en/3D_file_format
445
boost::regex rx_n(R"(^\s*([0-9]+)\s+([0-9]+)\s+([0-9]+)\s*$)");
448
bool colorPerVertex = false;
449
std::vector<App::Color> diffuseColor;
450
MeshPointArray meshPoints;
451
MeshFacetArray meshFacets;
456
if (!rstrIn || rstrIn.bad()) {
460
std::streambuf* buf = rstrIn.rdbuf();
465
std::getline(rstrIn, line);
466
boost::algorithm::to_lower(line);
467
if (line.find("coff") != std::string::npos) {
468
// we expect colors to be there per vertex: x y z r g b a
469
colorPerVertex = true;
471
else if (line.find("off") == std::string::npos) {
472
return false; // not an OFF file
475
// get number of vertices and faces
476
int numPoints = 0, numFaces = 0;
479
std::getline(rstrIn, line);
480
boost::algorithm::to_lower(line);
481
if (boost::regex_match(line.c_str(), what, rx_n)) {
482
numPoints = std::atoi(what[1].first);
483
numFaces = std::atoi(what[2].first);
488
if (numPoints == 0 || numFaces == 0) {
492
meshPoints.reserve(numPoints);
493
meshFacets.reserve(numFaces);
494
if (colorPerVertex) {
495
diffuseColor.reserve(numPoints);
498
diffuseColor.reserve(numFaces);
502
while (cntPoints < numPoints) {
503
if (!std::getline(rstrIn, line)) {
506
std::istringstream str(line);
507
str.unsetf(std::ios_base::skipws);
510
continue; // empty line
513
float fX {}, fY {}, fZ {};
514
str >> fX >> std::ws >> fY >> std::ws >> fZ;
516
meshPoints.push_back(MeshPoint(Base::Vector3f(fX, fY, fZ)));
519
if (colorPerVertex) {
520
std::size_t pos = std::size_t(str.tellg());
521
if (line.size() > pos) {
522
float r {}, g {}, b {}, a {};
523
str >> std::ws >> r >> std::ws >> g >> std::ws >> b;
531
if (r > 1.0f || g > 1.0f || b > 1.0f || a > 1.0f) {
532
r = static_cast<float>(r) / 255.0f;
533
g = static_cast<float>(g) / 255.0f;
534
b = static_cast<float>(b) / 255.0f;
535
a = static_cast<float>(a) / 255.0f;
537
diffuseColor.emplace_back(r, g, b, a);
545
while (cntFaces < numFaces) {
546
if (!std::getline(rstrIn, line)) {
549
std::istringstream str(line);
550
str.unsetf(std::ios_base::skipws);
553
continue; // empty line
555
int count {}, index {};
558
std::vector<int> faces;
559
faces.reserve(count);
561
for (int i = 0; i < count; i++) {
564
faces.push_back(index);
567
for (int i = 0; i < count - 2; i++) {
568
item.SetVertices(faces[0], faces[i + 1], faces[i + 2]);
569
meshFacets.push_back(item);
573
std::size_t pos = std::size_t(str.tellg());
574
if (line.size() > pos) {
575
float r {}, g {}, b {}, a {};
576
str >> std::ws >> r >> std::ws >> g >> std::ws >> b;
584
if (r > 1.0f || g > 1.0f || b > 1.0f || a > 1.0f) {
585
r = static_cast<float>(r) / 255.0f;
586
g = static_cast<float>(g) / 255.0f;
587
b = static_cast<float>(b) / 255.0f;
588
a = static_cast<float>(a) / 255.0f;
590
for (int i = 0; i < count - 2; i++) {
591
diffuseColor.emplace_back(r, g, b, a);
599
if (colorPerVertex) {
600
if (meshPoints.size() == diffuseColor.size()) {
601
_material->binding = MeshIO::PER_VERTEX;
602
_material->diffuseColor.swap(diffuseColor);
606
if (meshFacets.size() == diffuseColor.size()) {
607
_material->binding = MeshIO::PER_FACE;
608
_material->diffuseColor.swap(diffuseColor);
612
this->_rclMesh.Clear(); // remove all data before
614
MeshCleanup meshCleanup(meshPoints, meshFacets);
616
meshCleanup.SetMaterial(_material);
618
meshCleanup.RemoveInvalids();
619
MeshPointFacetAdjacency meshAdj(meshPoints.size(), meshFacets);
620
meshAdj.SetFacetNeighbourhood();
621
this->_rclMesh.Adopt(meshPoints, meshFacets);
643
using first_argument_type = std::pair<std::string, int>;
644
using second_argument_type = std::string;
645
using result_type = bool;
647
bool operator()(const std::pair<std::string, int>& x, const std::string& y) const
654
} // namespace MeshCore
656
bool MeshInput::LoadPLY(std::istream& inp)
658
// http://local.wasp.uwa.edu.au/~pbourke/dataformats/ply/
659
std::size_t v_count = 0, f_count = 0;
660
MeshPointArray meshPoints;
661
MeshFacetArray meshFacets;
667
binary_little_endian,
671
if (!inp || inp.bad()) {
675
std::streambuf* buf = inp.rdbuf();
680
// read in the first three characters
687
if ((ply[0] != 'p') || (ply[1] != 'l') || (ply[2] != 'y')) {
688
return false; // wrong header
691
std::vector<std::pair<std::string, Ply::Number>> vertex_props;
692
std::vector<Ply::Number> face_props;
693
std::string line, element;
695
MeshIO::Binding rgb_value = MeshIO::OVERALL;
696
while (std::getline(inp, line)) {
697
std::istringstream str(line);
698
str.unsetf(std::ios_base::skipws);
701
continue; // empty line
705
if (kw == "format") {
706
std::string format_string, version;
707
char space_format_string {}, space_format_version {};
708
str >> space_format_string >> std::ws >> format_string >> space_format_version
709
>> std::ws >> version;
710
if (/*!str || !str.eof() ||*/
711
!std::isspace(space_format_string) || !std::isspace(space_format_version)) {
714
if (format_string == "ascii") {
717
else if (format_string == "binary_big_endian") {
718
format = binary_big_endian;
720
else if (format_string == "binary_little_endian") {
721
format = binary_little_endian;
724
// wrong format version
727
if (version != "1.0") {
732
else if (kw == "element") {
734
std::size_t count {};
735
char space_element_name {}, space_name_count {};
736
str >> space_element_name >> std::ws >> name >> space_name_count >> std::ws >> count;
737
if (/*!str || !str.eof() ||*/
738
!std::isspace(space_element_name) || !std::isspace(space_name_count)) {
741
else if (name == "vertex") {
744
meshPoints.reserve(count);
746
else if (name == "face") {
749
meshFacets.reserve(count);
755
else if (kw == "property") {
756
std::string type, name;
758
if (element == "vertex") {
759
str >> space >> std::ws >> type >> space >> std::ws >> name >> std::ws;
761
Ply::Number number {};
762
if (type == "char" || type == "int8") {
765
else if (type == "uchar" || type == "uint8") {
768
else if (type == "short" || type == "int16") {
771
else if (type == "ushort" || type == "uint16") {
774
else if (type == "int" || type == "int32") {
777
else if (type == "uint" || type == "uint32") {
780
else if (type == "float" || type == "float32") {
783
else if (type == "double" || type == "float64") {
787
// no valid number type
791
// store the property name and type
792
vertex_props.emplace_back(name, number);
794
else if (element == "face") {
795
std::string list, uchr;
796
str >> space >> std::ws >> list >> std::ws;
797
if (list == "list") {
798
str >> uchr >> std::ws >> type >> std::ws >> name >> std::ws;
805
if (name != "vertex_indices" && name != "vertex_index") {
807
if (type == "char" || type == "int8") {
810
else if (type == "uchar" || type == "uint8") {
813
else if (type == "short" || type == "int16") {
816
else if (type == "ushort" || type == "uint16") {
819
else if (type == "int" || type == "int32") {
822
else if (type == "uint" || type == "uint32") {
825
else if (type == "float" || type == "float32") {
828
else if (type == "double" || type == "float64") {
832
// no valid number type
836
// store the property name and type
837
face_props.push_back(number);
841
else if (kw == "end_header") {
842
break; // end of the header, now read the data
846
// check if valid 3d points
848
std::size_t num_x = std::count_if(vertex_props.begin(),
850
[&property](const std::pair<std::string, int>& p) {
851
return property(p, "x");
857
std::size_t num_y = std::count_if(vertex_props.begin(),
859
[&property](const std::pair<std::string, int>& p) {
860
return property(p, "y");
866
std::size_t num_z = std::count_if(vertex_props.begin(),
868
[&property](const std::pair<std::string, int>& p) {
869
return property(p, "z");
875
for (auto& it : vertex_props) {
876
if (it.first == "diffuse_red") {
879
else if (it.first == "diffuse_green") {
882
else if (it.first == "diffuse_blue") {
887
// check if valid colors are set
888
std::size_t num_r = std::count_if(vertex_props.begin(),
890
[&property](const std::pair<std::string, int>& p) {
891
return property(p, "red");
893
std::size_t num_g = std::count_if(vertex_props.begin(),
895
[&property](const std::pair<std::string, int>& p) {
896
return property(p, "green");
898
std::size_t num_b = std::count_if(vertex_props.begin(),
900
[&property](const std::pair<std::string, int>& p) {
901
return property(p, "blue");
903
std::size_t rgb_colors = num_r + num_g + num_b;
904
if (rgb_colors != 0 && rgb_colors != 3) {
908
// only if set per vertex
909
if (rgb_colors == 3) {
910
rgb_value = MeshIO::PER_VERTEX;
912
_material->binding = MeshIO::PER_VERTEX;
913
_material->diffuseColor.reserve(v_count);
917
if (format == ascii) {
918
boost::regex rx_d("(([-+]?[0-9]*)\\.?([0-9]+([eE][-+]?[0-9]+)?))\\s*");
919
boost::regex rx_s("\\b([-+]?[0-9]+)\\s*");
920
boost::regex rx_u("\\b([0-9]+)\\s*");
921
boost::regex rx_f(R"(^\s*3\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)\s*)");
924
for (std::size_t i = 0; i < v_count && std::getline(inp, line); i++) {
925
// go through the vertex properties
926
std::map<std::string, float> prop_values;
927
for (const auto& it : vertex_props) {
932
if (boost::regex_search(line, what, rx_s)) {
934
v = boost::lexical_cast<int>(what[1]);
935
prop_values[it.first] = static_cast<float>(v);
936
line = line.substr(what[0].length());
945
if (boost::regex_search(line, what, rx_u)) {
947
v = boost::lexical_cast<int>(what[1]);
948
prop_values[it.first] = static_cast<float>(v);
949
line = line.substr(what[0].length());
957
if (boost::regex_search(line, what, rx_d)) {
959
v = boost::lexical_cast<double>(what[1]);
960
prop_values[it.first] = static_cast<float>(v);
961
line = line.substr(what[0].length());
973
pt.x = (prop_values["x"]);
974
pt.y = (prop_values["y"]);
975
pt.z = (prop_values["z"]);
976
meshPoints.push_back(pt);
978
if (_material && (rgb_value == MeshIO::PER_VERTEX)) {
979
float r = (prop_values["red"]) / 255.0f;
980
float g = (prop_values["green"]) / 255.0f;
981
float b = (prop_values["blue"]) / 255.0f;
982
_material->diffuseColor.emplace_back(r, g, b);
986
int f1 {}, f2 {}, f3 {};
987
for (std::size_t i = 0; i < f_count && std::getline(inp, line); i++) {
988
if (boost::regex_search(line, what, rx_f)) {
989
f1 = boost::lexical_cast<int>(what[1]);
990
f2 = boost::lexical_cast<int>(what[2]);
991
f3 = boost::lexical_cast<int>(what[3]);
992
meshFacets.push_back(MeshFacet(f1, f2, f3));
998
Base::InputStream is(inp);
999
if (format == binary_little_endian) {
1000
is.setByteOrder(Base::Stream::LittleEndian);
1003
is.setByteOrder(Base::Stream::BigEndian);
1006
for (std::size_t i = 0; i < v_count; i++) {
1007
// go through the vertex properties
1008
std::map<std::string, float> prop_values;
1009
for (const auto& it : vertex_props) {
1010
switch (it.second) {
1014
prop_values[it.first] = static_cast<float>(v);
1019
prop_values[it.first] = static_cast<float>(v);
1024
prop_values[it.first] = static_cast<float>(v);
1029
prop_values[it.first] = static_cast<float>(v);
1034
prop_values[it.first] = static_cast<float>(v);
1039
prop_values[it.first] = static_cast<float>(v);
1044
prop_values[it.first] = v;
1049
prop_values[it.first] = static_cast<float>(v);
1057
pt.x = (prop_values["x"]);
1058
pt.y = (prop_values["y"]);
1059
pt.z = (prop_values["z"]);
1060
meshPoints.push_back(pt);
1062
if (_material && (rgb_value == MeshIO::PER_VERTEX)) {
1063
float r = (prop_values["red"]) / 255.0f;
1064
float g = (prop_values["green"]) / 255.0f;
1065
float b = (prop_values["blue"]) / 255.0f;
1066
_material->diffuseColor.emplace_back(r, g, b);
1071
uint32_t f1 {}, f2 {}, f3 {};
1072
for (std::size_t i = 0; i < f_count; i++) {
1075
is >> f1 >> f2 >> f3;
1076
if (f1 < v_count && f2 < v_count && f3 < v_count) {
1077
meshFacets.push_back(MeshFacet(f1, f2, f3));
1079
for (auto it : face_props) {
1108
for (unsigned char j = 0; j < n; j++) {
1115
for (unsigned char j = 0; j < n; j++) {
1127
this->_rclMesh.Clear(); // remove all data before
1129
MeshCleanup meshCleanup(meshPoints, meshFacets);
1131
meshCleanup.SetMaterial(_material);
1133
meshCleanup.RemoveInvalids();
1134
MeshPointFacetAdjacency meshAdj(meshPoints.size(), meshFacets);
1135
meshAdj.SetFacetNeighbourhood();
1136
this->_rclMesh.Adopt(meshPoints, meshFacets);
1141
bool MeshInput::LoadMeshNode(std::istream& rstrIn)
1143
boost::regex rx_p("^v\\s+([-+]?[0-9]*)\\.?([0-9]+([eE][-+]?[0-9]+)?)"
1144
"\\s+([-+]?[0-9]*)\\.?([0-9]+([eE][-+]?[0-9]+)?)"
1145
"\\s+([-+]?[0-9]*)\\.?([0-9]+([eE][-+]?[0-9]+)?)\\s*$");
1146
boost::regex rx_f(R"(^f\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)\s*$)");
1147
boost::regex rx_e("\\s*]\\s*");
1150
MeshPointArray meshPoints;
1151
MeshFacetArray meshFacets;
1154
float fX {}, fY {}, fZ {};
1155
unsigned int i1 = 1, i2 = 1, i3 = 1;
1156
MeshGeomFacet clFacet;
1158
if (!rstrIn || rstrIn.bad()) {
1162
std::streambuf* buf = rstrIn.rdbuf();
1167
while (std::getline(rstrIn, line)) {
1168
boost::algorithm::to_lower(line);
1169
if (boost::regex_match(line.c_str(), what, rx_p)) {
1170
fX = (float)std::atof(what[1].first);
1171
fY = (float)std::atof(what[4].first);
1172
fZ = (float)std::atof(what[7].first);
1173
meshPoints.push_back(MeshPoint(Base::Vector3f(fX, fY, fZ)));
1175
else if (boost::regex_match(line.c_str(), what, rx_f)) {
1176
i1 = std::atoi(what[1].first);
1177
i2 = std::atoi(what[2].first);
1178
i3 = std::atoi(what[3].first);
1179
meshFacets.push_back(MeshFacet(i1 - 1, i2 - 1, i3 - 1));
1181
else if (boost::regex_match(line.c_str(), what, rx_e)) {
1186
this->_rclMesh.Clear(); // remove all data before
1188
MeshCleanup meshCleanup(meshPoints, meshFacets);
1189
meshCleanup.RemoveInvalids();
1190
MeshPointFacetAdjacency meshAdj(meshPoints.size(), meshFacets);
1191
meshAdj.SetFacetNeighbourhood();
1192
this->_rclMesh.Adopt(meshPoints, meshFacets);
1197
/** Loads an ASCII STL file. */
1198
bool MeshInput::LoadAsciiSTL(std::istream& rstrIn)
1200
boost::regex rx_p("^\\s*VERTEX\\s+([-+]?[0-9]*)\\.?([0-9]+([eE][-+]?[0-9]+)?)"
1201
"\\s+([-+]?[0-9]*)\\.?([0-9]+([eE][-+]?[0-9]+)?)"
1202
"\\s+([-+]?[0-9]*)\\.?([0-9]+([eE][-+]?[0-9]+)?)\\s*$");
1203
boost::regex rx_f("^\\s*FACET\\s+NORMAL\\s+([-+]?[0-9]*)\\.?([0-9]+([eE][-+]?[0-9]+)?)"
1204
"\\s+([-+]?[0-9]*)\\.?([0-9]+([eE][-+]?[0-9]+)?)"
1205
"\\s+([-+]?[0-9]*)\\.?([0-9]+([eE][-+]?[0-9]+)?)\\s*$");
1209
float fX {}, fY {}, fZ {};
1210
unsigned long ulVertexCt {}, ulFacetCt {};
1211
MeshGeomFacet clFacet;
1213
if (!rstrIn || rstrIn.bad()) {
1217
std::streamoff ulSize = 0;
1218
std::streambuf* buf = rstrIn.rdbuf();
1219
ulSize = buf->pubseekoff(0, std::ios::end, std::ios::in);
1220
buf->pubseekoff(0, std::ios::beg, std::ios::in);
1224
while (std::getline(rstrIn, line)) {
1225
boost::algorithm::to_upper(line);
1226
if (line.find("ENDFACET") != std::string::npos) {
1229
// prevent from reading EOF (as I don't know how to reread the file then)
1230
if (rstrIn.tellg() > ulSize) {
1233
else if (line.find("ENDSOLID") != std::string::npos) {
1238
// restart from the beginning
1239
buf->pubseekoff(0, std::ios::beg, std::ios::in);
1242
MeshBuilder builder(this->_rclMesh);
1244
MeshFastBuilder builder(this->_rclMesh);
1246
builder.Initialize(ulFacetCt);
1249
while (std::getline(rstrIn, line)) {
1250
boost::algorithm::to_upper(line);
1251
if (boost::regex_match(line.c_str(), what, rx_f)) {
1252
fX = (float)std::atof(what[1].first);
1253
fY = (float)std::atof(what[4].first);
1254
fZ = (float)std::atof(what[7].first);
1255
clFacet.SetNormal(Base::Vector3f(fX, fY, fZ));
1257
else if (boost::regex_match(line.c_str(), what, rx_p)) {
1258
fX = (float)std::atof(what[1].first);
1259
fY = (float)std::atof(what[4].first);
1260
fZ = (float)std::atof(what[7].first);
1261
clFacet._aclPoints[ulVertexCt++].Set(fX, fY, fZ);
1262
if (ulVertexCt == 3) {
1264
builder.AddFacet(clFacet);
1274
/** Loads a binary STL file. */
1275
bool MeshInput::LoadBinarySTL(std::istream& rstrIn)
1278
Base::Vector3f clVects[4];
1282
if (!rstrIn || rstrIn.bad()) {
1286
// Header-Info ueberlesen
1287
rstrIn.read(szInfo, sizeof(szInfo));
1290
rstrIn.read((char*)&ulCt, sizeof(ulCt));
1295
// get file size and calculate the number of facets
1296
std::streamoff ulSize = 0;
1297
std::streambuf* buf = rstrIn.rdbuf();
1299
std::streamoff ulCurr {};
1300
ulCurr = buf->pubseekoff(0, std::ios::cur, std::ios::in);
1301
ulSize = buf->pubseekoff(0, std::ios::end, std::ios::in);
1302
buf->pubseekoff(ulCurr, std::ios::beg, std::ios::in);
1305
uint32_t ulFac = (ulSize - (80 + sizeof(uint32_t))) / 50;
1307
// compare the calculated with the read value
1309
return false; // not a valid STL file
1313
MeshBuilder builder(this->_rclMesh);
1315
MeshFastBuilder builder(this->_rclMesh);
1317
builder.Initialize(ulCt);
1319
for (uint32_t i = 0; i < ulCt; i++) {
1320
// read normal, points
1321
rstrIn.read((char*)&clVects, sizeof(clVects));
1323
std::swap(clVects[0], clVects[3]);
1324
builder.AddFacet(clVects);
1326
// overread 2 bytes attribute
1327
rstrIn.read((char*)&usAtt, sizeof(usAtt));
1335
/** Loads the mesh object from an XML file. */
1336
void MeshInput::LoadXML(Base::XMLReader& reader)
1338
MeshPointArray cPoints;
1339
MeshFacetArray cFacets;
1341
// reader.readElement("Mesh");
1343
reader.readElement("Points");
1344
int Cnt = reader.getAttributeAsInteger("Count");
1346
cPoints.resize(Cnt);
1347
for (int i = 0; i < Cnt; i++) {
1348
reader.readElement("P");
1349
cPoints[i].x = (float)reader.getAttributeAsFloat("x");
1350
cPoints[i].y = (float)reader.getAttributeAsFloat("y");
1351
cPoints[i].z = (float)reader.getAttributeAsFloat("z");
1353
reader.readEndElement("Points");
1355
reader.readElement("Faces");
1356
Cnt = reader.getAttributeAsInteger("Count");
1358
cFacets.resize(Cnt);
1359
for (int i = 0; i < Cnt; i++) {
1360
reader.readElement("F");
1361
cFacets[i]._aulPoints[0] = reader.getAttributeAsInteger("p0");
1362
cFacets[i]._aulPoints[1] = reader.getAttributeAsInteger("p1");
1363
cFacets[i]._aulPoints[2] = reader.getAttributeAsInteger("p2");
1364
cFacets[i]._aulNeighbours[0] = reader.getAttributeAsInteger("n0");
1365
cFacets[i]._aulNeighbours[1] = reader.getAttributeAsInteger("n1");
1366
cFacets[i]._aulNeighbours[2] = reader.getAttributeAsInteger("n2");
1369
reader.readEndElement("Faces");
1370
reader.readEndElement("Mesh");
1372
_rclMesh.Adopt(cPoints, cFacets);
1375
/** Loads a 3MF file. */
1376
bool MeshInput::Load3MF(std::istream& inp)
1378
Reader3MF reader(inp);
1380
std::vector<int> ids = reader.GetMeshIds();
1382
MeshKernel compound = reader.GetMesh(ids[0]);
1383
compound.Transform(reader.GetTransform(ids[0]));
1385
for (std::size_t index = 1; index < ids.size(); index++) {
1386
MeshKernel mesh = reader.GetMesh(ids[index]);
1387
mesh.Transform(reader.GetTransform(ids[index]));
1388
compound.Merge(mesh);
1391
_rclMesh = compound;
1398
/** Loads an OpenInventor file. */
1399
bool MeshInput::LoadInventor(std::istream& inp)
1401
Base::InventorLoader loader(inp);
1402
if (!loader.read()) {
1406
if (!loader.isValid()) {
1410
const auto& points = loader.getPoints();
1411
const auto& faces = loader.getFaces();
1413
MeshPointArray meshPoints;
1414
meshPoints.reserve(points.size());
1415
std::transform(points.begin(),
1417
std::back_inserter(meshPoints),
1418
[](const Base::Vector3f& v) {
1419
return MeshPoint(v);
1422
MeshFacetArray meshFacets;
1423
meshFacets.reserve(faces.size());
1424
std::transform(faces.begin(),
1426
std::back_inserter(meshFacets),
1427
[](const Base::InventorLoader::Face& f) {
1428
return MeshFacet(f.p1, f.p2, f.p3);
1431
MeshCleanup meshCleanup(meshPoints, meshFacets);
1432
meshCleanup.RemoveInvalids();
1433
MeshPointFacetAdjacency meshAdj(meshPoints.size(), meshFacets);
1434
meshAdj.SetFacetNeighbourhood();
1435
this->_rclMesh.Adopt(meshPoints, meshFacets);
1437
if (loader.isNonIndexed()) {
1438
if (!MeshEvalDuplicatePoints(this->_rclMesh).Evaluate()) {
1439
MeshFixDuplicatePoints(this->_rclMesh).Fixup();
1446
/** Loads a Nastran file. */
1447
bool MeshInput::LoadNastran(std::istream& rstrIn)
1449
if (!rstrIn || rstrIn.bad()) {
1453
boost::regex rx_t("\\s*CTRIA3\\s+([0-9]+)\\s+([0-9]+)"
1454
"\\s+([0-9]+)\\s+([0-9]+)\\s+([0-9]+)\\s*");
1455
boost::regex rx_q("\\s*CQUAD4\\s+([0-9]+)\\s+([0-9]+)"
1456
"\\s+([0-9]+)\\s+([0-9]+)\\s+([0-9]+)\\s+([0-9]+)\\s*");
1460
MeshFacet clMeshFacet;
1461
MeshPointArray vVertices;
1462
MeshFacetArray vTriangle;
1465
std::map<int, NODE> mNode;
1466
std::map<int, TRIA> mTria;
1467
std::map<int, QUAD> mQuad;
1469
int badElementCounter = 0;
1471
while (std::getline(rstrIn, line)) {
1472
boost::algorithm::to_upper(ltrim(line));
1474
// Skip all the following tests
1476
else if (line.rfind("GRID*", 0) == 0) {
1477
// This element is the 16-digit-precision GRID element, which occupies two lines of the
1478
// card. Note that FreeCAD discards the extra precision, downcasting to an four-byte
1481
// The two lines are:
1483
// GRID* Index(16) Blank(16) x(16) y(at least one)
1484
// * z(at least one)
1486
// The first character is typically the sign, and may be omitted for positive numbers,
1487
// so it is possible for a field to begin with a blank. Trailing zeros may be omitted,
1488
// so a field may also end with blanks. No space or other delimiter is required between
1489
// the numbers. The following is a valid NASTRAN GRID* element:
1491
// GRID* 1 0.1234567890120.
1495
< 8 + 16 + 16 + 16 + 1) { // Element type(8), index(16), empty(16), x(16), y(>=1)
1496
badElementCounter++;
1499
auto indexView = std::string_view(&line[8], 16);
1500
// auto blankView = std::string_view(&line[8+16], 16); // No data is needed here
1501
auto xView = std::string_view(&line[8 + 16 + 16], 16);
1502
auto yView = std::string_view(&line[8 + 16 + 16 + 16]);
1505
std::getline(rstrIn, line2);
1506
if ((!line2.empty() && line2[0] != '*') || line2.length() < 9) {
1507
badElementCounter++;
1508
continue; // File format error: second line is not a continuation line
1510
auto zView = std::string_view(&line2[8]);
1512
// We have to strip off any whitespace (technically really just any *trailing*
1514
auto indexString = boost::trim_copy(std::string(indexView));
1515
auto xString = boost::trim_copy(std::string(xView));
1516
auto yString = boost::trim_copy(std::string(yView));
1517
auto zString = boost::trim_copy(std::string(zView));
1519
auto converter = boost::cnv::spirit();
1520
auto indexCheck = boost::convert<int>(indexString, converter);
1521
if (!indexCheck.is_initialized()) {
1522
// File format error: index couldn't be converted to an integer
1523
badElementCounter++;
1527
indexCheck.get() - 1; // Minus one so we are zero-indexed to match existing code
1529
// Get the high-precision versions first
1530
auto x = boost::convert<double>(xString, converter);
1531
auto y = boost::convert<double>(yString, converter);
1532
auto z = boost::convert<double>(zString, converter);
1534
if (!x.is_initialized() || !y.is_initialized() || !z.is_initialized()) {
1535
// File format error: x, y or z could not be converted
1536
badElementCounter++;
1540
// Now drop precision:
1541
mNode[index].x = (float)x.get();
1542
mNode[index].y = (float)y.get();
1543
mNode[index].z = (float)z.get();
1545
else if (line.rfind("GRID", 0) == 0) {
1547
boost::regex rx_spaceDelimited("\\s*GRID\\s+([0-9]+)"
1548
"\\s+([-+]?[0-9]*)\\.?([0-9]+([eE][-+]?[0-9]+)?)"
1549
"\\s+([-+]?[0-9]*)\\.?([0-9]+([eE][-+]?[0-9]+)?)"
1550
"\\s+([-+]?[0-9]*)\\.?([0-9]+([eE][-+]?[0-9]+)?)\\s*");
1552
if (boost::regex_match(line.c_str(), what, rx_spaceDelimited)) {
1553
// insert the read-in vertex into a map to preserve the order
1554
index = std::atol(what[1].first) - 1;
1555
mNode[index].x = (float)std::atof(what[2].first);
1556
mNode[index].y = (float)std::atof(what[5].first);
1557
mNode[index].z = (float)std::atof(what[8].first);
1560
// Classic NASTRAN uses a fixed 8 character field width:
1562
// $-------ID------CP------X1------X2------X3------CD------PS------9-------+-------
1563
// GRID 1 1.2345671.2345671.234567
1564
// GRID 112 6.0000000.5000000.00E+00
1567
< 41) { // Element type(8), id(8), cp(8), x(8), y(8), z(at least 1)
1568
badElementCounter++;
1571
auto indexView = std::string_view(&line[8], 8);
1572
auto xView = std::string_view(&line[24], 8);
1573
auto yView = std::string_view(&line[32], 8);
1574
auto zView = std::string_view(&line[40], 8);
1576
auto indexString = boost::trim_copy(std::string(indexView));
1577
auto xString = boost::trim_copy(std::string(xView));
1578
auto yString = boost::trim_copy(std::string(yView));
1579
auto zString = boost::trim_copy(std::string(zView));
1581
auto converter = boost::cnv::spirit();
1582
auto indexCheck = boost::convert<int>(indexString, converter);
1583
if (!indexCheck.is_initialized()) {
1584
// File format error: index couldn't be converted to an integer
1585
badElementCounter++;
1588
index = indexCheck.get()
1589
- 1; // Minus one so we are zero-indexed to match existing code
1591
auto x = boost::convert<float>(xString, converter);
1592
auto y = boost::convert<float>(yString, converter);
1593
auto z = boost::convert<float>(zString, converter);
1595
if (!x.is_initialized() || !y.is_initialized() || !z.is_initialized()) {
1596
// File format error: x, y or z could not be converted
1597
badElementCounter++;
1601
mNode[index].x = x.get();
1602
mNode[index].y = y.get();
1603
mNode[index].z = z.get();
1606
else if (line.rfind("CTRIA3 ", 0) == 0) {
1607
if (boost::regex_match(line.c_str(), what, rx_t)) {
1608
// insert the read-in triangle into a map to preserve the order
1609
index = std::atol(what[1].first) - 1;
1610
mTria[index].iV[0] = std::atol(what[3].first) - 1;
1611
mTria[index].iV[1] = std::atol(what[4].first) - 1;
1612
mTria[index].iV[2] = std::atol(what[5].first) - 1;
1615
else if (line.rfind("CQUAD4", 0) == 0) {
1616
if (boost::regex_match(line.c_str(), what, rx_q)) {
1617
// insert the read-in quadrangle into a map to preserve the order
1618
index = std::atol(what[1].first) - 1;
1619
mQuad[index].iV[0] = std::atol(what[3].first) - 1;
1620
mQuad[index].iV[1] = std::atol(what[4].first) - 1;
1621
mQuad[index].iV[2] = std::atol(what[5].first) - 1;
1622
mQuad[index].iV[3] = std::atol(what[6].first) - 1;
1627
if (badElementCounter > 0) {
1628
Base::Console().Warning("Found bad elements while reading NASTRAN file.\n");
1631
// Check the triangles to make sure the vertices they refer to actually exist:
1632
for (const auto& tri : mTria) {
1633
for (int i : tri.second.iV) {
1634
if (mNode.find(i) == mNode.end()) {
1635
Base::Console().Error(
1636
"CTRIA3 element refers to a node that does not exist, or could not be read.\n");
1642
// Check the quads to make sure the vertices they refer to actually exist:
1643
for (const auto& quad : mQuad) {
1644
for (int i : quad.second.iV) {
1645
if (mNode.find(i) == mNode.end()) {
1646
Base::Console().Error(
1647
"CQUAD4 element refers to a node that does not exist, or could not be read.\n");
1654
if (mTria.empty()) {
1658
index = mTria.rbegin()->first + 1;
1660
for (const auto& QI : mQuad) {
1661
for (int i = 0; i < 2; i++) {
1662
float fDx = mNode[QI.second.iV[i + 2]].x - mNode[QI.second.iV[i]].x;
1663
float fDy = mNode[QI.second.iV[i + 2]].y - mNode[QI.second.iV[i]].y;
1664
float fDz = mNode[QI.second.iV[i + 2]].z - mNode[QI.second.iV[i]].z;
1665
fLength[i] = fDx * fDx + fDy * fDy + fDz * fDz;
1667
if (fLength[0] < fLength[1]) {
1668
mTria[index].iV[0] = QI.second.iV[0];
1669
mTria[index].iV[1] = QI.second.iV[1];
1670
mTria[index].iV[2] = QI.second.iV[2];
1672
mTria[index + 1].iV[0] = QI.second.iV[0];
1673
mTria[index + 1].iV[1] = QI.second.iV[2];
1674
mTria[index + 1].iV[2] = QI.second.iV[3];
1677
mTria[index].iV[0] = QI.second.iV[0];
1678
mTria[index].iV[1] = QI.second.iV[1];
1679
mTria[index].iV[2] = QI.second.iV[3];
1681
mTria[index + 1].iV[0] = QI.second.iV[1];
1682
mTria[index + 1].iV[1] = QI.second.iV[2];
1683
mTria[index + 1].iV[2] = QI.second.iV[3];
1689
// Applying the nodes
1690
vVertices.reserve(mNode.size());
1691
for (const auto& MI : mNode) {
1692
vVertices.push_back(Base::Vector3f(MI.second.x, MI.second.y, MI.second.z));
1695
// Converting data to Mesh. Negative conversion for right orientation of normal-vectors.
1696
vTriangle.reserve(mTria.size());
1697
for (const auto& MI : mTria) {
1698
clMeshFacet._aulPoints[0] = MI.second.iV[1];
1699
clMeshFacet._aulPoints[1] = MI.second.iV[0];
1700
clMeshFacet._aulPoints[2] = MI.second.iV[2];
1701
vTriangle.push_back(clMeshFacet);
1704
// make sure to add only vertices which are referenced by the triangles
1705
_rclMesh.Merge(vVertices, vTriangle);
1710
/** Loads a Cadmould FE file. */
1711
bool MeshInput::LoadCadmouldFE(std::ifstream& rstrIn)
1713
if (!rstrIn || rstrIn.bad()) {
1720
// --------------------------------------------------------------
1722
std::string MeshOutput::stl_header = "MESH-MESH-MESH-MESH-MESH-MESH-MESH-MESH-"
1723
"MESH-MESH-MESH-MESH-MESH-MESH-MESH-MESH\n";
1725
void MeshOutput::SetSTLHeaderData(const std::string& header)
1727
if (header.size() > 80) {
1728
stl_header = header.substr(0, 80);
1730
else if (header.size() < 80) {
1731
std::fill(stl_header.begin(), stl_header.end(), ' ');
1732
std::copy(header.begin(), header.end(), stl_header.begin());
1735
stl_header = header;
1739
std::string MeshOutput::asyWidth = "500";
1740
std::string MeshOutput::asyHeight = "500";
1742
void MeshOutput::SetAsymptoteSize(const std::string& w, const std::string& h)
1748
void MeshOutput::Transform(const Base::Matrix4D& mat)
1751
if (mat != Base::Matrix4D()) {
1752
apply_transform = true;
1756
std::vector<std::string> MeshOutput::supportedMeshFormats()
1758
std::vector<std::string> fmt;
1759
fmt.emplace_back("bms");
1760
fmt.emplace_back("ply");
1761
fmt.emplace_back("stl");
1762
fmt.emplace_back("obj");
1763
fmt.emplace_back("off");
1764
fmt.emplace_back("smf");
1765
fmt.emplace_back("x3d");
1766
fmt.emplace_back("x3dz");
1767
fmt.emplace_back("xhtml");
1768
fmt.emplace_back("wrl");
1769
fmt.emplace_back("wrz");
1770
fmt.emplace_back("amf");
1771
fmt.emplace_back("asy");
1772
fmt.emplace_back("3mf");
1776
MeshIO::Format MeshOutput::GetFormat(const char* FileName)
1778
Base::FileInfo file(FileName);
1779
if (file.hasExtension("bms")) {
1782
else if (file.hasExtension("stl")) {
1783
return MeshIO::BSTL;
1785
else if (file.hasExtension("ast")) {
1786
return MeshIO::ASTL;
1788
else if (file.hasExtension("obj")) {
1791
else if (file.hasExtension("off")) {
1794
else if (file.hasExtension("ply")) {
1797
else if (file.hasExtension("idtf")) {
1798
return MeshIO::IDTF;
1800
else if (file.hasExtension("mgl")) {
1803
else if (file.hasExtension("iv")) {
1806
else if (file.hasExtension("x3d")) {
1809
else if (file.hasExtension("x3dz")) {
1810
return MeshIO::X3DZ;
1812
else if (file.hasExtension("xhtml")) {
1813
return MeshIO::X3DOM;
1815
else if (file.hasExtension("py")) {
1818
else if (file.hasExtension({"wrl", "vrml"})) {
1819
return MeshIO::VRML;
1821
else if (file.hasExtension("wrz")) {
1824
else if (file.hasExtension({"nas", "bdf"})) {
1827
else if (file.hasExtension("amf")) {
1830
else if (file.hasExtension("3mf")) {
1831
return MeshIO::ThreeMF;
1833
else if (file.hasExtension("smf")) {
1836
else if (file.hasExtension("asy")) {
1840
return MeshIO::Undefined;
1844
/// Save in a file, format is decided by the extension if not explicitly given
1845
bool MeshOutput::SaveAny(const char* FileName, MeshIO::Format format) const
1847
// ask for write permission
1848
Base::FileInfo file(FileName);
1849
Base::FileInfo directory(file.dirPath());
1850
if ((file.exists() && !file.isWritable()) || !directory.exists() || !directory.isWritable()) {
1851
throw Base::FileException("No write permission for file", FileName);
1854
MeshIO::Format fileformat = format;
1855
if (fileformat == MeshIO::Undefined) {
1856
fileformat = GetFormat(FileName);
1859
Base::ofstream str(file, std::ios::out | std::ios::binary);
1861
if (fileformat == MeshIO::BMS) {
1862
_rclMesh.Write(str);
1864
else if (fileformat == MeshIO::BSTL) {
1865
MeshOutput aWriter(_rclMesh);
1866
aWriter.Transform(this->_transform);
1870
ok = aWriter.SaveBinarySTL(str);
1872
throw Base::FileException("Export of STL mesh failed", FileName);
1875
else if (fileformat == MeshIO::ASTL) {
1876
MeshOutput aWriter(_rclMesh);
1877
aWriter.SetObjectName(objectName);
1878
aWriter.Transform(this->_transform);
1882
ok = aWriter.SaveAsciiSTL(str);
1884
throw Base::FileException("Export of STL mesh failed", FileName);
1887
else if (fileformat == MeshIO::OBJ) {
1889
if (!SaveOBJ(str, FileName)) {
1890
throw Base::FileException("Export of OBJ mesh failed", FileName);
1893
else if (fileformat == MeshIO::SMF) {
1895
if (!SaveSMF(str)) {
1896
throw Base::FileException("Export of SMF mesh failed", FileName);
1899
else if (fileformat == MeshIO::OFF) {
1901
if (!SaveOFF(str)) {
1902
throw Base::FileException("Export of OFF mesh failed", FileName);
1905
else if (fileformat == MeshIO::PLY) {
1907
if (!SaveBinaryPLY(str)) {
1908
throw Base::FileException("Export of PLY mesh failed", FileName);
1911
else if (fileformat == MeshIO::APLY) {
1913
if (!SaveAsciiPLY(str)) {
1914
throw Base::FileException("Export of PLY mesh failed", FileName);
1917
else if (fileformat == MeshIO::IDTF) {
1919
if (!SaveIDTF(str)) {
1920
throw Base::FileException("Export of IDTF mesh failed", FileName);
1923
else if (fileformat == MeshIO::MGL) {
1925
if (!SaveMGL(str)) {
1926
throw Base::FileException("Export of MGL mesh failed", FileName);
1929
else if (fileformat == MeshIO::IV) {
1931
if (!SaveInventor(str)) {
1932
throw Base::FileException("Export of Inventor mesh failed", FileName);
1935
else if (fileformat == MeshIO::X3D) {
1937
if (!SaveX3D(str)) {
1938
throw Base::FileException("Export of X3D failed", FileName);
1941
else if (fileformat == MeshIO::X3DZ) {
1942
// Compressed X3D is nothing else than a GZIP'ped X3D ascii file
1943
zipios::GZIPOutputStream gzip(str);
1945
if (!SaveX3D(gzip)) {
1946
throw Base::FileException("Export of compressed X3D mesh failed", FileName);
1949
else if (fileformat == MeshIO::X3DOM) {
1951
if (!SaveX3DOM(str)) {
1952
throw Base::FileException("Export of X3DOM failed", FileName);
1955
else if (fileformat == MeshIO::ThreeMF) {
1957
if (!Save3MF(str)) {
1958
throw Base::FileException("Export of 3MF failed", FileName);
1961
else if (fileformat == MeshIO::PY) {
1963
if (!SavePython(str)) {
1964
throw Base::FileException("Export of Python mesh failed", FileName);
1967
else if (fileformat == MeshIO::VRML) {
1969
if (!SaveVRML(str)) {
1970
throw Base::FileException("Export of VRML mesh failed", FileName);
1973
else if (fileformat == MeshIO::WRZ) {
1974
// Compressed VRML is nothing else than a GZIP'ped VRML ascii file
1976
// Base::ogzstream gzip(FileName, std::ios::out | std::ios::binary);
1977
// Hint: The compression level seems to be higher than with ogzstream
1978
// which leads to problems to load the wrz file in debug mode, the
1979
// application simply crashes.
1980
zipios::GZIPOutputStream gzip(str);
1982
if (!SaveVRML(gzip)) {
1983
throw Base::FileException("Export of compressed VRML mesh failed", FileName);
1986
else if (fileformat == MeshIO::NAS) {
1988
if (!SaveNastran(str)) {
1989
throw Base::FileException("Export of NASTRAN mesh failed", FileName);
1992
else if (fileformat == MeshIO::ASY) {
1994
if (!SaveAsymptote(str)) {
1995
throw Base::FileException("Export of ASY mesh failed", FileName);
1999
throw Base::FileException("File format not supported", FileName);
2005
bool MeshOutput::SaveFormat(std::ostream& str, MeshIO::Format fmt) const
2009
_rclMesh.Write(str);
2012
return SaveAsciiSTL(str);
2014
return SaveBinarySTL(str);
2016
return SaveOBJ(str);
2018
return SaveSMF(str);
2020
return SaveOFF(str);
2022
return SaveIDTF(str);
2024
return SaveMGL(str);
2026
return SaveInventor(str);
2028
return SaveX3D(str);
2030
return SaveX3DOM(str);
2032
return SaveVRML(str);
2034
// it's up to the client to create the needed stream
2035
return SaveVRML(str);
2036
case MeshIO::ThreeMF:
2037
return Save3MF(str);
2039
return SaveNastran(str);
2041
return SaveBinaryPLY(str);
2043
return SaveAsciiPLY(str);
2045
return SavePython(str);
2047
return SaveAsymptote(str);
2049
throw Base::FileException("Unsupported file format");
2053
/** Saves the mesh object into an ASCII file. */
2054
bool MeshOutput::SaveAsciiSTL(std::ostream& rstrOut) const
2056
MeshFacetIterator clIter(_rclMesh), clEnd(_rclMesh);
2057
clIter.Transform(this->_transform);
2058
const MeshGeomFacet* pclFacet {};
2060
if (!rstrOut || rstrOut.bad() || _rclMesh.CountFacets() == 0) {
2064
rstrOut.precision(6);
2065
rstrOut.setf(std::ios::fixed | std::ios::showpoint);
2066
Base::SequencerLauncher seq("saving...", _rclMesh.CountFacets() + 1);
2068
if (this->objectName.empty()) {
2069
rstrOut << "solid Mesh\n";
2072
rstrOut << "solid " << this->objectName << '\n';
2077
while (clIter < clEnd) {
2078
pclFacet = &(*clIter);
2081
rstrOut << " facet normal " << pclFacet->GetNormal().x << " " << pclFacet->GetNormal().y
2082
<< " " << pclFacet->GetNormal().z << '\n';
2083
rstrOut << " outer loop\n";
2086
for (const auto& pnt : pclFacet->_aclPoints) {
2087
rstrOut << " vertex " << pnt.x << " " << pnt.y << " " << pnt.z << '\n';
2090
rstrOut << " endloop\n";
2091
rstrOut << " endfacet\n";
2094
seq.next(true); // allow to cancel
2097
rstrOut << "endsolid Mesh\n";
2102
/** Saves the mesh object into a binary file. */
2103
bool MeshOutput::SaveBinarySTL(std::ostream& rstrOut) const
2105
MeshFacetIterator clIter(_rclMesh), clEnd(_rclMesh);
2106
clIter.Transform(this->_transform);
2107
const MeshGeomFacet* pclFacet {};
2112
if (!rstrOut || rstrOut.bad() /*|| _rclMesh.CountFacets() == 0*/) {
2116
Base::SequencerLauncher seq("saving...", _rclMesh.CountFacets() + 1);
2118
// stl_header has a length of 80
2119
strcpy(szInfo, stl_header.c_str());
2120
rstrOut.write(szInfo, std::strlen(szInfo));
2122
uint32_t uCtFts = (uint32_t)_rclMesh.CountFacets();
2123
rstrOut.write((const char*)&uCtFts, sizeof(uCtFts));
2128
while (clIter < clEnd) {
2129
pclFacet = &(*clIter);
2131
Base::Vector3f normal = pclFacet->GetNormal();
2132
rstrOut.write((const char*)&(normal.x), sizeof(float));
2133
rstrOut.write((const char*)&(normal.y), sizeof(float));
2134
rstrOut.write((const char*)&(normal.z), sizeof(float));
2137
for (i = 0; i < 3; i++) {
2138
rstrOut.write((const char*)&(pclFacet->_aclPoints[i].x), sizeof(float));
2139
rstrOut.write((const char*)&(pclFacet->_aclPoints[i].y), sizeof(float));
2140
rstrOut.write((const char*)&(pclFacet->_aclPoints[i].z), sizeof(float));
2144
rstrOut.write((const char*)&usAtt, sizeof(usAtt));
2147
seq.next(true); // allow to cancel
2153
/** Saves an OBJ file. */
2154
bool MeshOutput::SaveOBJ(std::ostream& out) const
2156
WriterOBJ writer(this->_rclMesh, this->_material);
2157
writer.SetTransform(this->_transform);
2158
writer.SetGroups(this->_groups);
2159
return writer.Save(out);
2162
bool MeshOutput::SaveOBJ(std::ostream& out, const char* filename) const
2164
WriterOBJ writer(this->_rclMesh, this->_material);
2165
writer.SetTransform(this->_transform);
2166
writer.SetGroups(this->_groups);
2167
if (writer.Save(out)) {
2168
if (this->_material && this->_material->binding == MeshCore::MeshIO::PER_FACE) {
2169
Base::FileInfo fi(filename);
2170
std::string fn = fi.dirPath() + "/" + this->_material->library;
2172
Base::ofstream mtl(fi, std::ios::out | std::ios::binary);
2173
writer.SaveMaterial(mtl);
2183
/** Saves an SMF file. */
2184
bool MeshOutput::SaveSMF(std::ostream& out) const
2186
// http://people.sc.fsu.edu/~jburkardt/data/smf/smf.txt
2187
const MeshPointArray& rPoints = _rclMesh.GetPoints();
2188
const MeshFacetArray& rFacets = _rclMesh.GetFacets();
2190
if (!out || out.bad()) {
2194
Base::SequencerLauncher seq("saving...", _rclMesh.CountPoints() + _rclMesh.CountFacets());
2197
out << "#$SMF 1.0\n";
2198
out << "#$vertices " << rPoints.size() << '\n';
2199
out << "#$faces " << rFacets.size() << '\n';
2201
out << "# Created by FreeCAD <https://www.freecad.org>\n";
2204
out.setf(std::ios::fixed | std::ios::showpoint);
2208
std::size_t index = 0;
2209
for (MeshPointArray::_TConstIterator it = rPoints.begin(); it != rPoints.end(); ++it, ++index) {
2210
if (this->apply_transform) {
2211
pt = this->_transform * *it;
2214
pt.Set(it->x, it->y, it->z);
2217
out << "v " << pt.x << " " << pt.y << " " << pt.z << '\n';
2218
seq.next(true); // allow to cancel
2222
for (const auto& it : rFacets) {
2223
out << "f " << it._aulPoints[0] + 1 << " " << it._aulPoints[1] + 1 << " "
2224
<< it._aulPoints[2] + 1 << '\n';
2225
seq.next(true); // allow to cancel
2231
/** Saves an Asymptote file. */
2232
bool MeshOutput::SaveAsymptote(std::ostream& out) const
2235
" * Created by FreeCAD <https://www.freecad.org>\n"
2238
out << "import three;\n\n";
2240
if (!asyWidth.empty()) {
2241
out << "size(" << asyWidth;
2242
if (!asyHeight.empty()) {
2243
out << ", " << asyHeight;
2248
Base::BoundBox3f bbox = _rclMesh.GetBoundBox();
2249
Base::Vector3f center = bbox.GetCenter();
2250
this->_transform.multVec(center, center);
2251
Base::Vector3f camera(center);
2252
camera.x += std::max<float>(std::max<float>(bbox.LengthX(), bbox.LengthY()), bbox.LengthZ());
2253
Base::Vector3f target(center);
2254
Base::Vector3f upvec(0.0f, 0.0f, 1.0f);
2256
out << "// CA:Camera, OB:Camera\n"
2257
<< "currentprojection = orthographic(camera = (" << camera.x << ", " << camera.y << ", "
2258
<< camera.z << "),\n"
2259
<< " target = (" << target.x << ", " << target.y << ", "
2262
" showtarget = false,\n"
2264
<< upvec.x << ", " << upvec.y << ", " << upvec.z << "));\n\n";
2266
// out << "// LA:Spot, OB:Lamp\n"
2267
// << "// WO:World\n"
2268
// << "currentlight = light(diffuse = rgb(1, 1, 1),\n"
2269
// " specular = rgb(1, 1, 1),\n"
2270
// " background = rgb(0.078281, 0.16041, 0.25),\n"
2271
// " 0.56639, 0.21839, 0.79467);\n\n";
2273
out << "// ME:Mesh, OB:Mesh\n";
2275
MeshFacetIterator clIter(_rclMesh), clEnd(_rclMesh);
2276
clIter.Transform(this->_transform);
2280
const MeshPointArray& rPoints = _rclMesh.GetPoints();
2281
const MeshFacetArray& rFacets = _rclMesh.GetFacets();
2282
bool saveVertexColor = (_material && _material->binding == MeshIO::PER_VERTEX
2283
&& _material->diffuseColor.size() == rPoints.size());
2284
bool saveFaceColor = (_material && _material->binding == MeshIO::PER_FACE
2285
&& _material->diffuseColor.size() == rFacets.size());
2286
// global mesh color
2287
App::Color mc(0.8f, 0.8f, 0.8f);
2288
if (_material && _material->binding == MeshIO::OVERALL && _material->diffuseColor.size() == 1) {
2289
mc = _material->diffuseColor[0];
2292
std::size_t index = 0;
2293
const MeshGeomFacet* pclFacet {};
2294
while (clIter < clEnd) {
2295
pclFacet = &(*clIter);
2297
out << "draw(surface(";
2300
for (const auto& pnt : pclFacet->_aclPoints) {
2301
out << '(' << pnt.x << ", " << pnt.y << ", " << pnt.z << ")--";
2306
if (saveVertexColor) {
2307
const MeshFacet& face = rFacets[index];
2308
out << ",\n new pen[] {";
2309
for (int i = 0; i < 3; i++) {
2310
const App::Color& c = _material->diffuseColor[face._aulPoints[i]];
2311
out << "rgb(" << c.r << ", " << c.g << ", " << c.b << ")";
2318
else if (saveFaceColor) {
2319
const App::Color& c = _material->diffuseColor[index];
2320
out << "),\n rgb(" << c.r << ", " << c.g << ", " << c.b << "));\n";
2323
out << "),\n rgb(" << mc.r << ", " << mc.g << ", " << mc.b << "));\n";
2333
/** Saves an OFF file. */
2334
bool MeshOutput::SaveOFF(std::ostream& out) const
2336
const MeshPointArray& rPoints = _rclMesh.GetPoints();
2337
const MeshFacetArray& rFacets = _rclMesh.GetFacets();
2339
if (!out || out.bad()) {
2343
Base::SequencerLauncher seq("saving...", _rclMesh.CountPoints() + _rclMesh.CountFacets());
2345
bool exportColor = false;
2347
if (_material->binding == MeshIO::PER_FACE) {
2348
Base::Console().Warning(
2349
"Cannot export color information because it's defined per face");
2351
else if (_material->binding == MeshIO::PER_VERTEX) {
2352
if (_material->diffuseColor.size() != rPoints.size()) {
2353
Base::Console().Warning("Cannot export color information because there is a "
2354
"different number of points and colors");
2360
else if (_material->binding == MeshIO::OVERALL) {
2361
if (_material->diffuseColor.empty()) {
2362
Base::Console().Warning(
2363
"Cannot export color information because there is no color defined");
2377
out << rPoints.size() << " " << rFacets.size() << " 0\n";
2381
std::size_t index = 0;
2382
for (MeshPointArray::_TConstIterator it = rPoints.begin(); it != rPoints.end(); ++it, ++index) {
2383
if (this->apply_transform) {
2384
pt = this->_transform * *it;
2387
pt.Set(it->x, it->y, it->z);
2392
if (_material->binding == MeshIO::PER_VERTEX) {
2393
c = _material->diffuseColor[index];
2396
c = _material->diffuseColor.front();
2399
int r = static_cast<int>(c.r * 255.0f);
2400
int g = static_cast<int>(c.g * 255.0f);
2401
int b = static_cast<int>(c.b * 255.0f);
2402
int a = static_cast<int>(c.a * 255.0f);
2404
out << pt.x << " " << pt.y << " " << pt.z << " " << r << " " << g << " " << b << " "
2408
out << pt.x << " " << pt.y << " " << pt.z << '\n';
2410
seq.next(true); // allow to cancel
2413
// facet indices (no texture and normal indices)
2414
for (const auto& it : rFacets) {
2415
out << "3 " << it._aulPoints[0] << " " << it._aulPoints[1] << " " << it._aulPoints[2]
2417
seq.next(true); // allow to cancel
2423
bool MeshOutput::SaveBinaryPLY(std::ostream& out) const
2425
const MeshPointArray& rPoints = _rclMesh.GetPoints();
2426
const MeshFacetArray& rFacets = _rclMesh.GetFacets();
2427
std::size_t v_count = rPoints.size();
2428
std::size_t f_count = rFacets.size();
2429
if (!out || out.bad()) {
2432
bool saveVertexColor = (_material && _material->binding == MeshIO::PER_VERTEX
2433
&& _material->diffuseColor.size() == rPoints.size());
2435
<< "format binary_little_endian 1.0\n"
2436
<< "comment Created by FreeCAD <https://www.freecad.org>\n"
2437
<< "element vertex " << v_count << '\n'
2438
<< "property float32 x\n"
2439
<< "property float32 y\n"
2440
<< "property float32 z\n";
2441
if (saveVertexColor) {
2442
out << "property uchar red\n"
2443
<< "property uchar green\n"
2444
<< "property uchar blue\n";
2446
out << "element face " << f_count << '\n'
2447
<< "property list uchar int vertex_index\n"
2450
Base::OutputStream os(out);
2451
os.setByteOrder(Base::Stream::LittleEndian);
2453
for (std::size_t i = 0; i < v_count; i++) {
2454
const MeshPoint& p = rPoints[i];
2455
if (this->apply_transform) {
2456
Base::Vector3f pt = this->_transform * p;
2457
os << pt.x << pt.y << pt.z;
2460
os << p.x << p.y << p.z;
2462
if (saveVertexColor) {
2463
const App::Color& c = _material->diffuseColor[i];
2464
uint8_t r = uint8_t(255.0f * c.r);
2465
uint8_t g = uint8_t(255.0f * c.g);
2466
uint8_t b = uint8_t(255.0f * c.b);
2470
unsigned char n = 3;
2471
int f1 {}, f2 {}, f3 {};
2472
for (std::size_t i = 0; i < f_count; i++) {
2473
const MeshFacet& f = rFacets[i];
2474
f1 = (int)f._aulPoints[0];
2475
f2 = (int)f._aulPoints[1];
2476
f3 = (int)f._aulPoints[2];
2478
os << f1 << f2 << f3;
2484
bool MeshOutput::SaveAsciiPLY(std::ostream& out) const
2486
const MeshPointArray& rPoints = _rclMesh.GetPoints();
2487
const MeshFacetArray& rFacets = _rclMesh.GetFacets();
2488
std::size_t v_count = rPoints.size();
2489
std::size_t f_count = rFacets.size();
2490
if (!out || out.bad()) {
2494
bool saveVertexColor = (_material && _material->binding == MeshIO::PER_VERTEX
2495
&& _material->diffuseColor.size() == rPoints.size());
2497
<< "format ascii 1.0\n"
2498
<< "comment Created by FreeCAD <https://www.freecad.org>\n"
2499
<< "element vertex " << v_count << '\n'
2500
<< "property float32 x\n"
2501
<< "property float32 y\n"
2502
<< "property float32 z\n";
2503
if (saveVertexColor) {
2504
out << "property uchar red\n"
2505
<< "property uchar green\n"
2506
<< "property uchar blue\n";
2508
out << "element face " << f_count << '\n'
2509
<< "property list uchar int vertex_index\n"
2513
out.setf(std::ios::fixed | std::ios::showpoint);
2514
if (saveVertexColor) {
2515
for (std::size_t i = 0; i < v_count; i++) {
2516
const MeshPoint& p = rPoints[i];
2517
if (this->apply_transform) {
2518
Base::Vector3f pt = this->_transform * p;
2519
out << pt.x << " " << pt.y << " " << pt.z;
2522
out << p.x << " " << p.y << " " << p.z;
2525
const App::Color& c = _material->diffuseColor[i];
2526
int r = (int)(255.0f * c.r);
2527
int g = (int)(255.0f * c.g);
2528
int b = (int)(255.0f * c.b);
2529
out << " " << r << " " << g << " " << b << '\n';
2533
for (std::size_t i = 0; i < v_count; i++) {
2534
const MeshPoint& p = rPoints[i];
2535
if (this->apply_transform) {
2536
Base::Vector3f pt = this->_transform * p;
2537
out << pt.x << " " << pt.y << " " << pt.z << '\n';
2540
out << p.x << " " << p.y << " " << p.z << '\n';
2546
int f1 {}, f2 {}, f3 {};
2547
for (std::size_t i = 0; i < f_count; i++) {
2548
const MeshFacet& f = rFacets[i];
2549
f1 = (int)f._aulPoints[0];
2550
f2 = (int)f._aulPoints[1];
2551
f3 = (int)f._aulPoints[2];
2552
out << n << " " << f1 << " " << f2 << " " << f3 << '\n';
2558
bool MeshOutput::SaveMeshNode(std::ostream& rstrOut)
2560
const MeshPointArray& rPoints = _rclMesh.GetPoints();
2561
const MeshFacetArray& rFacets = _rclMesh.GetFacets();
2563
if (!rstrOut || rstrOut.bad()) {
2568
rstrOut << "[" << '\n';
2569
if (this->apply_transform) {
2571
for (const auto& it : rPoints) {
2572
pt = this->_transform * it;
2573
rstrOut << "v " << pt.x << " " << pt.y << " " << pt.z << '\n';
2577
for (const auto& it : rPoints) {
2578
rstrOut << "v " << it.x << " " << it.y << " " << it.z << '\n';
2581
// facet indices (no texture and normal indices)
2582
for (const auto& it : rFacets) {
2583
rstrOut << "f " << it._aulPoints[0] + 1 << " " << it._aulPoints[1] + 1 << " "
2584
<< it._aulPoints[2] + 1 << '\n';
2586
rstrOut << "]" << '\n';
2591
/** Saves the mesh object into an XML file. */
2592
void MeshOutput::SaveXML(Base::Writer& writer) const
2594
const MeshPointArray& rPoints = _rclMesh.GetPoints();
2595
const MeshFacetArray& rFacets = _rclMesh.GetFacets();
2597
// writer << writer.ind() << "<Mesh>" << '\n';
2600
writer.Stream() << writer.ind() << "<Points Count=\"" << _rclMesh.CountPoints() << "\">"
2604
if (this->apply_transform) {
2606
for (const auto& it : rPoints) {
2607
pt = this->_transform * it;
2608
writer.Stream() << writer.ind() << "<P "
2609
<< "x=\"" << pt.x << "\" "
2610
<< "y=\"" << pt.y << "\" "
2611
<< "z=\"" << pt.z << "\"/>" << '\n';
2615
for (const auto& it : rPoints) {
2616
writer.Stream() << writer.ind() << "<P "
2617
<< "x=\"" << it.x << "\" "
2618
<< "y=\"" << it.y << "\" "
2619
<< "z=\"" << it.z << "\"/>" << '\n';
2623
writer.Stream() << writer.ind() << "</Points>" << '\n';
2626
writer.Stream() << writer.ind() << "<Faces Count=\"" << _rclMesh.CountFacets() << "\">" << '\n';
2629
for (const auto& it : rFacets) {
2630
writer.Stream() << writer.ind() << "<F "
2631
<< "p0=\"" << it._aulPoints[0] << "\" "
2632
<< "p1=\"" << it._aulPoints[1] << "\" "
2633
<< "p2=\"" << it._aulPoints[2] << "\" "
2634
<< "n0=\"" << it._aulNeighbours[0] << "\" "
2635
<< "n1=\"" << it._aulNeighbours[1] << "\" "
2636
<< "n2=\"" << it._aulNeighbours[2] << "\"/>" << '\n';
2639
writer.Stream() << writer.ind() << "</Faces>" << '\n';
2641
writer.Stream() << writer.ind() << "</Mesh>" << '\n';
2645
/** Saves the mesh object into a 3MF file. */
2646
bool MeshOutput::Save3MF(std::ostream& str) const
2648
Writer3MF writer(str);
2649
writer.AddMesh(_rclMesh, _transform);
2650
return writer.Save();
2653
/** Writes an IDTF file. */
2654
bool MeshOutput::SaveIDTF(std::ostream& str) const
2656
if (!str || str.bad() || (_rclMesh.CountFacets() == 0)) {
2660
const MeshPointArray& pts = _rclMesh.GetPoints();
2661
const MeshFacetArray& fts = _rclMesh.GetFacets();
2662
std::string resource = objectName;
2663
if (resource.empty()) {
2664
resource = "Resource";
2668
str.setf(std::ios::fixed | std::ios::showpoint);
2670
str << "FILE_FORMAT \"IDTF\"\n"
2671
<< "FORMAT_VERSION 100\n\n";
2673
str << Base::tabs(0) << "NODE \"MODEL\" {\n";
2674
str << Base::tabs(1) << "NODE_NAME \"FreeCAD\"\n";
2675
str << Base::tabs(1) << "PARENT_LIST {\n";
2676
str << Base::tabs(2) << "PARENT_COUNT 1\n";
2677
str << Base::tabs(2) << "PARENT 0 {\n";
2678
str << Base::tabs(3) << "PARENT_NAME \"<NULL>\"\n";
2679
str << Base::tabs(3) << "PARENT_TM {\n";
2680
str << Base::tabs(4) << "1.000000 0.000000 0.000000 0.000000\n";
2681
str << Base::tabs(4) << "0.000000 1.000000 0.000000 0.000000\n";
2682
str << Base::tabs(4) << "0.000000 0.000000 1.000000 0.000000\n";
2683
str << Base::tabs(4) << "0.000000 0.000000 0.000000 1.000000\n";
2684
str << Base::tabs(3) << "}\n";
2685
str << Base::tabs(2) << "}\n";
2686
str << Base::tabs(1) << "}\n";
2687
str << Base::tabs(1) << "RESOURCE_NAME \"" << resource << "\"\n";
2688
str << Base::tabs(0) << "}\n\n";
2690
str << Base::tabs(0) << "RESOURCE_LIST \"MODEL\" {\n";
2691
str << Base::tabs(1) << "RESOURCE_COUNT 1\n";
2692
str << Base::tabs(1) << "RESOURCE 0 {\n";
2693
str << Base::tabs(2) << "RESOURCE_NAME \"" << resource << "\"\n";
2694
str << Base::tabs(2) << "MODEL_TYPE \"MESH\"\n";
2695
str << Base::tabs(2) << "MESH {\n";
2696
str << Base::tabs(3) << "FACE_COUNT " << fts.size() << '\n';
2697
str << Base::tabs(3) << "MODEL_POSITION_COUNT " << pts.size() << '\n';
2698
str << Base::tabs(3) << "MODEL_NORMAL_COUNT " << 3 * fts.size() << '\n';
2699
str << Base::tabs(3) << "MODEL_DIFFUSE_COLOR_COUNT 0\n";
2700
str << Base::tabs(3) << "MODEL_SPECULAR_COLOR_COUNT 0\n";
2701
str << Base::tabs(3) << "MODEL_TEXTURE_COORD_COUNT 0\n";
2702
str << Base::tabs(3) << "MODEL_BONE_COUNT 0\n";
2703
str << Base::tabs(3) << "MODEL_SHADING_COUNT 1\n";
2704
str << Base::tabs(3) << "MODEL_SHADING_DESCRIPTION_LIST {\n";
2705
str << Base::tabs(4) << "SHADING_DESCRIPTION 0 {\n";
2706
str << Base::tabs(5) << "TEXTURE_LAYER_COUNT 0\n";
2707
str << Base::tabs(5) << "SHADER_ID 0\n";
2708
str << Base::tabs(4) << "}\n";
2709
str << Base::tabs(3) << "}\n";
2710
str << Base::tabs(3) << "MESH_FACE_POSITION_LIST {\n";
2711
for (const auto& ft : fts) {
2712
str << Base::tabs(4) << ft._aulPoints[0] << " " << ft._aulPoints[1] << " "
2713
<< ft._aulPoints[2] << '\n';
2715
str << Base::tabs(3) << "}\n";
2716
str << Base::tabs(3) << "MESH_FACE_NORMAL_LIST {\n";
2718
for (MeshFacetArray::_TConstIterator it = fts.begin(); it != fts.end(); ++it) {
2719
str << Base::tabs(4) << index << " " << index + 1 << " " << index + 2 << '\n';
2722
str << Base::tabs(3) << "}\n";
2723
str << Base::tabs(3) << "MESH_FACE_SHADING_LIST {\n";
2724
for (MeshFacetArray::_TConstIterator it = fts.begin(); it != fts.end(); ++it) {
2725
str << Base::tabs(4) << "0\n";
2727
str << Base::tabs(3) << "}\n";
2728
str << Base::tabs(3) << "MODEL_POSITION_LIST {\n";
2729
for (const auto& pt : pts) {
2730
str << Base::tabs(4) << pt.x << " " << pt.y << " " << pt.z << '\n';
2732
str << Base::tabs(3) << "}\n";
2733
str << Base::tabs(3) << "MODEL_NORMAL_LIST {\n";
2734
for (const auto& ft : fts) {
2735
MeshGeomFacet face = _rclMesh.GetFacet(ft);
2736
Base::Vector3f normal = face.GetNormal();
2737
str << Base::tabs(4) << normal.x << " " << normal.y << " " << normal.z << '\n';
2738
str << Base::tabs(4) << normal.x << " " << normal.y << " " << normal.z << '\n';
2739
str << Base::tabs(4) << normal.x << " " << normal.y << " " << normal.z << '\n';
2742
str << Base::tabs(3) << "}\n";
2743
str << Base::tabs(2) << "}\n";
2744
str << Base::tabs(1) << "}\n";
2745
str << Base::tabs(0) << "}\n";
2750
/** Writes an MGL file. */
2751
bool MeshOutput::SaveMGL(std::ostream& str) const
2755
list t 0 1 2 | 0 1 3 | 0 2 3 | 1 2 3
2759
triplot t xt yt zt 'b'
2760
#triplot t xt yt zt '#k'
2762
if (!str || str.bad() || (_rclMesh.CountFacets() == 0)) {
2766
const MeshPointArray& pts = _rclMesh.GetPoints();
2767
const MeshFacetArray& fts = _rclMesh.GetFacets();
2770
str.setf(std::ios::fixed | std::ios::showpoint);
2772
str << "light on\n";
2774
for (const auto& ft : fts) {
2775
str << ft._aulPoints[0] << " " << ft._aulPoints[1] << " " << ft._aulPoints[2] << " | ";
2780
for (const auto& pt : pts) {
2786
for (const auto& pt : pts) {
2792
for (const auto& pt : pts) {
2797
str << "triplot t xt yt zt 'b'" << std::endl;
2798
str << "#triplot t xt yt zt '#k'" << std::endl;
2803
/** Writes an OpenInventor file. */
2804
bool MeshOutput::SaveInventor(std::ostream& rstrOut) const
2806
WriterInventor writer(_rclMesh, _material);
2807
writer.SetTransform(_transform);
2808
return writer.Save(rstrOut);
2811
/** Writes an X3D file. */
2812
bool MeshOutput::SaveX3D(std::ostream& out) const
2814
if (!out || out.bad() || (_rclMesh.CountFacets() == 0)) {
2819
out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
2821
return SaveX3DContent(out, false);
2824
/** Writes an X3D file. */
2825
bool MeshOutput::SaveX3DContent(std::ostream& out, bool exportViewpoints) const
2827
if (!out || out.bad() || (_rclMesh.CountFacets() == 0)) {
2831
const MeshPointArray& pts = _rclMesh.GetPoints();
2832
const MeshFacetArray& fts = _rclMesh.GetFacets();
2833
Base::BoundBox3f bbox = _rclMesh.GetBoundBox();
2834
if (apply_transform) {
2835
bbox = bbox.Transformed(_transform);
2838
App::Color mat(0.65f, 0.65f, 0.65f);
2839
if (_material && _material->binding == MeshIO::Binding::OVERALL) {
2840
if (!_material->diffuseColor.empty()) {
2841
mat = _material->diffuseColor.front();
2844
bool saveVertexColor = (_material && _material->binding == MeshIO::PER_VERTEX
2845
&& _material->diffuseColor.size() == pts.size());
2846
bool saveFaceColor = (_material && _material->binding == MeshIO::PER_FACE
2847
&& _material->diffuseColor.size() == fts.size());
2849
Base::SequencerLauncher seq("Saving...", _rclMesh.CountFacets() + 1);
2851
out.setf(std::ios::fixed | std::ios::showpoint);
2854
out << R"(<X3D profile="Immersive" version="3.2" xmlns:xsd=)"
2855
<< "\"http://www.w3.org/2001/XMLSchema-instance\" xsd:noNamespaceSchemaLocation="
2856
<< "\"http://www.web3d.org/specifications/x3d-3.2.xsd\" width=\"1280px\" "
2857
"height=\"1024px\">\n";
2859
<< " <meta name=\"generator\" content=\"FreeCAD\"/>\n"
2860
<< " <meta name=\"author\" content=\"\"/> \n"
2861
<< " <meta name=\"company\" content=\"\"/>\n"
2865
out << " <Scene>\n";
2867
if (exportViewpoints) {
2868
auto viewpoint = [&out](const char* text,
2869
const Base::Vector3f& cnt,
2870
const Base::Vector3f& pos,
2871
const Base::Vector3f& axis,
2873
out << " <Viewpoint id=\"" << text << "\" centerOfRotation=\"" << cnt.x << " "
2874
<< cnt.y << " " << cnt.z << "\" position=\"" << pos.x << " " << pos.y << " "
2875
<< pos.z << "\" orientation=\"" << axis.x << " " << axis.y << " " << axis.z << " "
2876
<< angle << R"(" description="camera" fieldOfView="0.9">)"
2877
<< "</Viewpoint>\n";
2880
Base::Vector3f cnt = bbox.GetCenter();
2881
float dist = 1.2f * bbox.CalcDiagonalLength();
2882
float dist3 = 0.577350f * dist; // sqrt(1/3) * dist
2886
Base::Vector3f(cnt.x + dist3, cnt.y - dist3, cnt.z + dist3),
2887
Base::Vector3f(0.742906f, 0.307722f, 0.594473f),
2891
Base::Vector3f(cnt.x, cnt.y - dist, cnt.z),
2892
Base::Vector3f(1.0f, 0.0f, 0.0f),
2896
Base::Vector3f(cnt.x, cnt.y + dist, cnt.z),
2897
Base::Vector3f(0.0f, 0.707106f, 0.707106f),
2901
Base::Vector3f(cnt.x + dist, cnt.y, cnt.z),
2902
Base::Vector3f(0.577350f, 0.577350f, 0.577350f),
2906
Base::Vector3f(cnt.x - dist, cnt.y, cnt.z),
2907
Base::Vector3f(-0.577350f, 0.577350f, 0.577350f),
2911
Base::Vector3f(cnt.x, cnt.y, cnt.z + dist),
2912
Base::Vector3f(0.0f, 0.0f, 1.0f),
2916
Base::Vector3f(cnt.x, cnt.y, cnt.z - dist),
2917
Base::Vector3f(1.0f, 0.0f, 0.0f),
2921
if (apply_transform) {
2922
Base::Placement p(_transform);
2923
const Base::Vector3d& v = p.getPosition();
2924
const Base::Rotation& r = p.getRotation();
2925
Base::Vector3d axis;
2927
r.getValue(axis, angle);
2928
out << " <Transform "
2929
<< "translation='" << v.x << " " << v.y << " " << v.z << "' "
2930
<< "rotation='" << axis.x << " " << axis.y << " " << axis.z << " " << angle << "'>\n";
2933
out << " <Transform>\n";
2935
out << " <Shape>\n";
2936
out << " <Appearance>\n"
2937
" <Material diffuseColor='"
2938
<< mat.r << " " << mat.g << " " << mat.b
2939
<< "' shininess='0.9' specularColor='1 1 1'></Material>\n"
2942
out << " <IndexedFaceSet solid=\"false\" ";
2943
if (saveVertexColor) {
2944
out << "colorPerVertex=\"true\" ";
2946
else if (saveFaceColor) {
2947
out << "colorPerVertex=\"false\" ";
2950
out << "coordIndex=\"";
2951
for (const auto& ft : fts) {
2952
out << ft._aulPoints[0] << " " << ft._aulPoints[1] << " " << ft._aulPoints[2] << " -1 ";
2956
out << " <Coordinate point=\"";
2957
for (const auto& pt : pts) {
2958
out << pt.x << " " << pt.y << " " << pt.z << ", ";
2962
// write colors per vertex or face
2963
if (saveVertexColor || saveFaceColor) {
2964
out << " <Color color=\"";
2965
for (const auto& c : _material->diffuseColor) {
2966
out << c.r << " " << c.g << " " << c.b << ", ";
2972
out << " </IndexedFaceSet>\n"
2974
<< " </Transform>\n"
2975
<< " <Background groundColor=\"0.7 0.7 0.7\" skyColor=\"0.7 0.7 0.7\" />\n"
2976
<< " <NavigationInfo/>\n"
2983
/** Writes an X3DOM file. */
2984
bool MeshOutput::SaveX3DOM(std::ostream& out) const
2986
if (!out || out.bad() || (_rclMesh.CountFacets() == 0)) {
2991
// https://stackoverflow.com/questions/31976056/unable-to-color-faces-using-indexedfaceset-in-x3dom
2993
out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
2994
<< "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" "
2995
"\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n";
2996
out << "<html xmlns='http://www.w3.org/1999/xhtml'>\n"
2998
<< " <script type='text/javascript' src='http://www.x3dom.org/download/x3dom.js'> "
3000
<< " <link rel='stylesheet' type='text/css' "
3001
"href='http://www.x3dom.org/download/x3dom.css'></link>\n"
3004
auto onclick = [&out](const char* text) {
3005
out << " <button onclick=\"document.getElementById('" << text
3006
<< "').setAttribute('set_bind','true');\">" << text << "</button>\n";
3017
#if 0 // https://stackoverflow.com/questions/32305678/x3dom-how-to-make-zoom-buttons
3018
function zoom (delta) {
3019
var x3d = document.getElementById("right");
3020
var vpt = x3d.getElementsByTagName("Viewpoint")[0];
3021
vpt.fieldOfView = parseFloat(vpt.fieldOfView) + delta;
3024
<button onclick="zoom(0.15);">Zoom out</button>
3027
SaveX3DContent(out, true);
3034
/** Writes a Nastran file. */
3035
bool MeshOutput::SaveNastran(std::ostream& rstrOut) const
3037
if (!rstrOut || rstrOut.bad() || (_rclMesh.CountFacets() == 0)) {
3041
MeshPointIterator clPIter(_rclMesh);
3042
clPIter.Transform(this->_transform);
3043
MeshFacetIterator clTIter(_rclMesh);
3046
Base::SequencerLauncher seq("Saving...", _rclMesh.CountFacets() + 1);
3048
rstrOut.precision(3);
3049
rstrOut.setf(std::ios::fixed | std::ios::showpoint);
3050
for (clPIter.Init(); clPIter.More(); clPIter.Next()) {
3051
float x = clPIter->x;
3052
float y = clPIter->y;
3053
float z = clPIter->z;
3057
rstrOut << std::setfill(' ') << std::setw(12) << iIndx;
3058
rstrOut << std::setfill(' ') << std::setw(16) << x;
3059
rstrOut << std::setfill(' ') << std::setw(8) << y;
3060
rstrOut << std::setfill(' ') << std::setw(8) << z;
3068
for (clTIter.Init(); clTIter.More(); clTIter.Next()) {
3069
rstrOut << "CTRIA3";
3071
rstrOut << std::setfill(' ') << std::setw(10) << iIndx;
3072
rstrOut << std::setfill(' ') << std::setw(8) << (int)0;
3073
rstrOut << std::setfill(' ') << std::setw(8) << clTIter.GetIndices()._aulPoints[1] + 1;
3074
rstrOut << std::setfill(' ') << std::setw(8) << clTIter.GetIndices()._aulPoints[0] + 1;
3075
rstrOut << std::setfill(' ') << std::setw(8) << clTIter.GetIndices()._aulPoints[2] + 1;
3082
rstrOut << "ENDDATA";
3087
/** Writes a Cadmould FE file. */
3088
bool MeshOutput::SaveCadmouldFE(std::ostream& /*rstrOut*/) const
3093
/** Writes a Python module */
3094
bool MeshOutput::SavePython(std::ostream& str) const
3096
if (!str || str.bad() || (_rclMesh.CountFacets() == 0)) {
3100
MeshFacetIterator clIter(_rclMesh);
3101
clIter.Transform(this->_transform);
3103
str.setf(std::ios::fixed | std::ios::showpoint);
3105
str << "faces = [\n";
3106
for (clIter.Init(); clIter.More(); clIter.Next()) {
3107
const MeshGeomFacet& rFacet = *clIter;
3108
for (const auto& pnt : rFacet._aclPoints) {
3109
str << "[" << pnt.x << "," << pnt.y << "," << pnt.z << "],";
3119
/** Writes a VRML file. */
3120
bool MeshOutput::SaveVRML(std::ostream& rstrOut) const
3122
if (!rstrOut || rstrOut.bad() || (_rclMesh.CountFacets() == 0)) {
3126
Base::BoundBox3f clBB = _rclMesh.GetBoundBox();
3128
Base::SequencerLauncher seq("Saving VRML file...",
3129
_rclMesh.CountPoints() + _rclMesh.CountFacets());
3131
rstrOut << "#VRML V2.0 utf8\n";
3132
rstrOut << "WorldInfo {\n"
3133
<< " title \"Exported triangle mesh to VRML97\"\n"
3134
<< " info [\"Created by FreeCAD\"\n"
3135
<< " \"<https://www.freecad.org>\"]\n"
3139
rstrOut.precision(3);
3140
rstrOut.setf(std::ios::fixed | std::ios::showpoint);
3141
rstrOut << "Transform {\n"
3143
<< " rotation 0 0 1 0\n"
3144
<< " scaleOrientation 0 0 1 0\n"
3145
<< " center " << 0.0f << " " << 0.0f << " " << 0.0f << "\n"
3146
<< " translation " << 0.0f << " " << 0.0f << " " << 0.0f << "\n";
3148
rstrOut << " children\n";
3149
rstrOut << " Shape { \n";
3152
rstrOut << " appearance\n"
3153
<< " Appearance {\n"
3156
if (_material && _material->binding == MeshIO::OVERALL) {
3157
if (!_material->diffuseColor.empty()) {
3158
App::Color c = _material->diffuseColor.front();
3159
rstrOut << " diffuseColor " << c.r << " " << c.g << " " << c.b << "\n";
3162
rstrOut << " diffuseColor 0.8 0.8 0.8\n";
3166
rstrOut << " diffuseColor 0.8 0.8 0.8\n";
3168
rstrOut << " }\n }\n"; // end write appearance
3171
// write IndexedFaceSet
3172
rstrOut << " geometry\n"
3173
<< " IndexedFaceSet {\n";
3175
rstrOut.precision(2);
3176
rstrOut.setf(std::ios::fixed | std::ios::showpoint);
3179
rstrOut << " coord\n Coordinate {\n point [\n";
3180
MeshPointIterator pPIter(_rclMesh);
3181
pPIter.Transform(this->_transform);
3182
unsigned long i = 0, k = _rclMesh.CountPoints();
3183
rstrOut.precision(3);
3184
rstrOut.setf(std::ios::fixed | std::ios::showpoint);
3185
for (pPIter.Init(); pPIter.More(); pPIter.Next()) {
3186
rstrOut << " " << pPIter->x << " " << pPIter->y << " " << pPIter->z;
3187
if (i++ < (k - 1)) {
3197
rstrOut << " ]\n }\n"; // end write coord
3199
if (_material && _material->binding != MeshIO::OVERALL) {
3200
// write colors for each vertex
3201
rstrOut << " color\n Color {\n color [\n";
3202
rstrOut.precision(3);
3203
rstrOut.setf(std::ios::fixed | std::ios::showpoint);
3204
for (std::vector<App::Color>::const_iterator pCIter = _material->diffuseColor.begin();
3205
pCIter != _material->diffuseColor.end();
3207
rstrOut << " " << float(pCIter->r) << " " << float(pCIter->g) << " "
3208
<< float(pCIter->b);
3209
if (pCIter < (_material->diffuseColor.end() - 1)) {
3217
rstrOut << " ]\n }\n";
3218
if (_material->binding == MeshIO::PER_VERTEX) {
3219
rstrOut << " colorPerVertex TRUE\n";
3222
rstrOut << " colorPerVertex FALSE\n";
3227
rstrOut << " coordIndex [\n";
3228
MeshFacetIterator pFIter(_rclMesh);
3229
pFIter.Transform(this->_transform);
3230
i = 0, k = _rclMesh.CountFacets();
3232
for (pFIter.Init(); pFIter.More(); pFIter.Next()) {
3233
MeshFacet clFacet = pFIter.GetIndices();
3234
rstrOut << " " << clFacet._aulPoints[0] << ", " << clFacet._aulPoints[1] << ", "
3235
<< clFacet._aulPoints[2] << ", -1";
3236
if (i++ < (k - 1)) {
3246
rstrOut << " ]\n }\n"; // End IndexedFaceSet
3247
rstrOut << " }\n"; // End Shape
3248
rstrOut << "}\n"; // close children and Transform
3253
// ----------------------------------------------------------------------------
3255
MeshCleanup::MeshCleanup(MeshPointArray& p, MeshFacetArray& f)
3260
void MeshCleanup::SetMaterial(Material* mat)
3262
materialArray = mat;
3265
void MeshCleanup::RemoveInvalids()
3267
// first mark all points as invalid
3268
pointArray.SetFlag(MeshPoint::INVALID);
3269
std::size_t numPoints = pointArray.size();
3271
// Now go through the facets and invalidate facets with wrong indices
3272
// If a facet is valid all its referenced points are validated again
3273
// Points that are not referenced are still invalid and thus can be deleted
3274
for (auto& it : facetArray) {
3275
for (PointIndex point : it._aulPoints) {
3276
// vertex index out of range
3277
if (point >= numPoints) {
3283
// validate referenced points
3285
pointArray[it._aulPoints[0]].ResetInvalid();
3286
pointArray[it._aulPoints[1]].ResetInvalid();
3287
pointArray[it._aulPoints[2]].ResetInvalid();
3291
// Remove the invalid items
3292
RemoveInvalidFacets();
3293
RemoveInvalidPoints();
3296
void MeshCleanup::RemoveInvalidFacets()
3298
MeshIsFlag<MeshFacet> flag;
3299
std::size_t countInvalidFacets =
3300
std::count_if(facetArray.begin(), facetArray.end(), [flag](const MeshFacet& f) {
3301
return flag(f, MeshFacet::INVALID);
3303
if (countInvalidFacets > 0) {
3305
// adjust the material array if needed
3306
if (materialArray && materialArray->binding == MeshIO::PER_FACE
3307
&& materialArray->diffuseColor.size() == facetArray.size()) {
3308
std::vector<App::Color> colors;
3309
colors.reserve(facetArray.size() - countInvalidFacets);
3310
for (std::size_t index = 0; index < facetArray.size(); index++) {
3311
if (facetArray[index].IsValid()) {
3312
colors.push_back(materialArray->diffuseColor[index]);
3316
materialArray->diffuseColor.swap(colors);
3319
MeshFacetArray copy_facets(facetArray.size() - countInvalidFacets);
3320
// copy all valid facets to the new array
3321
std::remove_copy_if(facetArray.begin(),
3323
copy_facets.begin(),
3324
[flag](const MeshFacet& f) {
3325
return flag(f, MeshFacet::INVALID);
3327
facetArray.swap(copy_facets);
3331
void MeshCleanup::RemoveInvalidPoints()
3333
MeshIsFlag<MeshPoint> flag;
3334
std::size_t countInvalidPoints =
3335
std::count_if(pointArray.begin(), pointArray.end(), [flag](const MeshPoint& p) {
3336
return flag(p, MeshPoint::INVALID);
3338
if (countInvalidPoints > 0) {
3339
// generate array of decrements
3340
std::vector<PointIndex> decrements;
3341
decrements.resize(pointArray.size());
3342
PointIndex decr = 0;
3344
MeshPointArray::_TIterator p_end = pointArray.end();
3345
std::vector<PointIndex>::iterator decr_it = decrements.begin();
3346
for (MeshPointArray::_TIterator p_it = pointArray.begin(); p_it != p_end;
3347
++p_it, ++decr_it) {
3349
if (!p_it->IsValid()) {
3354
// correct point indices of the facets
3355
MeshFacetArray::_TIterator f_end = facetArray.end();
3356
for (MeshFacetArray::_TIterator f_it = facetArray.begin(); f_it != f_end; ++f_it) {
3357
f_it->_aulPoints[0] -= decrements[f_it->_aulPoints[0]];
3358
f_it->_aulPoints[1] -= decrements[f_it->_aulPoints[1]];
3359
f_it->_aulPoints[2] -= decrements[f_it->_aulPoints[2]];
3362
// delete point, number of valid points
3363
std::size_t validPoints = pointArray.size() - countInvalidPoints;
3365
// adjust the material array if needed
3366
if (materialArray && materialArray->binding == MeshIO::PER_VERTEX
3367
&& materialArray->diffuseColor.size() == pointArray.size()) {
3368
std::vector<App::Color> colors;
3369
colors.reserve(validPoints);
3370
for (std::size_t index = 0; index < pointArray.size(); index++) {
3371
if (pointArray[index].IsValid()) {
3372
colors.push_back(materialArray->diffuseColor[index]);
3376
materialArray->diffuseColor.swap(colors);
3379
MeshPointArray copy_points(validPoints);
3380
// copy all valid facets to the new array
3381
std::remove_copy_if(pointArray.begin(),
3383
copy_points.begin(),
3384
[flag](const MeshPoint& p) {
3385
return flag(p, MeshPoint::INVALID);
3387
pointArray.swap(copy_points);
3391
// ----------------------------------------------------------------------------
3393
MeshPointFacetAdjacency::MeshPointFacetAdjacency(std::size_t p, MeshFacetArray& f)
3400
void MeshPointFacetAdjacency::Build()
3402
std::vector<std::size_t> numFacetAdjacency(numPoints);
3403
for (const auto& it : facets) {
3404
numFacetAdjacency[it._aulPoints[0]]++;
3405
numFacetAdjacency[it._aulPoints[1]]++;
3406
numFacetAdjacency[it._aulPoints[2]]++;
3409
pointFacetAdjacency.resize(numPoints);
3410
for (std::size_t i = 0; i < numPoints; i++) {
3411
pointFacetAdjacency[i].reserve(numFacetAdjacency[i]);
3414
std::size_t numFacets = facets.size();
3415
for (std::size_t i = 0; i < numFacets; i++) {
3416
for (PointIndex ptIndex : facets[i]._aulPoints) {
3417
pointFacetAdjacency[ptIndex].push_back(i);
3422
void MeshPointFacetAdjacency::SetFacetNeighbourhood()
3424
std::size_t numFacets = facets.size();
3425
for (std::size_t index = 0; index < numFacets; index++) {
3426
MeshFacet& facet1 = facets[index];
3427
for (int i = 0; i < 3; i++) {
3428
std::size_t n1 = facet1._aulPoints[i];
3429
std::size_t n2 = facet1._aulPoints[(i + 1) % 3];
3431
bool success = false;
3432
const std::vector<std::size_t>& refFacets = pointFacetAdjacency[n1];
3433
for (std::size_t it : refFacets) {
3435
MeshFacet& facet2 = facets[it];
3436
if (facet2.HasPoint(n2)) {
3437
facet1._aulNeighbours[i] = it;
3445
facet1._aulNeighbours[i] = FACET_INDEX_MAX;