FreeCAD

Форк
0
/
MeshIO.cpp 
3449 строк · 114.3 Кб
1
/***************************************************************************
2
 *   Copyright (c) 2005 Imetric 3D GmbH                                    *
3
 *                                                                         *
4
 *   This file is part of the FreeCAD CAx development system.              *
5
 *                                                                         *
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.      *
10
 *                                                                         *
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.                  *
15
 *                                                                         *
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                                *
20
 *                                                                         *
21
 ***************************************************************************/
22

23
#include "PreCompiled.h"
24

25
#ifndef _PreComp_
26
#include <algorithm>
27
#include <cmath>
28
#include <iomanip>
29
#include <sstream>
30
#include <string_view>
31
#endif
32

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>
38

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>
56

57
#include "Builder.h"
58
#include "Definitions.h"
59
#include "Degeneration.h"
60
#include "Iterator.h"
61
#include "MeshIO.h"
62
#include "MeshKernel.h"
63

64

65
using namespace MeshCore;
66

67
namespace MeshCore
68
{
69

70
std::string& ltrim(std::string& str)
71
{
72
    std::string::size_type pos = 0;
73
    for (char it : str) {
74
        if (it != 0x20 && it != 0x09) {
75
            break;
76
        }
77
        pos++;
78
    }
79
    if (pos > 0) {
80
        str = str.substr(pos);
81
    }
82
    return str;
83
}
84

85
int numDigits(int number)
86
{
87
    number = std::abs(number);
88
    int digits = 1;
89
    int step = 10;
90
    while (step <= number) {
91
        digits++;
92
        step *= 10;
93
    }
94
    return digits;
95
}
96

97
/* Usage by CMeshNastran, CMeshCadmouldFE. Added by Sergey Sukhov (26.04.2002)*/
98
struct NODE
99
{
100
    float x, y, z;
101
};
102
struct TRIA
103
{
104
    int iV[3];
105
};
106
struct QUAD
107
{
108
    int iV[4];
109
};
110

111
}  // namespace MeshCore
112

113
// --------------------------------------------------------------
114

115
bool Material::operator==(const Material& mat) const
116
{
117
    if (binding != mat.binding) {
118
        return false;
119
    }
120
    if (ambientColor != mat.ambientColor) {
121
        return false;
122
    }
123
    if (diffuseColor != mat.diffuseColor) {
124
        return false;
125
    }
126
    if (specularColor != mat.specularColor) {
127
        return false;
128
    }
129
    if (emissiveColor != mat.emissiveColor) {
130
        return false;
131
    }
132
    if (shininess != mat.shininess) {
133
        return false;
134
    }
135
    if (transparency != mat.transparency) {
136
        return false;
137
    }
138
    return true;
139
}
140

141
bool Material::operator!=(const Material& mat) const
142
{
143
    return !operator==(mat);
144
}
145

146
// --------------------------------------------------------------
147

148
std::vector<std::string> MeshInput::supportedMeshFormats()
149
{
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");
160
    return fmt;
161
}
162

163
MeshIO::Format MeshInput::getFormat(const char* FileName)
164
{
165
    Base::FileInfo fi(FileName);
166
    if (fi.hasExtension("bms")) {
167
        return MeshIO::Format::BMS;
168
    }
169
    else if (fi.hasExtension("ply")) {
170
        return MeshIO::Format::PLY;
171
    }
172
    else if (fi.hasExtension("stl")) {
173
        return MeshIO::Format::STL;
174
    }
175
    else if (fi.hasExtension("ast")) {
176
        return MeshIO::Format::ASTL;
177
    }
178
    else if (fi.hasExtension("obj")) {
179
        return MeshIO::Format::OBJ;
180
    }
181
    else if (fi.hasExtension("off")) {
182
        return MeshIO::Format::OFF;
183
    }
184
    else if (fi.hasExtension("smf")) {
185
        return MeshIO::Format::SMF;
186
    }
187
    else {
188
        throw Base::FileException("File extension not supported", FileName);
189
    }
190
}
191

192
bool MeshInput::LoadAny(const char* FileName)
193
{
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);
198
    }
199
    if (!fi.isReadable()) {
200
        throw Base::FileException("No permission on the file", FileName);
201
    }
202

203
    Base::ifstream str(fi, std::ios::in | std::ios::binary);
204

205
    if (fi.hasExtension("bms")) {
206
        _rclMesh.Read(str);
207
        return true;
208
    }
209
    else {
210
        // read file
211
        bool ok = false;
212
        if (fi.hasExtension({"stl", "ast"})) {
213
            ok = LoadSTL(str);
214
        }
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);
219
            }
220
        }
221
        else if (fi.hasExtension({"nas", "bdf"})) {
222
            ok = LoadNastran(str);
223
        }
224
        else if (fi.hasExtension("obj")) {
225
            ok = LoadOBJ(str, FileName);
226
        }
227
        else if (fi.hasExtension("smf")) {
228
            ok = LoadSMF(str);
229
        }
230
        else if (fi.hasExtension("3mf")) {
231
            ok = Load3MF(str);
232
        }
233
        else if (fi.hasExtension("off")) {
234
            ok = LoadOFF(str);
235
        }
236
        else if (fi.hasExtension("ply")) {
237
            ok = LoadPLY(str);
238
        }
239
        else {
240
            throw Base::FileException("File extension not supported", FileName);
241
        }
242

243
        return ok;
244
    }
245
}
246

247
bool MeshInput::LoadFormat(std::istream& str, MeshIO::Format fmt)
248
{
249
    switch (fmt) {
250
        case MeshIO::BMS:
251
            _rclMesh.Read(str);
252
            return true;
253
        case MeshIO::APLY:
254
        case MeshIO::PLY:
255
            return LoadPLY(str);
256
        case MeshIO::ASTL:
257
            return LoadAsciiSTL(str);
258
        case MeshIO::BSTL:
259
            return LoadBinarySTL(str);
260
        case MeshIO::STL:
261
            return LoadSTL(str);
262
        case MeshIO::OBJ:
263
            return LoadOBJ(str);
264
        case MeshIO::SMF:
265
            return LoadSMF(str);
266
        case MeshIO::ThreeMF:
267
            return Load3MF(str);
268
        case MeshIO::OFF:
269
            return LoadOFF(str);
270
        case MeshIO::IV:
271
            return LoadInventor(str);
272
        case MeshIO::NAS:
273
            return LoadNastran(str);
274
        default:
275
            throw Base::FileException("Unsupported file format");
276
    }
277
}
278

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.
281
 */
282
bool MeshInput::LoadSTL(std::istream& rstrIn)
283
{
284
    char szBuf[200];
285

286
    if (!rstrIn || rstrIn.bad()) {
287
        return false;
288
    }
289

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();
296
    if (!buf) {
297
        return false;
298
    }
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
303
    if (ulCt > 1) {
304
        ulBytes = 100;
305
    }
306
    // Either it's really an invalid STL file or it's just empty. In this case the number of facets
307
    // must be 0.
308
    if (!rstrIn.read(szBuf, ulBytes)) {
309
        return (ulCt == 0);
310
    }
311
    szBuf[ulBytes] = 0;
312
    boost::algorithm::to_upper(szBuf);
313

314
    try {
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);
321
        }
322
        else {
323
            // Ascii STL
324
            buf->pubseekoff(0, std::ios::beg, std::ios::in);
325
            return LoadAsciiSTL(rstrIn);
326
        }
327
    }
328
    catch (const Base::MemoryException&) {
329
        _rclMesh.Clear();
330
        throw;  // Throw the same instance of Base::MemoryException
331
    }
332
    catch (const Base::AbortException&) {
333
        _rclMesh.Clear();
334
        return false;
335
    }
336
    catch (const Base::Exception&) {
337
        _rclMesh.Clear();
338
        throw;  // Throw the same instance of Base::Exception
339
    }
340
    catch (...) {
341
        _rclMesh.Clear();
342
        throw;
343
    }
344

345
    return true;
346
}
347

348
/** Loads an OBJ file. */
349
bool MeshInput::LoadOBJ(std::istream& rstrIn)
350
{
351
    ReaderOBJ reader(this->_rclMesh, this->_material);
352
    if (reader.Load(rstrIn)) {
353
        _groupNames = reader.GetGroupNames();
354
        return true;
355
    }
356

357
    return false;
358
}
359

360
bool MeshInput::LoadOBJ(std::istream& str, const char* filename)
361
{
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;
368
            fi.setFile(fn);
369
            Base::ifstream mtl(fi, std::ios::in | std::ios::binary);
370
            reader.LoadMaterial(mtl);
371
            mtl.close();
372
        }
373

374
        return true;
375
    }
376

377
    return false;
378
}
379

380
/** Loads an SMF file. */
381
bool MeshInput::LoadSMF(std::istream& rstrIn)
382
{
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]+)"
387
                       "\\s+([-+]?[0-9]+)"
388
                       "\\s+([-+]?[0-9]+)\\s*$");
389
    boost::cmatch what;
390

391
    unsigned long segment = 0;
392
    MeshPointArray meshPoints;
393
    MeshFacetArray meshFacets;
394

395
    std::string line;
396
    float fX {}, fY {}, fZ {};
397
    int i1 = 1, i2 = 1, i3 = 1;
398
    MeshFacet item;
399

400
    if (!rstrIn || rstrIn.bad()) {
401
        return false;
402
    }
403

404
    std::streambuf* buf = rstrIn.rdbuf();
405
    if (!buf) {
406
        return false;
407
    }
408

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)));
415
        }
416
        else if (boost::regex_match(line.c_str(), what, rx_f3)) {
417
            // 3-vertex face
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);
427
        }
428
    }
429

430
    this->_rclMesh.Clear();  // remove all data before
431

432
    MeshCleanup meshCleanup(meshPoints, meshFacets);
433
    meshCleanup.RemoveInvalids();
434
    MeshPointFacetAdjacency meshAdj(meshPoints.size(), meshFacets);
435
    meshAdj.SetFacetNeighbourhood();
436
    this->_rclMesh.Adopt(meshPoints, meshFacets);
437

438
    return true;
439
}
440

441
/** Loads an OFF file. */
442
bool MeshInput::LoadOFF(std::istream& rstrIn)
443
{
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*$)");
446
    boost::cmatch what;
447

448
    bool colorPerVertex = false;
449
    std::vector<App::Color> diffuseColor;
450
    MeshPointArray meshPoints;
451
    MeshFacetArray meshFacets;
452

453
    std::string line;
454
    MeshFacet item;
455

456
    if (!rstrIn || rstrIn.bad()) {
457
        return false;
458
    }
459

460
    std::streambuf* buf = rstrIn.rdbuf();
461
    if (!buf) {
462
        return false;
463
    }
464

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;
470
    }
471
    else if (line.find("off") == std::string::npos) {
472
        return false;  // not an OFF file
473
    }
474

475
    // get number of vertices and faces
476
    int numPoints = 0, numFaces = 0;
477

478
    while (true) {
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);
484
            break;
485
        }
486
    }
487

488
    if (numPoints == 0 || numFaces == 0) {
489
        return false;
490
    }
491

492
    meshPoints.reserve(numPoints);
493
    meshFacets.reserve(numFaces);
494
    if (colorPerVertex) {
495
        diffuseColor.reserve(numPoints);
496
    }
497
    else {
498
        diffuseColor.reserve(numFaces);
499
    }
500

501
    int cntPoints = 0;
502
    while (cntPoints < numPoints) {
503
        if (!std::getline(rstrIn, line)) {
504
            break;
505
        }
506
        std::istringstream str(line);
507
        str.unsetf(std::ios_base::skipws);
508
        str >> std::ws;
509
        if (str.eof()) {
510
            continue;  // empty line
511
        }
512

513
        float fX {}, fY {}, fZ {};
514
        str >> fX >> std::ws >> fY >> std::ws >> fZ;
515
        if (str) {
516
            meshPoints.push_back(MeshPoint(Base::Vector3f(fX, fY, fZ)));
517
            cntPoints++;
518

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;
524
                    if (str) {
525
                        str >> std::ws >> a;
526
                        // no transparency
527
                        if (!str) {
528
                            a = 0.0f;
529
                        }
530

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;
536
                        }
537
                        diffuseColor.emplace_back(r, g, b, a);
538
                    }
539
                }
540
            }
541
        }
542
    }
543

544
    int cntFaces = 0;
545
    while (cntFaces < numFaces) {
546
        if (!std::getline(rstrIn, line)) {
547
            break;
548
        }
549
        std::istringstream str(line);
550
        str.unsetf(std::ios_base::skipws);
551
        str >> std::ws;
552
        if (str.eof()) {
553
            continue;  // empty line
554
        }
555
        int count {}, index {};
556
        str >> count;
557
        if (count >= 3) {
558
            std::vector<int> faces;
559
            faces.reserve(count);
560

561
            for (int i = 0; i < count; i++) {
562
                str >> std::ws;
563
                str >> index;
564
                faces.push_back(index);
565
            }
566

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);
570
            }
571
            cntFaces++;
572

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;
577
                if (str) {
578
                    str >> std::ws >> a;
579
                    // no transparency
580
                    if (!str) {
581
                        a = 0.0f;
582
                    }
583

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;
589
                    }
590
                    for (int i = 0; i < count - 2; i++) {
591
                        diffuseColor.emplace_back(r, g, b, a);
592
                    }
593
                }
594
            }
595
        }
596
    }
597

598
    if (_material) {
599
        if (colorPerVertex) {
600
            if (meshPoints.size() == diffuseColor.size()) {
601
                _material->binding = MeshIO::PER_VERTEX;
602
                _material->diffuseColor.swap(diffuseColor);
603
            }
604
        }
605
        else {
606
            if (meshFacets.size() == diffuseColor.size()) {
607
                _material->binding = MeshIO::PER_FACE;
608
                _material->diffuseColor.swap(diffuseColor);
609
            }
610
        }
611
    }
612
    this->_rclMesh.Clear();  // remove all data before
613

614
    MeshCleanup meshCleanup(meshPoints, meshFacets);
615
    if (_material) {
616
        meshCleanup.SetMaterial(_material);
617
    }
618
    meshCleanup.RemoveInvalids();
619
    MeshPointFacetAdjacency meshAdj(meshPoints.size(), meshFacets);
620
    meshAdj.SetFacetNeighbourhood();
621
    this->_rclMesh.Adopt(meshPoints, meshFacets);
622

623
    return true;
624
}
625

626
namespace MeshCore
627
{
628
namespace Ply
629
{
630
enum Number
631
{
632
    int8,
633
    uint8,
634
    int16,
635
    uint16,
636
    int32,
637
    uint32,
638
    float32,
639
    float64
640
};
641
struct Property
642
{
643
    using first_argument_type = std::pair<std::string, int>;
644
    using second_argument_type = std::string;
645
    using result_type = bool;
646

647
    bool operator()(const std::pair<std::string, int>& x, const std::string& y) const
648
    {
649
        return x.first == y;
650
    }
651
};
652
}  // namespace Ply
653
using namespace Ply;
654
}  // namespace MeshCore
655

656
bool MeshInput::LoadPLY(std::istream& inp)
657
{
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;
662

663
    enum
664
    {
665
        unknown,
666
        ascii,
667
        binary_little_endian,
668
        binary_big_endian
669
    } format = unknown;
670

671
    if (!inp || inp.bad()) {
672
        return false;
673
    }
674

675
    std::streambuf* buf = inp.rdbuf();
676
    if (!buf) {
677
        return false;
678
    }
679

680
    // read in the first three characters
681
    char ply[3];
682
    inp.read(ply, 3);
683
    inp.ignore(1);
684
    if (!inp) {
685
        return false;
686
    }
687
    if ((ply[0] != 'p') || (ply[1] != 'l') || (ply[2] != 'y')) {
688
        return false;  // wrong header
689
    }
690

691
    std::vector<std::pair<std::string, Ply::Number>> vertex_props;
692
    std::vector<Ply::Number> face_props;
693
    std::string line, element;
694

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);
699
        str >> std::ws;
700
        if (str.eof()) {
701
            continue;  // empty line
702
        }
703
        std::string kw;
704
        str >> kw;
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)) {
712
                return false;
713
            }
714
            if (format_string == "ascii") {
715
                format = ascii;
716
            }
717
            else if (format_string == "binary_big_endian") {
718
                format = binary_big_endian;
719
            }
720
            else if (format_string == "binary_little_endian") {
721
                format = binary_little_endian;
722
            }
723
            else {
724
                // wrong format version
725
                return false;
726
            }
727
            if (version != "1.0") {
728
                // wrong version
729
                return false;
730
            }
731
        }
732
        else if (kw == "element") {
733
            std::string name;
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)) {
739
                return false;
740
            }
741
            else if (name == "vertex") {
742
                element = name;
743
                v_count = count;
744
                meshPoints.reserve(count);
745
            }
746
            else if (name == "face") {
747
                element = name;
748
                f_count = count;
749
                meshFacets.reserve(count);
750
            }
751
            else {
752
                element.clear();
753
            }
754
        }
755
        else if (kw == "property") {
756
            std::string type, name;
757
            char space {};
758
            if (element == "vertex") {
759
                str >> space >> std::ws >> type >> space >> std::ws >> name >> std::ws;
760

761
                Ply::Number number {};
762
                if (type == "char" || type == "int8") {
763
                    number = int8;
764
                }
765
                else if (type == "uchar" || type == "uint8") {
766
                    number = uint8;
767
                }
768
                else if (type == "short" || type == "int16") {
769
                    number = int16;
770
                }
771
                else if (type == "ushort" || type == "uint16") {
772
                    number = uint16;
773
                }
774
                else if (type == "int" || type == "int32") {
775
                    number = int32;
776
                }
777
                else if (type == "uint" || type == "uint32") {
778
                    number = uint32;
779
                }
780
                else if (type == "float" || type == "float32") {
781
                    number = float32;
782
                }
783
                else if (type == "double" || type == "float64") {
784
                    number = float64;
785
                }
786
                else {
787
                    // no valid number type
788
                    return false;
789
                }
790

791
                // store the property name and type
792
                vertex_props.emplace_back(name, number);
793
            }
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;
799
                }
800
                else {
801
                    // not a 'list'
802
                    type = list;
803
                    str >> name;
804
                }
805
                if (name != "vertex_indices" && name != "vertex_index") {
806
                    Number number {};
807
                    if (type == "char" || type == "int8") {
808
                        number = int8;
809
                    }
810
                    else if (type == "uchar" || type == "uint8") {
811
                        number = uint8;
812
                    }
813
                    else if (type == "short" || type == "int16") {
814
                        number = int16;
815
                    }
816
                    else if (type == "ushort" || type == "uint16") {
817
                        number = uint16;
818
                    }
819
                    else if (type == "int" || type == "int32") {
820
                        number = int32;
821
                    }
822
                    else if (type == "uint" || type == "uint32") {
823
                        number = uint32;
824
                    }
825
                    else if (type == "float" || type == "float32") {
826
                        number = float32;
827
                    }
828
                    else if (type == "double" || type == "float64") {
829
                        number = float64;
830
                    }
831
                    else {
832
                        // no valid number type
833
                        return false;
834
                    }
835

836
                    // store the property name and type
837
                    face_props.push_back(number);
838
                }
839
            }
840
        }
841
        else if (kw == "end_header") {
842
            break;  // end of the header, now read the data
843
        }
844
    }
845

846
    // check if valid 3d points
847
    Property property;
848
    std::size_t num_x = std::count_if(vertex_props.begin(),
849
                                      vertex_props.end(),
850
                                      [&property](const std::pair<std::string, int>& p) {
851
                                          return property(p, "x");
852
                                      });
853
    if (num_x != 1) {
854
        return false;
855
    }
856

857
    std::size_t num_y = std::count_if(vertex_props.begin(),
858
                                      vertex_props.end(),
859
                                      [&property](const std::pair<std::string, int>& p) {
860
                                          return property(p, "y");
861
                                      });
862
    if (num_y != 1) {
863
        return false;
864
    }
865

866
    std::size_t num_z = std::count_if(vertex_props.begin(),
867
                                      vertex_props.end(),
868
                                      [&property](const std::pair<std::string, int>& p) {
869
                                          return property(p, "z");
870
                                      });
871
    if (num_z != 1) {
872
        return false;
873
    }
874

875
    for (auto& it : vertex_props) {
876
        if (it.first == "diffuse_red") {
877
            it.first = "red";
878
        }
879
        else if (it.first == "diffuse_green") {
880
            it.first = "green";
881
        }
882
        else if (it.first == "diffuse_blue") {
883
            it.first = "blue";
884
        }
885
    }
886

887
    // check if valid colors are set
888
    std::size_t num_r = std::count_if(vertex_props.begin(),
889
                                      vertex_props.end(),
890
                                      [&property](const std::pair<std::string, int>& p) {
891
                                          return property(p, "red");
892
                                      });
893
    std::size_t num_g = std::count_if(vertex_props.begin(),
894
                                      vertex_props.end(),
895
                                      [&property](const std::pair<std::string, int>& p) {
896
                                          return property(p, "green");
897
                                      });
898
    std::size_t num_b = std::count_if(vertex_props.begin(),
899
                                      vertex_props.end(),
900
                                      [&property](const std::pair<std::string, int>& p) {
901
                                          return property(p, "blue");
902
                                      });
903
    std::size_t rgb_colors = num_r + num_g + num_b;
904
    if (rgb_colors != 0 && rgb_colors != 3) {
905
        return false;
906
    }
907

908
    // only if set per vertex
909
    if (rgb_colors == 3) {
910
        rgb_value = MeshIO::PER_VERTEX;
911
        if (_material) {
912
            _material->binding = MeshIO::PER_VERTEX;
913
            _material->diffuseColor.reserve(v_count);
914
        }
915
    }
916

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*)");
922
        boost::smatch what;
923

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) {
928
                switch (it.second) {
929
                    case int8:
930
                    case int16:
931
                    case int32: {
932
                        if (boost::regex_search(line, what, rx_s)) {
933
                            int v {};
934
                            v = boost::lexical_cast<int>(what[1]);
935
                            prop_values[it.first] = static_cast<float>(v);
936
                            line = line.substr(what[0].length());
937
                        }
938
                        else {
939
                            return false;
940
                        }
941
                    } break;
942
                    case uint8:
943
                    case uint16:
944
                    case uint32: {
945
                        if (boost::regex_search(line, what, rx_u)) {
946
                            int v {};
947
                            v = boost::lexical_cast<int>(what[1]);
948
                            prop_values[it.first] = static_cast<float>(v);
949
                            line = line.substr(what[0].length());
950
                        }
951
                        else {
952
                            return false;
953
                        }
954
                    } break;
955
                    case float32:
956
                    case float64: {
957
                        if (boost::regex_search(line, what, rx_d)) {
958
                            double v {};
959
                            v = boost::lexical_cast<double>(what[1]);
960
                            prop_values[it.first] = static_cast<float>(v);
961
                            line = line.substr(what[0].length());
962
                        }
963
                        else {
964
                            return false;
965
                        }
966
                    } break;
967
                    default:
968
                        return false;
969
                }
970
            }
971

972
            Base::Vector3f pt;
973
            pt.x = (prop_values["x"]);
974
            pt.y = (prop_values["y"]);
975
            pt.z = (prop_values["z"]);
976
            meshPoints.push_back(pt);
977

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);
983
            }
984
        }
985

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));
993
            }
994
        }
995
    }
996
    // binary
997
    else {
998
        Base::InputStream is(inp);
999
        if (format == binary_little_endian) {
1000
            is.setByteOrder(Base::Stream::LittleEndian);
1001
        }
1002
        else {
1003
            is.setByteOrder(Base::Stream::BigEndian);
1004
        }
1005

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) {
1011
                    case int8: {
1012
                        int8_t v {};
1013
                        is >> v;
1014
                        prop_values[it.first] = static_cast<float>(v);
1015
                    } break;
1016
                    case uint8: {
1017
                        uint8_t v {};
1018
                        is >> v;
1019
                        prop_values[it.first] = static_cast<float>(v);
1020
                    } break;
1021
                    case int16: {
1022
                        int16_t v {};
1023
                        is >> v;
1024
                        prop_values[it.first] = static_cast<float>(v);
1025
                    } break;
1026
                    case uint16: {
1027
                        uint16_t v {};
1028
                        is >> v;
1029
                        prop_values[it.first] = static_cast<float>(v);
1030
                    } break;
1031
                    case int32: {
1032
                        int32_t v {};
1033
                        is >> v;
1034
                        prop_values[it.first] = static_cast<float>(v);
1035
                    } break;
1036
                    case uint32: {
1037
                        uint32_t v {};
1038
                        is >> v;
1039
                        prop_values[it.first] = static_cast<float>(v);
1040
                    } break;
1041
                    case float32: {
1042
                        float v {};
1043
                        is >> v;
1044
                        prop_values[it.first] = v;
1045
                    } break;
1046
                    case float64: {
1047
                        double v {};
1048
                        is >> v;
1049
                        prop_values[it.first] = static_cast<float>(v);
1050
                    } break;
1051
                    default:
1052
                        return false;
1053
                }
1054
            }
1055

1056
            Base::Vector3f pt;
1057
            pt.x = (prop_values["x"]);
1058
            pt.y = (prop_values["y"]);
1059
            pt.z = (prop_values["z"]);
1060
            meshPoints.push_back(pt);
1061

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);
1067
            }
1068
        }
1069

1070
        unsigned char n {};
1071
        uint32_t f1 {}, f2 {}, f3 {};
1072
        for (std::size_t i = 0; i < f_count; i++) {
1073
            is >> n;
1074
            if (n == 3) {
1075
                is >> f1 >> f2 >> f3;
1076
                if (f1 < v_count && f2 < v_count && f3 < v_count) {
1077
                    meshFacets.push_back(MeshFacet(f1, f2, f3));
1078
                }
1079
                for (auto it : face_props) {
1080
                    switch (it) {
1081
                        case int8: {
1082
                            int8_t v {};
1083
                            is >> v;
1084
                        } break;
1085
                        case uint8: {
1086
                            uint8_t v {};
1087
                            is >> v;
1088
                        } break;
1089
                        case int16: {
1090
                            int16_t v {};
1091
                            is >> v;
1092
                        } break;
1093
                        case uint16: {
1094
                            uint16_t v {};
1095
                            is >> v;
1096
                        } break;
1097
                        case int32: {
1098
                            int32_t v {};
1099
                            is >> v;
1100
                        } break;
1101
                        case uint32: {
1102
                            uint32_t v {};
1103
                            is >> v;
1104
                        } break;
1105
                        case float32: {
1106
                            is >> n;
1107
                            float v {};
1108
                            for (unsigned char j = 0; j < n; j++) {
1109
                                is >> v;
1110
                            }
1111
                        } break;
1112
                        case float64: {
1113
                            is >> n;
1114
                            double v {};
1115
                            for (unsigned char j = 0; j < n; j++) {
1116
                                is >> v;
1117
                            }
1118
                        } break;
1119
                        default:
1120
                            return false;
1121
                    }
1122
                }
1123
            }
1124
        }
1125
    }
1126

1127
    this->_rclMesh.Clear();  // remove all data before
1128

1129
    MeshCleanup meshCleanup(meshPoints, meshFacets);
1130
    if (_material) {
1131
        meshCleanup.SetMaterial(_material);
1132
    }
1133
    meshCleanup.RemoveInvalids();
1134
    MeshPointFacetAdjacency meshAdj(meshPoints.size(), meshFacets);
1135
    meshAdj.SetFacetNeighbourhood();
1136
    this->_rclMesh.Adopt(meshPoints, meshFacets);
1137

1138
    return true;
1139
}
1140

1141
bool MeshInput::LoadMeshNode(std::istream& rstrIn)
1142
{
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*");
1148
    boost::cmatch what;
1149

1150
    MeshPointArray meshPoints;
1151
    MeshFacetArray meshFacets;
1152

1153
    std::string line;
1154
    float fX {}, fY {}, fZ {};
1155
    unsigned int i1 = 1, i2 = 1, i3 = 1;
1156
    MeshGeomFacet clFacet;
1157

1158
    if (!rstrIn || rstrIn.bad()) {
1159
        return false;
1160
    }
1161

1162
    std::streambuf* buf = rstrIn.rdbuf();
1163
    if (!buf) {
1164
        return false;
1165
    }
1166

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)));
1174
        }
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));
1180
        }
1181
        else if (boost::regex_match(line.c_str(), what, rx_e)) {
1182
            break;
1183
        }
1184
    }
1185

1186
    this->_rclMesh.Clear();  // remove all data before
1187

1188
    MeshCleanup meshCleanup(meshPoints, meshFacets);
1189
    meshCleanup.RemoveInvalids();
1190
    MeshPointFacetAdjacency meshAdj(meshPoints.size(), meshFacets);
1191
    meshAdj.SetFacetNeighbourhood();
1192
    this->_rclMesh.Adopt(meshPoints, meshFacets);
1193

1194
    return true;
1195
}
1196

1197
/** Loads an ASCII STL file. */
1198
bool MeshInput::LoadAsciiSTL(std::istream& rstrIn)
1199
{
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*$");
1206
    boost::cmatch what;
1207

1208
    std::string line;
1209
    float fX {}, fY {}, fZ {};
1210
    unsigned long ulVertexCt {}, ulFacetCt {};
1211
    MeshGeomFacet clFacet;
1212

1213
    if (!rstrIn || rstrIn.bad()) {
1214
        return false;
1215
    }
1216

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);
1221
    ulSize -= 20;
1222

1223
    // count facets
1224
    while (std::getline(rstrIn, line)) {
1225
        boost::algorithm::to_upper(line);
1226
        if (line.find("ENDFACET") != std::string::npos) {
1227
            ulFacetCt++;
1228
        }
1229
        // prevent from reading EOF (as I don't know how to reread the file then)
1230
        if (rstrIn.tellg() > ulSize) {
1231
            break;
1232
        }
1233
        else if (line.find("ENDSOLID") != std::string::npos) {
1234
            break;
1235
        }
1236
    }
1237

1238
    // restart from the beginning
1239
    buf->pubseekoff(0, std::ios::beg, std::ios::in);
1240

1241
#if 0
1242
    MeshBuilder builder(this->_rclMesh);
1243
#else
1244
    MeshFastBuilder builder(this->_rclMesh);
1245
#endif
1246
    builder.Initialize(ulFacetCt);
1247

1248
    ulVertexCt = 0;
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));
1256
        }
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) {
1263
                ulVertexCt = 0;
1264
                builder.AddFacet(clFacet);
1265
            }
1266
        }
1267
    }
1268

1269
    builder.Finish();
1270

1271
    return true;
1272
}
1273

1274
/** Loads a binary STL file. */
1275
bool MeshInput::LoadBinarySTL(std::istream& rstrIn)
1276
{
1277
    char szInfo[80];
1278
    Base::Vector3f clVects[4];
1279
    uint16_t usAtt = 0;
1280
    uint32_t ulCt = 0;
1281

1282
    if (!rstrIn || rstrIn.bad()) {
1283
        return false;
1284
    }
1285

1286
    // Header-Info ueberlesen
1287
    rstrIn.read(szInfo, sizeof(szInfo));
1288

1289
    // Anzahl Facets
1290
    rstrIn.read((char*)&ulCt, sizeof(ulCt));
1291
    if (rstrIn.bad()) {
1292
        return false;
1293
    }
1294

1295
    // get file size and calculate the number of facets
1296
    std::streamoff ulSize = 0;
1297
    std::streambuf* buf = rstrIn.rdbuf();
1298
    if (buf) {
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);
1303
    }
1304

1305
    uint32_t ulFac = (ulSize - (80 + sizeof(uint32_t))) / 50;
1306

1307
    // compare the calculated with the read value
1308
    if (ulCt > ulFac) {
1309
        return false;  // not a valid STL file
1310
    }
1311

1312
#if 0
1313
    MeshBuilder builder(this->_rclMesh);
1314
#else
1315
    MeshFastBuilder builder(this->_rclMesh);
1316
#endif
1317
    builder.Initialize(ulCt);
1318

1319
    for (uint32_t i = 0; i < ulCt; i++) {
1320
        // read normal, points
1321
        rstrIn.read((char*)&clVects, sizeof(clVects));
1322

1323
        std::swap(clVects[0], clVects[3]);
1324
        builder.AddFacet(clVects);
1325

1326
        // overread 2 bytes attribute
1327
        rstrIn.read((char*)&usAtt, sizeof(usAtt));
1328
    }
1329

1330
    builder.Finish();
1331

1332
    return true;
1333
}
1334

1335
/** Loads the mesh object from an XML file. */
1336
void MeshInput::LoadXML(Base::XMLReader& reader)
1337
{
1338
    MeshPointArray cPoints;
1339
    MeshFacetArray cFacets;
1340

1341
    //  reader.readElement("Mesh");
1342

1343
    reader.readElement("Points");
1344
    int Cnt = reader.getAttributeAsInteger("Count");
1345

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");
1352
    }
1353
    reader.readEndElement("Points");
1354

1355
    reader.readElement("Faces");
1356
    Cnt = reader.getAttributeAsInteger("Count");
1357

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");
1367
    }
1368

1369
    reader.readEndElement("Faces");
1370
    reader.readEndElement("Mesh");
1371

1372
    _rclMesh.Adopt(cPoints, cFacets);
1373
}
1374

1375
/** Loads a 3MF file. */
1376
bool MeshInput::Load3MF(std::istream& inp)
1377
{
1378
    Reader3MF reader(inp);
1379
    reader.Load();
1380
    std::vector<int> ids = reader.GetMeshIds();
1381
    if (!ids.empty()) {
1382
        MeshKernel compound = reader.GetMesh(ids[0]);
1383
        compound.Transform(reader.GetTransform(ids[0]));
1384

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);
1389
        }
1390

1391
        _rclMesh = compound;
1392
        return true;
1393
    }
1394

1395
    return false;
1396
}
1397

1398
/** Loads an OpenInventor file. */
1399
bool MeshInput::LoadInventor(std::istream& inp)
1400
{
1401
    Base::InventorLoader loader(inp);
1402
    if (!loader.read()) {
1403
        return false;
1404
    }
1405

1406
    if (!loader.isValid()) {
1407
        return false;
1408
    }
1409

1410
    const auto& points = loader.getPoints();
1411
    const auto& faces = loader.getFaces();
1412

1413
    MeshPointArray meshPoints;
1414
    meshPoints.reserve(points.size());
1415
    std::transform(points.begin(),
1416
                   points.end(),
1417
                   std::back_inserter(meshPoints),
1418
                   [](const Base::Vector3f& v) {
1419
                       return MeshPoint(v);
1420
                   });
1421

1422
    MeshFacetArray meshFacets;
1423
    meshFacets.reserve(faces.size());
1424
    std::transform(faces.begin(),
1425
                   faces.end(),
1426
                   std::back_inserter(meshFacets),
1427
                   [](const Base::InventorLoader::Face& f) {
1428
                       return MeshFacet(f.p1, f.p2, f.p3);
1429
                   });
1430

1431
    MeshCleanup meshCleanup(meshPoints, meshFacets);
1432
    meshCleanup.RemoveInvalids();
1433
    MeshPointFacetAdjacency meshAdj(meshPoints.size(), meshFacets);
1434
    meshAdj.SetFacetNeighbourhood();
1435
    this->_rclMesh.Adopt(meshPoints, meshFacets);
1436

1437
    if (loader.isNonIndexed()) {
1438
        if (!MeshEvalDuplicatePoints(this->_rclMesh).Evaluate()) {
1439
            MeshFixDuplicatePoints(this->_rclMesh).Fixup();
1440
        }
1441
    }
1442

1443
    return true;
1444
}
1445

1446
/** Loads a Nastran file. */
1447
bool MeshInput::LoadNastran(std::istream& rstrIn)
1448
{
1449
    if (!rstrIn || rstrIn.bad()) {
1450
        return false;
1451
    }
1452

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*");
1457
    boost::cmatch what;
1458

1459
    std::string line;
1460
    MeshFacet clMeshFacet;
1461
    MeshPointArray vVertices;
1462
    MeshFacetArray vTriangle;
1463

1464
    int index {};
1465
    std::map<int, NODE> mNode;
1466
    std::map<int, TRIA> mTria;
1467
    std::map<int, QUAD> mQuad;
1468

1469
    int badElementCounter = 0;
1470

1471
    while (std::getline(rstrIn, line)) {
1472
        boost::algorithm::to_upper(ltrim(line));
1473
        if (line.empty()) {
1474
            // Skip all the following tests
1475
        }
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
1479
            // float.
1480
            //
1481
            // The two lines are:
1482
            // 1      8               24             40             56
1483
            // GRID*  Index(16)       Blank(16)      x(16)          y(at least one)
1484
            // *      z(at least one)
1485
            //
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:
1490
            //
1491
            // GRID*  1                               0.1234567890120.
1492
            // *      1.
1493
            //
1494
            if (line.length()
1495
                < 8 + 16 + 16 + 16 + 1) {  // Element type(8), index(16), empty(16), x(16), y(>=1)
1496
                badElementCounter++;
1497
                continue;
1498
            }
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]);
1503

1504
            std::string line2;
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
1509
            }
1510
            auto zView = std::string_view(&line2[8]);
1511

1512
            // We have to strip off any whitespace (technically really just any *trailing*
1513
            // whitespace):
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));
1518

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++;
1524
                continue;
1525
            }
1526
            index =
1527
                indexCheck.get() - 1;  // Minus one so we are zero-indexed to match existing code
1528

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);
1533

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++;
1537
                continue;
1538
            }
1539

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();
1544
        }
1545
        else if (line.rfind("GRID", 0) == 0) {
1546

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*");
1551

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);
1558
            }
1559
            else {
1560
                // Classic NASTRAN uses a fixed 8 character field width:
1561
                // 1       8       16      24      32      40
1562
                // $-------ID------CP------X1------X2------X3------CD------PS------9-------+-------
1563
                // GRID    1               1.2345671.2345671.234567
1564
                // GRID    112             6.0000000.5000000.00E+00
1565

1566
                if (line.length()
1567
                    < 41) {  // Element type(8), id(8), cp(8), x(8), y(8), z(at least 1)
1568
                    badElementCounter++;
1569
                    continue;
1570
                }
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);
1575

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));
1580

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++;
1586
                    continue;
1587
                }
1588
                index = indexCheck.get()
1589
                    - 1;  // Minus one so we are zero-indexed to match existing code
1590

1591
                auto x = boost::convert<float>(xString, converter);
1592
                auto y = boost::convert<float>(yString, converter);
1593
                auto z = boost::convert<float>(zString, converter);
1594

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++;
1598
                    continue;
1599
                }
1600

1601
                mNode[index].x = x.get();
1602
                mNode[index].y = y.get();
1603
                mNode[index].z = z.get();
1604
            }
1605
        }
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;
1613
            }
1614
        }
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;
1623
            }
1624
        }
1625
    }
1626

1627
    if (badElementCounter > 0) {
1628
        Base::Console().Warning("Found bad elements while reading NASTRAN file.\n");
1629
    }
1630

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");
1637
                return false;
1638
            }
1639
        }
1640
    }
1641

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");
1648
                return false;
1649
            }
1650
        }
1651
    }
1652

1653
    float fLength[2];
1654
    if (mTria.empty()) {
1655
        index = 0;
1656
    }
1657
    else {
1658
        index = mTria.rbegin()->first + 1;
1659
    }
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;
1666
        }
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];
1671

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];
1675
        }
1676
        else {
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];
1680

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];
1684
        }
1685

1686
        index += 2;
1687
    }
1688

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));
1693
    }
1694

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);
1702
    }
1703

1704
    // make sure to add only vertices which are referenced by the triangles
1705
    _rclMesh.Merge(vVertices, vTriangle);
1706

1707
    return true;
1708
}
1709

1710
/** Loads a Cadmould FE file. */
1711
bool MeshInput::LoadCadmouldFE(std::ifstream& rstrIn)
1712
{
1713
    if (!rstrIn || rstrIn.bad()) {
1714
        return false;
1715
    }
1716
    assert(0);
1717
    return false;
1718
}
1719

1720
// --------------------------------------------------------------
1721

1722
std::string MeshOutput::stl_header = "MESH-MESH-MESH-MESH-MESH-MESH-MESH-MESH-"
1723
                                     "MESH-MESH-MESH-MESH-MESH-MESH-MESH-MESH\n";
1724

1725
void MeshOutput::SetSTLHeaderData(const std::string& header)
1726
{
1727
    if (header.size() > 80) {
1728
        stl_header = header.substr(0, 80);
1729
    }
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());
1733
    }
1734
    else {
1735
        stl_header = header;
1736
    }
1737
}
1738

1739
std::string MeshOutput::asyWidth = "500";
1740
std::string MeshOutput::asyHeight = "500";
1741

1742
void MeshOutput::SetAsymptoteSize(const std::string& w, const std::string& h)
1743
{
1744
    asyWidth = w;
1745
    asyHeight = h;
1746
}
1747

1748
void MeshOutput::Transform(const Base::Matrix4D& mat)
1749
{
1750
    _transform = mat;
1751
    if (mat != Base::Matrix4D()) {
1752
        apply_transform = true;
1753
    }
1754
}
1755

1756
std::vector<std::string> MeshOutput::supportedMeshFormats()
1757
{
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");
1773
    return fmt;
1774
}
1775

1776
MeshIO::Format MeshOutput::GetFormat(const char* FileName)
1777
{
1778
    Base::FileInfo file(FileName);
1779
    if (file.hasExtension("bms")) {
1780
        return MeshIO::BMS;
1781
    }
1782
    else if (file.hasExtension("stl")) {
1783
        return MeshIO::BSTL;
1784
    }
1785
    else if (file.hasExtension("ast")) {
1786
        return MeshIO::ASTL;
1787
    }
1788
    else if (file.hasExtension("obj")) {
1789
        return MeshIO::OBJ;
1790
    }
1791
    else if (file.hasExtension("off")) {
1792
        return MeshIO::OFF;
1793
    }
1794
    else if (file.hasExtension("ply")) {
1795
        return MeshIO::PLY;
1796
    }
1797
    else if (file.hasExtension("idtf")) {
1798
        return MeshIO::IDTF;
1799
    }
1800
    else if (file.hasExtension("mgl")) {
1801
        return MeshIO::MGL;
1802
    }
1803
    else if (file.hasExtension("iv")) {
1804
        return MeshIO::IV;
1805
    }
1806
    else if (file.hasExtension("x3d")) {
1807
        return MeshIO::X3D;
1808
    }
1809
    else if (file.hasExtension("x3dz")) {
1810
        return MeshIO::X3DZ;
1811
    }
1812
    else if (file.hasExtension("xhtml")) {
1813
        return MeshIO::X3DOM;
1814
    }
1815
    else if (file.hasExtension("py")) {
1816
        return MeshIO::PY;
1817
    }
1818
    else if (file.hasExtension({"wrl", "vrml"})) {
1819
        return MeshIO::VRML;
1820
    }
1821
    else if (file.hasExtension("wrz")) {
1822
        return MeshIO::WRZ;
1823
    }
1824
    else if (file.hasExtension({"nas", "bdf"})) {
1825
        return MeshIO::NAS;
1826
    }
1827
    else if (file.hasExtension("amf")) {
1828
        return MeshIO::AMF;
1829
    }
1830
    else if (file.hasExtension("3mf")) {
1831
        return MeshIO::ThreeMF;
1832
    }
1833
    else if (file.hasExtension("smf")) {
1834
        return MeshIO::SMF;
1835
    }
1836
    else if (file.hasExtension("asy")) {
1837
        return MeshIO::ASY;
1838
    }
1839
    else {
1840
        return MeshIO::Undefined;
1841
    }
1842
}
1843

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
1846
{
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);
1852
    }
1853

1854
    MeshIO::Format fileformat = format;
1855
    if (fileformat == MeshIO::Undefined) {
1856
        fileformat = GetFormat(FileName);
1857
    }
1858

1859
    Base::ofstream str(file, std::ios::out | std::ios::binary);
1860

1861
    if (fileformat == MeshIO::BMS) {
1862
        _rclMesh.Write(str);
1863
    }
1864
    else if (fileformat == MeshIO::BSTL) {
1865
        MeshOutput aWriter(_rclMesh);
1866
        aWriter.Transform(this->_transform);
1867

1868
        // write file
1869
        bool ok = false;
1870
        ok = aWriter.SaveBinarySTL(str);
1871
        if (!ok) {
1872
            throw Base::FileException("Export of STL mesh failed", FileName);
1873
        }
1874
    }
1875
    else if (fileformat == MeshIO::ASTL) {
1876
        MeshOutput aWriter(_rclMesh);
1877
        aWriter.SetObjectName(objectName);
1878
        aWriter.Transform(this->_transform);
1879

1880
        // write file
1881
        bool ok = false;
1882
        ok = aWriter.SaveAsciiSTL(str);
1883
        if (!ok) {
1884
            throw Base::FileException("Export of STL mesh failed", FileName);
1885
        }
1886
    }
1887
    else if (fileformat == MeshIO::OBJ) {
1888
        // write file
1889
        if (!SaveOBJ(str, FileName)) {
1890
            throw Base::FileException("Export of OBJ mesh failed", FileName);
1891
        }
1892
    }
1893
    else if (fileformat == MeshIO::SMF) {
1894
        // write file
1895
        if (!SaveSMF(str)) {
1896
            throw Base::FileException("Export of SMF mesh failed", FileName);
1897
        }
1898
    }
1899
    else if (fileformat == MeshIO::OFF) {
1900
        // write file
1901
        if (!SaveOFF(str)) {
1902
            throw Base::FileException("Export of OFF mesh failed", FileName);
1903
        }
1904
    }
1905
    else if (fileformat == MeshIO::PLY) {
1906
        // write file
1907
        if (!SaveBinaryPLY(str)) {
1908
            throw Base::FileException("Export of PLY mesh failed", FileName);
1909
        }
1910
    }
1911
    else if (fileformat == MeshIO::APLY) {
1912
        // write file
1913
        if (!SaveAsciiPLY(str)) {
1914
            throw Base::FileException("Export of PLY mesh failed", FileName);
1915
        }
1916
    }
1917
    else if (fileformat == MeshIO::IDTF) {
1918
        // write file
1919
        if (!SaveIDTF(str)) {
1920
            throw Base::FileException("Export of IDTF mesh failed", FileName);
1921
        }
1922
    }
1923
    else if (fileformat == MeshIO::MGL) {
1924
        // write file
1925
        if (!SaveMGL(str)) {
1926
            throw Base::FileException("Export of MGL mesh failed", FileName);
1927
        }
1928
    }
1929
    else if (fileformat == MeshIO::IV) {
1930
        // write file
1931
        if (!SaveInventor(str)) {
1932
            throw Base::FileException("Export of Inventor mesh failed", FileName);
1933
        }
1934
    }
1935
    else if (fileformat == MeshIO::X3D) {
1936
        // write file
1937
        if (!SaveX3D(str)) {
1938
            throw Base::FileException("Export of X3D failed", FileName);
1939
        }
1940
    }
1941
    else if (fileformat == MeshIO::X3DZ) {
1942
        // Compressed X3D is nothing else than a GZIP'ped X3D ascii file
1943
        zipios::GZIPOutputStream gzip(str);
1944
        // write file
1945
        if (!SaveX3D(gzip)) {
1946
            throw Base::FileException("Export of compressed X3D mesh failed", FileName);
1947
        }
1948
    }
1949
    else if (fileformat == MeshIO::X3DOM) {
1950
        // write file
1951
        if (!SaveX3DOM(str)) {
1952
            throw Base::FileException("Export of X3DOM failed", FileName);
1953
        }
1954
    }
1955
    else if (fileformat == MeshIO::ThreeMF) {
1956
        // write file
1957
        if (!Save3MF(str)) {
1958
            throw Base::FileException("Export of 3MF failed", FileName);
1959
        }
1960
    }
1961
    else if (fileformat == MeshIO::PY) {
1962
        // write file
1963
        if (!SavePython(str)) {
1964
            throw Base::FileException("Export of Python mesh failed", FileName);
1965
        }
1966
    }
1967
    else if (fileformat == MeshIO::VRML) {
1968
        // write file
1969
        if (!SaveVRML(str)) {
1970
            throw Base::FileException("Export of VRML mesh failed", FileName);
1971
        }
1972
    }
1973
    else if (fileformat == MeshIO::WRZ) {
1974
        // Compressed VRML is nothing else than a GZIP'ped VRML ascii file
1975
        // str.close();
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);
1981
        // write file
1982
        if (!SaveVRML(gzip)) {
1983
            throw Base::FileException("Export of compressed VRML mesh failed", FileName);
1984
        }
1985
    }
1986
    else if (fileformat == MeshIO::NAS) {
1987
        // write file
1988
        if (!SaveNastran(str)) {
1989
            throw Base::FileException("Export of NASTRAN mesh failed", FileName);
1990
        }
1991
    }
1992
    else if (fileformat == MeshIO::ASY) {
1993
        // write file
1994
        if (!SaveAsymptote(str)) {
1995
            throw Base::FileException("Export of ASY mesh failed", FileName);
1996
        }
1997
    }
1998
    else {
1999
        throw Base::FileException("File format not supported", FileName);
2000
    }
2001

2002
    return true;
2003
}
2004

2005
bool MeshOutput::SaveFormat(std::ostream& str, MeshIO::Format fmt) const
2006
{
2007
    switch (fmt) {
2008
        case MeshIO::BMS:
2009
            _rclMesh.Write(str);
2010
            return true;
2011
        case MeshIO::ASTL:
2012
            return SaveAsciiSTL(str);
2013
        case MeshIO::BSTL:
2014
            return SaveBinarySTL(str);
2015
        case MeshIO::OBJ:
2016
            return SaveOBJ(str);
2017
        case MeshIO::SMF:
2018
            return SaveSMF(str);
2019
        case MeshIO::OFF:
2020
            return SaveOFF(str);
2021
        case MeshIO::IDTF:
2022
            return SaveIDTF(str);
2023
        case MeshIO::MGL:
2024
            return SaveMGL(str);
2025
        case MeshIO::IV:
2026
            return SaveInventor(str);
2027
        case MeshIO::X3D:
2028
            return SaveX3D(str);
2029
        case MeshIO::X3DOM:
2030
            return SaveX3DOM(str);
2031
        case MeshIO::VRML:
2032
            return SaveVRML(str);
2033
        case MeshIO::WRZ:
2034
            // it's up to the client to create the needed stream
2035
            return SaveVRML(str);
2036
        case MeshIO::ThreeMF:
2037
            return Save3MF(str);
2038
        case MeshIO::NAS:
2039
            return SaveNastran(str);
2040
        case MeshIO::PLY:
2041
            return SaveBinaryPLY(str);
2042
        case MeshIO::APLY:
2043
            return SaveAsciiPLY(str);
2044
        case MeshIO::PY:
2045
            return SavePython(str);
2046
        case MeshIO::ASY:
2047
            return SaveAsymptote(str);
2048
        default:
2049
            throw Base::FileException("Unsupported file format");
2050
    }
2051
}
2052

2053
/** Saves the mesh object into an ASCII file. */
2054
bool MeshOutput::SaveAsciiSTL(std::ostream& rstrOut) const
2055
{
2056
    MeshFacetIterator clIter(_rclMesh), clEnd(_rclMesh);
2057
    clIter.Transform(this->_transform);
2058
    const MeshGeomFacet* pclFacet {};
2059

2060
    if (!rstrOut || rstrOut.bad() || _rclMesh.CountFacets() == 0) {
2061
        return false;
2062
    }
2063

2064
    rstrOut.precision(6);
2065
    rstrOut.setf(std::ios::fixed | std::ios::showpoint);
2066
    Base::SequencerLauncher seq("saving...", _rclMesh.CountFacets() + 1);
2067

2068
    if (this->objectName.empty()) {
2069
        rstrOut << "solid Mesh\n";
2070
    }
2071
    else {
2072
        rstrOut << "solid " << this->objectName << '\n';
2073
    }
2074

2075
    clIter.Begin();
2076
    clEnd.End();
2077
    while (clIter < clEnd) {
2078
        pclFacet = &(*clIter);
2079

2080
        // normal
2081
        rstrOut << "  facet normal " << pclFacet->GetNormal().x << " " << pclFacet->GetNormal().y
2082
                << " " << pclFacet->GetNormal().z << '\n';
2083
        rstrOut << "    outer loop\n";
2084

2085
        // vertices
2086
        for (const auto& pnt : pclFacet->_aclPoints) {
2087
            rstrOut << "      vertex " << pnt.x << " " << pnt.y << " " << pnt.z << '\n';
2088
        }
2089

2090
        rstrOut << "    endloop\n";
2091
        rstrOut << "  endfacet\n";
2092

2093
        ++clIter;
2094
        seq.next(true);  // allow to cancel
2095
    }
2096

2097
    rstrOut << "endsolid Mesh\n";
2098

2099
    return true;
2100
}
2101

2102
/** Saves the mesh object into a binary file. */
2103
bool MeshOutput::SaveBinarySTL(std::ostream& rstrOut) const
2104
{
2105
    MeshFacetIterator clIter(_rclMesh), clEnd(_rclMesh);
2106
    clIter.Transform(this->_transform);
2107
    const MeshGeomFacet* pclFacet {};
2108
    uint32_t i {};
2109
    uint16_t usAtt {};
2110
    char szInfo[81];
2111

2112
    if (!rstrOut || rstrOut.bad() /*|| _rclMesh.CountFacets() == 0*/) {
2113
        return false;
2114
    }
2115

2116
    Base::SequencerLauncher seq("saving...", _rclMesh.CountFacets() + 1);
2117

2118
    // stl_header has a length of 80
2119
    strcpy(szInfo, stl_header.c_str());
2120
    rstrOut.write(szInfo, std::strlen(szInfo));
2121

2122
    uint32_t uCtFts = (uint32_t)_rclMesh.CountFacets();
2123
    rstrOut.write((const char*)&uCtFts, sizeof(uCtFts));
2124

2125
    usAtt = 0;
2126
    clIter.Begin();
2127
    clEnd.End();
2128
    while (clIter < clEnd) {
2129
        pclFacet = &(*clIter);
2130
        // normal
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));
2135

2136
        // vertices
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));
2141
        }
2142

2143
        // attribute
2144
        rstrOut.write((const char*)&usAtt, sizeof(usAtt));
2145

2146
        ++clIter;
2147
        seq.next(true);  // allow to cancel
2148
    }
2149

2150
    return true;
2151
}
2152

2153
/** Saves an OBJ file. */
2154
bool MeshOutput::SaveOBJ(std::ostream& out) const
2155
{
2156
    WriterOBJ writer(this->_rclMesh, this->_material);
2157
    writer.SetTransform(this->_transform);
2158
    writer.SetGroups(this->_groups);
2159
    return writer.Save(out);
2160
}
2161

2162
bool MeshOutput::SaveOBJ(std::ostream& out, const char* filename) const
2163
{
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;
2171
            fi.setFile(fn);
2172
            Base::ofstream mtl(fi, std::ios::out | std::ios::binary);
2173
            writer.SaveMaterial(mtl);
2174
            mtl.close();
2175
        }
2176

2177
        return true;
2178
    }
2179

2180
    return false;
2181
}
2182

2183
/** Saves an SMF file. */
2184
bool MeshOutput::SaveSMF(std::ostream& out) const
2185
{
2186
    // http://people.sc.fsu.edu/~jburkardt/data/smf/smf.txt
2187
    const MeshPointArray& rPoints = _rclMesh.GetPoints();
2188
    const MeshFacetArray& rFacets = _rclMesh.GetFacets();
2189

2190
    if (!out || out.bad()) {
2191
        return false;
2192
    }
2193

2194
    Base::SequencerLauncher seq("saving...", _rclMesh.CountPoints() + _rclMesh.CountFacets());
2195

2196
    // Header
2197
    out << "#$SMF 1.0\n";
2198
    out << "#$vertices " << rPoints.size() << '\n';
2199
    out << "#$faces " << rFacets.size() << '\n';
2200
    out << "#\n";
2201
    out << "# Created by FreeCAD <https://www.freecad.org>\n";
2202

2203
    out.precision(6);
2204
    out.setf(std::ios::fixed | std::ios::showpoint);
2205

2206
    // vertices
2207
    Base::Vector3f pt;
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;
2212
        }
2213
        else {
2214
            pt.Set(it->x, it->y, it->z);
2215
        }
2216

2217
        out << "v " << pt.x << " " << pt.y << " " << pt.z << '\n';
2218
        seq.next(true);  // allow to cancel
2219
    }
2220

2221
    // facet indices
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
2226
    }
2227

2228
    return true;
2229
}
2230

2231
/** Saves an Asymptote file. */
2232
bool MeshOutput::SaveAsymptote(std::ostream& out) const
2233
{
2234
    out << "/*\n"
2235
           " * Created by FreeCAD <https://www.freecad.org>\n"
2236
           " */\n\n";
2237

2238
    out << "import three;\n\n";
2239

2240
    if (!asyWidth.empty()) {
2241
        out << "size(" << asyWidth;
2242
        if (!asyHeight.empty()) {
2243
            out << ", " << asyHeight;
2244
        }
2245
        out << ");\n\n";
2246
    }
2247

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);
2255

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 << ", "
2260
        << target.z
2261
        << "),\n"
2262
           "                                 showtarget = false,\n"
2263
           "                                 up = ("
2264
        << upvec.x << ", " << upvec.y << ", " << upvec.z << "));\n\n";
2265

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";
2272

2273
    out << "// ME:Mesh, OB:Mesh\n";
2274

2275
    MeshFacetIterator clIter(_rclMesh), clEnd(_rclMesh);
2276
    clIter.Transform(this->_transform);
2277
    clIter.Begin();
2278
    clEnd.End();
2279

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];
2290
    }
2291

2292
    std::size_t index = 0;
2293
    const MeshGeomFacet* pclFacet {};
2294
    while (clIter < clEnd) {
2295
        pclFacet = &(*clIter);
2296

2297
        out << "draw(surface(";
2298

2299
        // vertices
2300
        for (const auto& pnt : pclFacet->_aclPoints) {
2301
            out << '(' << pnt.x << ", " << pnt.y << ", " << pnt.z << ")--";
2302
        }
2303

2304
        out << "cycle";
2305

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 << ")";
2312
                if (i < 2) {
2313
                    out << ", ";
2314
                }
2315
            }
2316
            out << "}));\n";
2317
        }
2318
        else if (saveFaceColor) {
2319
            const App::Color& c = _material->diffuseColor[index];
2320
            out << "),\n     rgb(" << c.r << ", " << c.g << ", " << c.b << "));\n";
2321
        }
2322
        else {
2323
            out << "),\n     rgb(" << mc.r << ", " << mc.g << ", " << mc.b << "));\n";
2324
        }
2325

2326
        ++clIter;
2327
        ++index;
2328
    }
2329

2330
    return true;
2331
}
2332

2333
/** Saves an OFF file. */
2334
bool MeshOutput::SaveOFF(std::ostream& out) const
2335
{
2336
    const MeshPointArray& rPoints = _rclMesh.GetPoints();
2337
    const MeshFacetArray& rFacets = _rclMesh.GetFacets();
2338

2339
    if (!out || out.bad()) {
2340
        return false;
2341
    }
2342

2343
    Base::SequencerLauncher seq("saving...", _rclMesh.CountPoints() + _rclMesh.CountFacets());
2344

2345
    bool exportColor = false;
2346
    if (_material) {
2347
        if (_material->binding == MeshIO::PER_FACE) {
2348
            Base::Console().Warning(
2349
                "Cannot export color information because it's defined per face");
2350
        }
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");
2355
            }
2356
            else {
2357
                exportColor = true;
2358
            }
2359
        }
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");
2364
            }
2365
            else {
2366
                exportColor = true;
2367
            }
2368
        }
2369
    }
2370

2371
    if (exportColor) {
2372
        out << "COFF\n";
2373
    }
2374
    else {
2375
        out << "OFF\n";
2376
    }
2377
    out << rPoints.size() << " " << rFacets.size() << " 0\n";
2378

2379
    // vertices
2380
    Base::Vector3f pt;
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;
2385
        }
2386
        else {
2387
            pt.Set(it->x, it->y, it->z);
2388
        }
2389

2390
        if (exportColor) {
2391
            App::Color c;
2392
            if (_material->binding == MeshIO::PER_VERTEX) {
2393
                c = _material->diffuseColor[index];
2394
            }
2395
            else {
2396
                c = _material->diffuseColor.front();
2397
            }
2398

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);
2403

2404
            out << pt.x << " " << pt.y << " " << pt.z << " " << r << " " << g << " " << b << " "
2405
                << a << '\n';
2406
        }
2407
        else {
2408
            out << pt.x << " " << pt.y << " " << pt.z << '\n';
2409
        }
2410
        seq.next(true);  // allow to cancel
2411
    }
2412

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]
2416
            << '\n';
2417
        seq.next(true);  // allow to cancel
2418
    }
2419

2420
    return true;
2421
}
2422

2423
bool MeshOutput::SaveBinaryPLY(std::ostream& out) const
2424
{
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()) {
2430
        return false;
2431
    }
2432
    bool saveVertexColor = (_material && _material->binding == MeshIO::PER_VERTEX
2433
                            && _material->diffuseColor.size() == rPoints.size());
2434
    out << "ply\n"
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";
2445
    }
2446
    out << "element face " << f_count << '\n'
2447
        << "property list uchar int vertex_index\n"
2448
        << "end_header\n";
2449

2450
    Base::OutputStream os(out);
2451
    os.setByteOrder(Base::Stream::LittleEndian);
2452

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;
2458
        }
2459
        else {
2460
            os << p.x << p.y << p.z;
2461
        }
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);
2467
            os << r << g << b;
2468
        }
2469
    }
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];
2477
        os << n;
2478
        os << f1 << f2 << f3;
2479
    }
2480

2481
    return true;
2482
}
2483

2484
bool MeshOutput::SaveAsciiPLY(std::ostream& out) const
2485
{
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()) {
2491
        return false;
2492
    }
2493

2494
    bool saveVertexColor = (_material && _material->binding == MeshIO::PER_VERTEX
2495
                            && _material->diffuseColor.size() == rPoints.size());
2496
    out << "ply\n"
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";
2507
    }
2508
    out << "element face " << f_count << '\n'
2509
        << "property list uchar int vertex_index\n"
2510
        << "end_header\n";
2511

2512
    out.precision(6);
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;
2520
            }
2521
            else {
2522
                out << p.x << " " << p.y << " " << p.z;
2523
            }
2524

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';
2530
        }
2531
    }
2532
    else {
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';
2538
            }
2539
            else {
2540
                out << p.x << " " << p.y << " " << p.z << '\n';
2541
            }
2542
        }
2543
    }
2544

2545
    unsigned int n = 3;
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';
2553
    }
2554

2555
    return true;
2556
}
2557

2558
bool MeshOutput::SaveMeshNode(std::ostream& rstrOut)
2559
{
2560
    const MeshPointArray& rPoints = _rclMesh.GetPoints();
2561
    const MeshFacetArray& rFacets = _rclMesh.GetFacets();
2562

2563
    if (!rstrOut || rstrOut.bad()) {
2564
        return false;
2565
    }
2566

2567
    // vertices
2568
    rstrOut << "[" << '\n';
2569
    if (this->apply_transform) {
2570
        Base::Vector3f pt;
2571
        for (const auto& it : rPoints) {
2572
            pt = this->_transform * it;
2573
            rstrOut << "v " << pt.x << " " << pt.y << " " << pt.z << '\n';
2574
        }
2575
    }
2576
    else {
2577
        for (const auto& it : rPoints) {
2578
            rstrOut << "v " << it.x << " " << it.y << " " << it.z << '\n';
2579
        }
2580
    }
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';
2585
    }
2586
    rstrOut << "]" << '\n';
2587

2588
    return true;
2589
}
2590

2591
/** Saves the mesh object into an XML file. */
2592
void MeshOutput::SaveXML(Base::Writer& writer) const
2593
{
2594
    const MeshPointArray& rPoints = _rclMesh.GetPoints();
2595
    const MeshFacetArray& rFacets = _rclMesh.GetFacets();
2596

2597
    //  writer << writer.ind() << "<Mesh>" << '\n';
2598

2599
    writer.incInd();
2600
    writer.Stream() << writer.ind() << "<Points Count=\"" << _rclMesh.CountPoints() << "\">"
2601
                    << '\n';
2602

2603
    writer.incInd();
2604
    if (this->apply_transform) {
2605
        Base::Vector3f pt;
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';
2612
        }
2613
    }
2614
    else {
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';
2620
        }
2621
    }
2622
    writer.decInd();
2623
    writer.Stream() << writer.ind() << "</Points>" << '\n';
2624

2625
    // write the faces
2626
    writer.Stream() << writer.ind() << "<Faces Count=\"" << _rclMesh.CountFacets() << "\">" << '\n';
2627

2628
    writer.incInd();
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';
2637
    }
2638
    writer.decInd();
2639
    writer.Stream() << writer.ind() << "</Faces>" << '\n';
2640

2641
    writer.Stream() << writer.ind() << "</Mesh>" << '\n';
2642
    writer.decInd();
2643
}
2644

2645
/** Saves the mesh object into a 3MF file. */
2646
bool MeshOutput::Save3MF(std::ostream& str) const
2647
{
2648
    Writer3MF writer(str);
2649
    writer.AddMesh(_rclMesh, _transform);
2650
    return writer.Save();
2651
}
2652

2653
/** Writes an IDTF file. */
2654
bool MeshOutput::SaveIDTF(std::ostream& str) const
2655
{
2656
    if (!str || str.bad() || (_rclMesh.CountFacets() == 0)) {
2657
        return false;
2658
    }
2659

2660
    const MeshPointArray& pts = _rclMesh.GetPoints();
2661
    const MeshFacetArray& fts = _rclMesh.GetFacets();
2662
    std::string resource = objectName;
2663
    if (resource.empty()) {
2664
        resource = "Resource";
2665
    }
2666

2667
    str.precision(6);
2668
    str.setf(std::ios::fixed | std::ios::showpoint);
2669

2670
    str << "FILE_FORMAT \"IDTF\"\n"
2671
        << "FORMAT_VERSION 100\n\n";
2672

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";
2689

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';
2714
    }
2715
    str << Base::tabs(3) << "}\n";
2716
    str << Base::tabs(3) << "MESH_FACE_NORMAL_LIST {\n";
2717
    int index = 0;
2718
    for (MeshFacetArray::_TConstIterator it = fts.begin(); it != fts.end(); ++it) {
2719
        str << Base::tabs(4) << index << " " << index + 1 << " " << index + 2 << '\n';
2720
        index += 3;
2721
    }
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";
2726
    }
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';
2731
    }
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';
2740
    }
2741

2742
    str << Base::tabs(3) << "}\n";
2743
    str << Base::tabs(2) << "}\n";
2744
    str << Base::tabs(1) << "}\n";
2745
    str << Base::tabs(0) << "}\n";
2746

2747
    return true;
2748
}
2749

2750
/** Writes an MGL file. */
2751
bool MeshOutput::SaveMGL(std::ostream& str) const
2752
{
2753
    /*
2754
    light on
2755
    list t 0 1 2 | 0 1 3 | 0 2 3 | 1 2 3
2756
    list xt 1 1 0 0
2757
    list yt -1 -1 1 0
2758
    list zt -1 -1 -1 1
2759
    triplot t xt yt zt 'b'
2760
    #triplot t xt yt zt '#k'
2761
    */
2762
    if (!str || str.bad() || (_rclMesh.CountFacets() == 0)) {
2763
        return false;
2764
    }
2765

2766
    const MeshPointArray& pts = _rclMesh.GetPoints();
2767
    const MeshFacetArray& fts = _rclMesh.GetFacets();
2768

2769
    str.precision(2);
2770
    str.setf(std::ios::fixed | std::ios::showpoint);
2771

2772
    str << "light on\n";
2773
    str << "list t ";
2774
    for (const auto& ft : fts) {
2775
        str << ft._aulPoints[0] << " " << ft._aulPoints[1] << " " << ft._aulPoints[2] << " | ";
2776
    }
2777
    str << std::endl;
2778

2779
    str << "list xt ";
2780
    for (const auto& pt : pts) {
2781
        str << pt.x << " ";
2782
    }
2783
    str << std::endl;
2784

2785
    str << "list yt ";
2786
    for (const auto& pt : pts) {
2787
        str << pt.y << " ";
2788
    }
2789
    str << std::endl;
2790

2791
    str << "list zt ";
2792
    for (const auto& pt : pts) {
2793
        str << pt.z << " ";
2794
    }
2795
    str << std::endl;
2796

2797
    str << "triplot t xt yt zt 'b'" << std::endl;
2798
    str << "#triplot t xt yt zt '#k'" << std::endl;
2799

2800
    return true;
2801
}
2802

2803
/** Writes an OpenInventor file. */
2804
bool MeshOutput::SaveInventor(std::ostream& rstrOut) const
2805
{
2806
    WriterInventor writer(_rclMesh, _material);
2807
    writer.SetTransform(_transform);
2808
    return writer.Save(rstrOut);
2809
}
2810

2811
/** Writes an X3D file. */
2812
bool MeshOutput::SaveX3D(std::ostream& out) const
2813
{
2814
    if (!out || out.bad() || (_rclMesh.CountFacets() == 0)) {
2815
        return false;
2816
    }
2817

2818
    // XML header info
2819
    out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
2820

2821
    return SaveX3DContent(out, false);
2822
}
2823

2824
/** Writes an X3D file. */
2825
bool MeshOutput::SaveX3DContent(std::ostream& out, bool exportViewpoints) const
2826
{
2827
    if (!out || out.bad() || (_rclMesh.CountFacets() == 0)) {
2828
        return false;
2829
    }
2830

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);
2836
    }
2837

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();
2842
        }
2843
    }
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());
2848

2849
    Base::SequencerLauncher seq("Saving...", _rclMesh.CountFacets() + 1);
2850
    out.precision(6);
2851
    out.setf(std::ios::fixed | std::ios::showpoint);
2852

2853
    // Header info
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";
2858
    out << "  <head>\n"
2859
        << "    <meta name=\"generator\" content=\"FreeCAD\"/>\n"
2860
        << "    <meta name=\"author\" content=\"\"/> \n"
2861
        << "    <meta name=\"company\" content=\"\"/>\n"
2862
        << "  </head>\n";
2863

2864
    // Beginning
2865
    out << "  <Scene>\n";
2866

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,
2872
                                float angle) {
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";
2878
        };
2879

2880
        Base::Vector3f cnt = bbox.GetCenter();
2881
        float dist = 1.2f * bbox.CalcDiagonalLength();
2882
        float dist3 = 0.577350f * dist;  // sqrt(1/3) * dist
2883

2884
        viewpoint("Iso",
2885
                  cnt,
2886
                  Base::Vector3f(cnt.x + dist3, cnt.y - dist3, cnt.z + dist3),
2887
                  Base::Vector3f(0.742906f, 0.307722f, 0.594473f),
2888
                  1.21712f);
2889
        viewpoint("Front",
2890
                  cnt,
2891
                  Base::Vector3f(cnt.x, cnt.y - dist, cnt.z),
2892
                  Base::Vector3f(1.0f, 0.0f, 0.0f),
2893
                  1.5707964f);
2894
        viewpoint("Back",
2895
                  cnt,
2896
                  Base::Vector3f(cnt.x, cnt.y + dist, cnt.z),
2897
                  Base::Vector3f(0.0f, 0.707106f, 0.707106f),
2898
                  3.141592f);
2899
        viewpoint("Right",
2900
                  cnt,
2901
                  Base::Vector3f(cnt.x + dist, cnt.y, cnt.z),
2902
                  Base::Vector3f(0.577350f, 0.577350f, 0.577350f),
2903
                  2.094395f);
2904
        viewpoint("Left",
2905
                  cnt,
2906
                  Base::Vector3f(cnt.x - dist, cnt.y, cnt.z),
2907
                  Base::Vector3f(-0.577350f, 0.577350f, 0.577350f),
2908
                  4.188790f);
2909
        viewpoint("Top",
2910
                  cnt,
2911
                  Base::Vector3f(cnt.x, cnt.y, cnt.z + dist),
2912
                  Base::Vector3f(0.0f, 0.0f, 1.0f),
2913
                  0.0f);
2914
        viewpoint("Bottom",
2915
                  cnt,
2916
                  Base::Vector3f(cnt.x, cnt.y, cnt.z - dist),
2917
                  Base::Vector3f(1.0f, 0.0f, 0.0f),
2918
                  3.141592f);
2919
    }
2920

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;
2926
        double angle {};
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";
2931
    }
2932
    else {
2933
        out << "    <Transform>\n";
2934
    }
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"
2940
           "        </Appearance>\n";
2941

2942
    out << "        <IndexedFaceSet solid=\"false\" ";
2943
    if (saveVertexColor) {
2944
        out << "colorPerVertex=\"true\" ";
2945
    }
2946
    else if (saveFaceColor) {
2947
        out << "colorPerVertex=\"false\" ";
2948
    }
2949

2950
    out << "coordIndex=\"";
2951
    for (const auto& ft : fts) {
2952
        out << ft._aulPoints[0] << " " << ft._aulPoints[1] << " " << ft._aulPoints[2] << " -1 ";
2953
    }
2954
    out << "\">\n";
2955

2956
    out << "          <Coordinate point=\"";
2957
    for (const auto& pt : pts) {
2958
        out << pt.x << " " << pt.y << " " << pt.z << ", ";
2959
    }
2960
    out << "\"/>\n";
2961

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 << ", ";
2967
        }
2968
        out << "\"/>\n";
2969
    }
2970

2971
    // End
2972
    out << "        </IndexedFaceSet>\n"
2973
        << "      </Shape>\n"
2974
        << "    </Transform>\n"
2975
        << "    <Background groundColor=\"0.7 0.7 0.7\" skyColor=\"0.7 0.7 0.7\" />\n"
2976
        << "    <NavigationInfo/>\n"
2977
        << "  </Scene>\n"
2978
        << "</X3D>\n";
2979

2980
    return true;
2981
}
2982

2983
/** Writes an X3DOM file. */
2984
bool MeshOutput::SaveX3DOM(std::ostream& out) const
2985
{
2986
    if (!out || out.bad() || (_rclMesh.CountFacets() == 0)) {
2987
        return false;
2988
    }
2989

2990
    // See:
2991
    // https://stackoverflow.com/questions/31976056/unable-to-color-faces-using-indexedfaceset-in-x3dom
2992
    //
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"
2997
        << "  <head>\n"
2998
        << "    <script type='text/javascript' src='http://www.x3dom.org/download/x3dom.js'> "
2999
           "</script>\n"
3000
        << "    <link rel='stylesheet' type='text/css' "
3001
           "href='http://www.x3dom.org/download/x3dom.css'></link>\n"
3002
        << "  </head>\n";
3003

3004
    auto onclick = [&out](const char* text) {
3005
        out << "  <button onclick=\"document.getElementById('" << text
3006
            << "').setAttribute('set_bind','true');\">" << text << "</button>\n";
3007
    };
3008

3009
    onclick("Iso");
3010
    onclick("Front");
3011
    onclick("Back");
3012
    onclick("Right");
3013
    onclick("Left");
3014
    onclick("Top");
3015
    onclick("Bottom");
3016

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;
3022
    }
3023

3024
    <button onclick="zoom(0.15);">Zoom out</button>
3025
#endif
3026

3027
    SaveX3DContent(out, true);
3028

3029
    out << "</html>\n";
3030

3031
    return true;
3032
}
3033

3034
/** Writes a Nastran file. */
3035
bool MeshOutput::SaveNastran(std::ostream& rstrOut) const
3036
{
3037
    if (!rstrOut || rstrOut.bad() || (_rclMesh.CountFacets() == 0)) {
3038
        return false;
3039
    }
3040

3041
    MeshPointIterator clPIter(_rclMesh);
3042
    clPIter.Transform(this->_transform);
3043
    MeshFacetIterator clTIter(_rclMesh);
3044
    int iIndx = 1;
3045

3046
    Base::SequencerLauncher seq("Saving...", _rclMesh.CountFacets() + 1);
3047

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;
3054

3055
        rstrOut << "GRID";
3056

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;
3061
        rstrOut << '\n';
3062

3063
        iIndx++;
3064
        seq.next();
3065
    }
3066

3067
    iIndx = 1;
3068
    for (clTIter.Init(); clTIter.More(); clTIter.Next()) {
3069
        rstrOut << "CTRIA3";
3070

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;
3076
        rstrOut << '\n';
3077

3078
        iIndx++;
3079
        seq.next();
3080
    }
3081

3082
    rstrOut << "ENDDATA";
3083

3084
    return true;
3085
}
3086

3087
/** Writes a Cadmould FE file. */
3088
bool MeshOutput::SaveCadmouldFE(std::ostream& /*rstrOut*/) const
3089
{
3090
    return false;
3091
}
3092

3093
/** Writes a Python module */
3094
bool MeshOutput::SavePython(std::ostream& str) const
3095
{
3096
    if (!str || str.bad() || (_rclMesh.CountFacets() == 0)) {
3097
        return false;
3098
    }
3099

3100
    MeshFacetIterator clIter(_rclMesh);
3101
    clIter.Transform(this->_transform);
3102
    str.precision(4);
3103
    str.setf(std::ios::fixed | std::ios::showpoint);
3104

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 << "],";
3110
        }
3111
        str << '\n';
3112
    }
3113

3114
    str << "]\n";
3115

3116
    return true;
3117
}
3118

3119
/** Writes a VRML file. */
3120
bool MeshOutput::SaveVRML(std::ostream& rstrOut) const
3121
{
3122
    if (!rstrOut || rstrOut.bad() || (_rclMesh.CountFacets() == 0)) {
3123
        return false;
3124
    }
3125

3126
    Base::BoundBox3f clBB = _rclMesh.GetBoundBox();
3127

3128
    Base::SequencerLauncher seq("Saving VRML file...",
3129
                                _rclMesh.CountPoints() + _rclMesh.CountFacets());
3130

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"
3136
            << "}\n\n";
3137

3138
    // Transform
3139
    rstrOut.precision(3);
3140
    rstrOut.setf(std::ios::fixed | std::ios::showpoint);
3141
    rstrOut << "Transform {\n"
3142
            << "  scale 1 1 1\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";
3147

3148
    rstrOut << "  children\n";
3149
    rstrOut << "    Shape { \n";
3150

3151
    // write appearance
3152
    rstrOut << "      appearance\n"
3153
            << "      Appearance {\n"
3154
            << "        material\n"
3155
            << "        Material {\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";
3160
        }
3161
        else {
3162
            rstrOut << "          diffuseColor 0.8 0.8 0.8\n";
3163
        }
3164
    }
3165
    else {
3166
        rstrOut << "          diffuseColor 0.8 0.8 0.8\n";
3167
    }
3168
    rstrOut << "        }\n      }\n";  // end write appearance
3169

3170

3171
    // write IndexedFaceSet
3172
    rstrOut << "      geometry\n"
3173
            << "      IndexedFaceSet {\n";
3174

3175
    rstrOut.precision(2);
3176
    rstrOut.setf(std::ios::fixed | std::ios::showpoint);
3177

3178
    // write coords
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)) {
3188
            rstrOut << ",\n";
3189
        }
3190
        else {
3191
            rstrOut << "\n";
3192
        }
3193

3194
        seq.next();
3195
    }
3196

3197
    rstrOut << "          ]\n        }\n";  // end write coord
3198

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();
3206
             ++pCIter) {
3207
            rstrOut << "          " << float(pCIter->r) << " " << float(pCIter->g) << " "
3208
                    << float(pCIter->b);
3209
            if (pCIter < (_material->diffuseColor.end() - 1)) {
3210
                rstrOut << ",\n";
3211
            }
3212
            else {
3213
                rstrOut << "\n";
3214
            }
3215
        }
3216

3217
        rstrOut << "      ]\n    }\n";
3218
        if (_material->binding == MeshIO::PER_VERTEX) {
3219
            rstrOut << "    colorPerVertex TRUE\n";
3220
        }
3221
        else {
3222
            rstrOut << "    colorPerVertex FALSE\n";
3223
        }
3224
    }
3225

3226
    // write face index
3227
    rstrOut << "        coordIndex [\n";
3228
    MeshFacetIterator pFIter(_rclMesh);
3229
    pFIter.Transform(this->_transform);
3230
    i = 0, k = _rclMesh.CountFacets();
3231

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)) {
3237
            rstrOut << ",\n";
3238
        }
3239
        else {
3240
            rstrOut << "\n";
3241
        }
3242

3243
        seq.next();
3244
    }
3245

3246
    rstrOut << "        ]\n      }\n";  // End IndexedFaceSet
3247
    rstrOut << "    }\n";               // End Shape
3248
    rstrOut << "}\n";                   // close children and Transform
3249

3250
    return true;
3251
}
3252

3253
// ----------------------------------------------------------------------------
3254

3255
MeshCleanup::MeshCleanup(MeshPointArray& p, MeshFacetArray& f)
3256
    : pointArray(p)
3257
    , facetArray(f)
3258
{}
3259

3260
void MeshCleanup::SetMaterial(Material* mat)
3261
{
3262
    materialArray = mat;
3263
}
3264

3265
void MeshCleanup::RemoveInvalids()
3266
{
3267
    // first mark all points as invalid
3268
    pointArray.SetFlag(MeshPoint::INVALID);
3269
    std::size_t numPoints = pointArray.size();
3270

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) {
3278
                it.SetInvalid();
3279
                break;
3280
            }
3281
        }
3282

3283
        // validate referenced points
3284
        if (it.IsValid()) {
3285
            pointArray[it._aulPoints[0]].ResetInvalid();
3286
            pointArray[it._aulPoints[1]].ResetInvalid();
3287
            pointArray[it._aulPoints[2]].ResetInvalid();
3288
        }
3289
    }
3290

3291
    // Remove the invalid items
3292
    RemoveInvalidFacets();
3293
    RemoveInvalidPoints();
3294
}
3295

3296
void MeshCleanup::RemoveInvalidFacets()
3297
{
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);
3302
        });
3303
    if (countInvalidFacets > 0) {
3304

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]);
3313
                }
3314
            }
3315

3316
            materialArray->diffuseColor.swap(colors);
3317
        }
3318

3319
        MeshFacetArray copy_facets(facetArray.size() - countInvalidFacets);
3320
        // copy all valid facets to the new array
3321
        std::remove_copy_if(facetArray.begin(),
3322
                            facetArray.end(),
3323
                            copy_facets.begin(),
3324
                            [flag](const MeshFacet& f) {
3325
                                return flag(f, MeshFacet::INVALID);
3326
                            });
3327
        facetArray.swap(copy_facets);
3328
    }
3329
}
3330

3331
void MeshCleanup::RemoveInvalidPoints()
3332
{
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);
3337
        });
3338
    if (countInvalidPoints > 0) {
3339
        // generate array of decrements
3340
        std::vector<PointIndex> decrements;
3341
        decrements.resize(pointArray.size());
3342
        PointIndex decr = 0;
3343

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) {
3348
            *decr_it = decr;
3349
            if (!p_it->IsValid()) {
3350
                decr++;
3351
            }
3352
        }
3353

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]];
3360
        }
3361

3362
        // delete point, number of valid points
3363
        std::size_t validPoints = pointArray.size() - countInvalidPoints;
3364

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]);
3373
                }
3374
            }
3375

3376
            materialArray->diffuseColor.swap(colors);
3377
        }
3378

3379
        MeshPointArray copy_points(validPoints);
3380
        // copy all valid facets to the new array
3381
        std::remove_copy_if(pointArray.begin(),
3382
                            pointArray.end(),
3383
                            copy_points.begin(),
3384
                            [flag](const MeshPoint& p) {
3385
                                return flag(p, MeshPoint::INVALID);
3386
                            });
3387
        pointArray.swap(copy_points);
3388
    }
3389
}
3390

3391
// ----------------------------------------------------------------------------
3392

3393
MeshPointFacetAdjacency::MeshPointFacetAdjacency(std::size_t p, MeshFacetArray& f)
3394
    : numPoints(p)
3395
    , facets(f)
3396
{
3397
    Build();
3398
}
3399

3400
void MeshPointFacetAdjacency::Build()
3401
{
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]]++;
3407
    }
3408

3409
    pointFacetAdjacency.resize(numPoints);
3410
    for (std::size_t i = 0; i < numPoints; i++) {
3411
        pointFacetAdjacency[i].reserve(numFacetAdjacency[i]);
3412
    }
3413

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);
3418
        }
3419
    }
3420
}
3421

3422
void MeshPointFacetAdjacency::SetFacetNeighbourhood()
3423
{
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];
3430

3431
            bool success = false;
3432
            const std::vector<std::size_t>& refFacets = pointFacetAdjacency[n1];
3433
            for (std::size_t it : refFacets) {
3434
                if (it != index) {
3435
                    MeshFacet& facet2 = facets[it];
3436
                    if (facet2.HasPoint(n2)) {
3437
                        facet1._aulNeighbours[i] = it;
3438
                        success = true;
3439
                        break;
3440
                    }
3441
                }
3442
            }
3443

3444
            if (!success) {
3445
                facet1._aulNeighbours[i] = FACET_INDEX_MAX;
3446
            }
3447
        }
3448
    }
3449
}
3450

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.