FreeCAD

Форк
0
/
Exporter.cpp 
499 строк · 15.5 Кб
1
/***************************************************************************
2
 *   Copyright (c) 2017 Ian Rees <ian.rees@gmail.com>                      *
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
#ifndef _PreComp_
25
#include <algorithm>
26
#include <boost/algorithm/string/replace.hpp>
27
#include <boost/core/ignore_unused.hpp>
28
#include <vector>
29
#endif
30

31
#include <App/Application.h>
32
#include <App/ComplexGeoData.h>
33
#include <App/ComplexGeoDataPy.h>
34
#include <App/DocumentObject.h>
35
#include <Base/Exception.h>
36
#include <Base/FileInfo.h>
37
#include <Base/Interpreter.h>
38
#include <Base/Sequencer.h>
39
#include <Base/Stream.h>
40
#include <Base/Tools.h>
41
#include "Core/Iterator.h"
42
#include "Core/IO/Writer3MF.h"
43
#include <zipios++/zipoutputstream.h>
44

45
#include "Exporter.h"
46

47

48
using namespace Mesh;
49
using namespace MeshCore;
50

51
static std::vector<std::string> expandSubObjectNames(
52
    const App::DocumentObject* obj,
53
    std::map<const App::DocumentObject*, std::vector<std::string>>& subObjectNameCache,
54
    int depth)
55
{
56
    if (!App::GetApplication().checkLinkDepth(depth)) {
57
        return {};
58
    }
59

60
    auto subs = obj->getSubObjects();
61
    if (subs.empty()) {
62
        subs.emplace_back("");
63
        return subs;
64
    }
65

66
    std::vector<std::string> res;
67
    for (auto& sub : subs) {
68
        int vis = sub.empty() ? 1 : obj->isElementVisible(sub.c_str());
69
        if (vis == 0) {
70
            continue;
71
        }
72
        auto sobj = obj->getSubObject(sub.c_str());
73
        if (!sobj || (vis < 0 && !sobj->Visibility.getValue())) {
74
            continue;
75
        }
76
        auto linked = sobj->getLinkedObject(true);
77
        auto it = subObjectNameCache.find(linked);
78
        if (it == subObjectNameCache.end()) {
79
            it = subObjectNameCache
80
                     .emplace(linked, expandSubObjectNames(linked, subObjectNameCache, depth + 1))
81
                     .first;
82
        }
83
        for (auto& ssub : it->second) {
84
            res.push_back(sub + ssub);
85
        }
86
    }
87
    return res;
88
}
89

90
Exporter::Exporter() = default;
91

92
// static
93
std::string Exporter::xmlEscape(const std::string& input)
94
{
95
    std::string out(input);
96
    boost::replace_all(out, "&", "&amp;");
97
    boost::replace_all(out, "\"", "&quot;");
98
    boost::replace_all(out, "'", "&apos;");
99
    boost::replace_all(out, "<", "&lt;");
100
    boost::replace_all(out, ">", "&gt;");
101
    return out;
102
}
103

104
int Exporter::addObject(App::DocumentObject* obj, float tol)
105
{
106
    int count = 0;
107
    for (std::string& sub : expandSubObjectNames(obj, subObjectNameCache, 0)) {
108
        Base::Matrix4D matrix;
109
        auto sobj = obj->getSubObject(sub.c_str(), nullptr, &matrix);
110
        auto linked = sobj->getLinkedObject(true, &matrix, false);
111
        auto it = meshCache.find(linked);
112
        if (it == meshCache.end()) {
113
            if (linked->isDerivedFrom(Mesh::Feature::getClassTypeId())) {
114
                it = meshCache.emplace(linked, static_cast<Mesh::Feature*>(linked)->Mesh.getValue())
115
                         .first;
116
                it->second.setTransform(matrix);
117
            }
118
            else {
119
                Base::PyGILStateLocker lock;
120
                PyObject* pyobj = nullptr;
121
                linked->getSubObject("", &pyobj, nullptr, false);
122
                if (!pyobj) {
123
                    continue;
124
                }
125
                if (PyObject_TypeCheck(pyobj, &Data::ComplexGeoDataPy::Type)) {
126
                    std::vector<Base::Vector3d> aPoints;
127
                    std::vector<Data::ComplexGeoData::Facet> aTopo;
128
                    auto geoData =
129
                        static_cast<Data::ComplexGeoDataPy*>(pyobj)->getComplexGeoDataPtr();
130
                    geoData->getFaces(aPoints, aTopo, tol);
131
                    it = meshCache.emplace(linked, MeshObject()).first;
132
                    it->second.setFacets(aTopo, aPoints);
133
                    it->second.setTransform(matrix);
134
                }
135
                Py_DECREF(pyobj);
136
            }
137
        }
138
        else if (it->second.getTransform() != matrix) {
139
            it->second.setTransform(matrix);
140
        }
141

142
        // Add a new mesh
143
        if (it != meshCache.end()) {
144
            if (addMesh(sobj->Label.getValue(), it->second)) {
145
                ++count;
146
            }
147
        }
148
    }
149
    return count;
150
}
151

152
void Exporter::throwIfNoPermission(const std::string& filename)
153
{
154
    // ask for write permission
155
    Base::FileInfo fi(filename);
156
    Base::FileInfo di(fi.dirPath());
157
    if ((fi.exists() && !fi.isWritable()) || !di.exists() || !di.isWritable()) {
158
        throw Base::FileException("No write permission for file", fi);
159
    }
160
}
161

162
// ----------------------------------------------------------------------------
163

164
MergeExporter::MergeExporter(std::string fileName, MeshIO::Format)
165
    : fName(std::move(fileName))
166
{}
167

168
MergeExporter::~MergeExporter()
169
{
170
    write();
171
}
172

173
void MergeExporter::write()
174
{
175
    // if we have more than one segment set the 'save' flag
176
    if (mergingMesh.countSegments() > 1) {
177
        for (unsigned long i = 0; i < mergingMesh.countSegments(); ++i) {
178
            mergingMesh.getSegment(i).save(true);
179
        }
180
    }
181

182
    try {
183
        mergingMesh.save(fName.c_str());
184
    }
185
    catch (const Base::Exception& e) {
186
        std::cerr << "Saving mesh failed: " << e.what() << std::endl;
187
    }
188
}
189

190
bool MergeExporter::addMesh(const char* name, const MeshObject& mesh)
191
{
192
    auto kernel = mesh.getKernel();
193
    kernel.Transform(mesh.getTransform());
194
    auto countFacets(mergingMesh.countFacets());
195
    if (countFacets == 0) {
196
        mergingMesh.setKernel(kernel);
197
    }
198
    else {
199
        mergingMesh.addMesh(kernel);
200
    }
201

202
    // if the mesh already has persistent segments then use them instead
203
    unsigned long numSegm = mesh.countSegments();
204
    unsigned long canSave = 0;
205
    for (unsigned long i = 0; i < numSegm; i++) {
206
        if (mesh.getSegment(i).isSaved()) {
207
            canSave++;
208
        }
209
    }
210

211
    if (canSave > 0) {
212
        for (unsigned long i = 0; i < numSegm; i++) {
213
            const Segment& segm = mesh.getSegment(i);
214
            if (segm.isSaved()) {
215
                std::vector<FacetIndex> indices = segm.getIndices();
216
                std::for_each(indices.begin(), indices.end(), [countFacets](FacetIndex& v) {
217
                    v += countFacets;
218
                });
219
                Segment new_segm(&mergingMesh, indices, true);
220
                new_segm.setName(segm.getName());
221
                mergingMesh.addSegment(new_segm);
222
            }
223
        }
224
    }
225
    else {
226
        // now create a segment for the added mesh
227
        std::vector<FacetIndex> indices;
228
        indices.resize(mergingMesh.countFacets() - countFacets);
229
        std::generate(indices.begin(), indices.end(), Base::iotaGen<FacetIndex>(countFacets));
230
        Segment segm(&mergingMesh, indices, true);
231
        segm.setName(name);
232
        mergingMesh.addSegment(segm);
233
    }
234

235
    return true;
236
}
237

238
// ----------------------------------------------------------------------------
239

240
AbstractFormatExtensionPtr GuiExtension3MFProducer::create() const
241
{
242
    return nullptr;
243
}
244

245
void GuiExtension3MFProducer::initialize()
246
{
247
    Base::PyGILStateLocker lock;
248
    PyObject* module = PyImport_ImportModule("MeshGui");
249
    if (module) {
250
        Py_DECREF(module);
251
    }
252
    else {
253
        PyErr_Clear();
254
    }
255
}
256

257
void Extension3MFFactory::addProducer(Extension3MFProducer* ext)
258
{
259
    producer.emplace_back(ext);
260
}
261

262
void Extension3MFFactory::initialize()
263
{
264
    std::vector<Extension3MFProducerPtr> ext = producer;
265
    for (const auto& it : ext) {
266
        it->initialize();
267
    }
268
}
269

270
std::vector<Extension3MFPtr> Extension3MFFactory::createExtensions()
271
{
272
    std::vector<Extension3MFPtr> ext;
273
    for (const auto& it : producer) {
274
        Extension3MFPtr ptr = std::dynamic_pointer_cast<Extension3MF>(it->create());
275
        if (ptr) {
276
            ext.push_back(ptr);
277
        }
278
    }
279
    return ext;
280
}
281

282
std::vector<Extension3MFProducerPtr> Extension3MFFactory::producer;
283

284
class Exporter3MF::Private
285
{
286
public:
287
    explicit Private(const std::string& filename, std::vector<Extension3MFPtr> ext)
288
        : writer3mf(filename)
289
        , ext(std::move(ext))
290
    {}
291
    MeshCore::Writer3MF writer3mf;
292
    std::vector<Extension3MFPtr> ext;
293
};
294

295
Exporter3MF::Exporter3MF(std::string fileName, const std::vector<Extension3MFPtr>& ext)
296
{
297
    throwIfNoPermission(fileName);
298
    d = std::make_unique<Private>(fileName, ext);
299
}
300

301
Exporter3MF::~Exporter3MF()
302
{
303
    write();
304
}
305

306
bool Exporter3MF::addMesh(const char* name, const MeshObject& mesh)
307
{
308
    boost::ignore_unused(name);
309
    bool ok = d->writer3mf.AddMesh(mesh.getKernel(), mesh.getTransform());
310
    if (ok) {
311
        for (const auto& it : d->ext) {
312
            d->writer3mf.AddResource(it->addMesh(mesh));
313
        }
314
    }
315

316
    return ok;
317
}
318

319
void Exporter3MF::setForceModel(bool model)
320
{
321
    d->writer3mf.SetForceModel(model);
322
}
323

324
void Exporter3MF::write()
325
{
326
    d->writer3mf.Save();
327
}
328

329
// ----------------------------------------------------------------------------
330

331
ExporterAMF::ExporterAMF(std::string fileName,
332
                         const std::map<std::string, std::string>& meta,
333
                         bool compress)
334
{
335
    // ask for write permission
336
    throwIfNoPermission(fileName);
337

338
    Base::FileInfo fi(fileName);
339
    if (compress) {
340
        auto* zipStreamPtr(new zipios::ZipOutputStream(fi.filePath()));
341

342
        // ISO 52915 specifies that compressed AMF files are zip-compressed and
343
        // must contain the AMF XML in an entry with the same name as the
344
        // compressed file.  It's OK to have other files in the zip too.
345
        zipStreamPtr->putNextEntry(zipios::ZipCDirEntry(fi.fileName()));
346

347
        // Default compression seems to work fine.
348
        outputStreamPtr = zipStreamPtr;
349
    }
350
    else {
351
        outputStreamPtr = new Base::ofstream(fi, std::ios::out | std::ios::binary);
352
    }
353

354
    if (outputStreamPtr) {
355
        *outputStreamPtr << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
356
                         << "<amf unit=\"millimeter\">\n";
357
        for (auto const& metaEntry : meta) {
358
            *outputStreamPtr << "\t<metadata type=\"" << metaEntry.first << "\">"
359
                             << metaEntry.second << "</metadata>\n";
360
        }
361
    }
362
}
363

364
ExporterAMF::~ExporterAMF()
365
{
366
    write();
367
}
368

369
void ExporterAMF::write()
370
{
371
    if (outputStreamPtr) {
372
        *outputStreamPtr << "\t<constellation id=\"0\">\n";
373
        for (auto objId(0); objId < nextObjectIndex; ++objId) {
374
            *outputStreamPtr << "\t\t<instance objectid=\"" << objId << "\">\n"
375
                             << "\t\t\t<deltax>0</deltax>\n"
376
                             << "\t\t\t<deltay>0</deltay>\n"
377
                             << "\t\t\t<rz>0</rz>\n"
378
                             << "\t\t</instance>\n";
379
        }
380
        *outputStreamPtr << "\t</constellation>\n"
381
                         << "</amf>\n";
382
        delete outputStreamPtr;
383
    }
384
}
385

386
class ExporterAMF::VertLess
387
{
388
public:
389
    bool operator()(const Base::Vector3f& a, const Base::Vector3f& b) const
390
    {
391
        if (a.x == b.x) {
392
            if (a.y == b.y) {
393
                if (a.z == b.z) {
394
                    return false;
395
                }
396
                else {
397
                    return a.z < b.z;
398
                }
399
            }
400
            else {
401
                return a.y < b.y;
402
            }
403
        }
404
        else {
405
            return a.x < b.x;
406
        }
407
    }
408
};
409

410
bool ExporterAMF::addMesh(const char* name, const MeshObject& mesh)
411
{
412
    auto kernel = mesh.getKernel();
413
    kernel.Transform(mesh.getTransform());
414

415
    if (!outputStreamPtr || outputStreamPtr->bad()) {
416
        return false;
417
    }
418

419
    auto numFacets(kernel.CountFacets());
420

421
    if (numFacets == 0) {
422
        return false;
423
    }
424

425
    MeshCore::MeshFacetIterator clIter(kernel), clEnd(kernel);
426

427
    Base::SequencerLauncher seq("Saving...", 2 * numFacets + 1);
428

429
    *outputStreamPtr << "\t<object id=\"" << nextObjectIndex << "\">\n";
430
    *outputStreamPtr << "\t\t<metadata type=\"name\">" << xmlEscape(name) << "</metadata>\n";
431
    *outputStreamPtr << "\t\t<mesh>\n"
432
                     << "\t\t\t<vertices>\n";
433

434
    const MeshCore::MeshGeomFacet* facet {};
435

436
    // Iterate through all facets of the mesh, and construct a:
437
    //   * Cache (map) of used vertices, outputting each new unique vertex to
438
    //     the output stream as we find it
439
    //   * Vector of the vertices, referred to by the indices from 1
440
    std::map<Base::Vector3f, unsigned long, ExporterAMF::VertLess> vertices;
441
    auto vertItr(vertices.begin());
442
    auto vertexCount(0UL);
443

444
    // {facet1A, facet1B, facet1C, facet2A, ..., facetNC}
445
    std::vector<unsigned long> facets;
446

447
    // For each facet in mesh
448
    for (clIter.Begin(), clEnd.End(); clIter < clEnd; ++clIter) {
449
        facet = &(*clIter);
450

451
        // For each vertex in facet
452
        for (auto pnt : facet->_aclPoints) {
453
            vertItr = vertices.find(pnt);
454

455
            if (vertItr == vertices.end()) {
456
                facets.push_back(vertexCount);
457

458
                vertices[pnt] = vertexCount++;
459

460
                // Output facet
461
                *outputStreamPtr << "\t\t\t\t<vertex>\n"
462
                                 << "\t\t\t\t\t<coordinates>\n";
463
                for (auto j(0); j < 3; ++j) {
464
                    char axis('x' + j);
465
                    *outputStreamPtr << "\t\t\t\t\t\t<" << axis << '>' << pnt[j] << "</" << axis
466
                                     << ">\n";
467
                }
468
                *outputStreamPtr << "\t\t\t\t\t</coordinates>\n"
469
                                 << "\t\t\t\t</vertex>\n";
470
            }
471
            else {
472
                facets.push_back(vertItr->second);
473
            }
474
        }
475

476
        seq.next(true);  // allow to cancel
477
    }
478

479
    *outputStreamPtr << "\t\t\t</vertices>\n"
480
                     << "\t\t\t<volume>\n";
481

482
    // Now that we've output all the vertices, we can
483
    // output the facets that refer to them!
484
    for (auto triItr(facets.begin()); triItr != facets.end();) {
485
        *outputStreamPtr << "\t\t\t\t<triangle>\n";
486
        for (auto i(1); i < 4; ++i) {
487
            *outputStreamPtr << "\t\t\t\t\t<v" << i << '>' << *(triItr++) << "</v" << i << ">\n";
488
        }
489
        *outputStreamPtr << "\t\t\t\t</triangle>\n";
490
        seq.next(true);  // allow to cancel
491
    }
492

493
    *outputStreamPtr << "\t\t\t</volume>\n"
494
                     << "\t\t</mesh>\n"
495
                     << "\t</object>\n";
496

497
    ++nextObjectIndex;
498
    return true;
499
}
500

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

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

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

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