| | |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | #include <algorithm> |
| | #include <boost/algorithm/string/replace.hpp> |
| | #include <boost/core/ignore_unused.hpp> |
| | #include <vector> |
| |
|
| |
|
| | #include <App/Application.h> |
| | #include <App/ComplexGeoData.h> |
| | #include <App/ComplexGeoDataPy.h> |
| | #include <App/DocumentObject.h> |
| | #include <Base/Exception.h> |
| | #include <Base/FileInfo.h> |
| | #include <Base/Interpreter.h> |
| | #include <Base/Sequencer.h> |
| | #include <Base/Stream.h> |
| | #include <Base/Tools.h> |
| | #include "Core/Iterator.h" |
| | #include "Core/IO/Writer3MF.h" |
| | #include <zipios++/zipoutputstream.h> |
| |
|
| | #include "Exporter.h" |
| |
|
| |
|
| | using namespace Mesh; |
| | using namespace MeshCore; |
| |
|
| | static std::vector<std::string> expandSubObjectNames( |
| | const App::DocumentObject* obj, |
| | std::map<const App::DocumentObject*, std::vector<std::string>>& subObjectNameCache, |
| | int depth |
| | ) |
| | { |
| | if (!App::GetApplication().checkLinkDepth(depth)) { |
| | return {}; |
| | } |
| |
|
| | auto subs = obj->getSubObjects(); |
| | if (subs.empty()) { |
| | subs.emplace_back(""); |
| | return subs; |
| | } |
| |
|
| | std::vector<std::string> res; |
| | for (auto& sub : subs) { |
| | int vis = sub.empty() ? 1 : obj->isElementVisible(sub.c_str()); |
| | if (vis == 0) { |
| | continue; |
| | } |
| | auto sobj = obj->getSubObject(sub.c_str()); |
| | if (!sobj || (vis < 0 && !sobj->Visibility.getValue())) { |
| | continue; |
| | } |
| | auto linked = sobj->getLinkedObject(true); |
| | auto it = subObjectNameCache.find(linked); |
| | if (it == subObjectNameCache.end()) { |
| | it = subObjectNameCache |
| | .emplace(linked, expandSubObjectNames(linked, subObjectNameCache, depth + 1)) |
| | .first; |
| | } |
| | for (auto& ssub : it->second) { |
| | res.push_back(sub + ssub); |
| | } |
| | } |
| | return res; |
| | } |
| |
|
| | Exporter::Exporter() = default; |
| |
|
| | |
| | std::string Exporter::xmlEscape(const std::string& input) |
| | { |
| | std::string out(input); |
| | boost::replace_all(out, "&", "&"); |
| | boost::replace_all(out, "\"", """); |
| | boost::replace_all(out, "'", "'"); |
| | boost::replace_all(out, "<", "<"); |
| | boost::replace_all(out, ">", ">"); |
| | return out; |
| | } |
| |
|
| | int Exporter::addObject(App::DocumentObject* obj, float tol) |
| | { |
| | int count = 0; |
| | for (std::string& sub : expandSubObjectNames(obj, subObjectNameCache, 0)) { |
| | Base::Matrix4D matrix; |
| | auto sobj = obj->getSubObject(sub.c_str(), nullptr, &matrix); |
| | auto linked = sobj->getLinkedObject(true, &matrix, false); |
| | auto it = meshCache.find(linked); |
| | if (it == meshCache.end()) { |
| | if (linked->isDerivedFrom<Mesh::Feature>()) { |
| | it = meshCache.emplace(linked, static_cast<Mesh::Feature*>(linked)->Mesh.getValue()).first; |
| | it->second.setTransform(matrix); |
| | } |
| | else { |
| | Base::PyGILStateLocker lock; |
| | PyObject* pyobj = nullptr; |
| | linked->getSubObject("", &pyobj, nullptr, false); |
| | if (!pyobj) { |
| | continue; |
| | } |
| | if (PyObject_TypeCheck(pyobj, &Data::ComplexGeoDataPy::Type)) { |
| | std::vector<Base::Vector3d> aPoints; |
| | std::vector<Data::ComplexGeoData::Facet> aTopo; |
| | auto geoData = static_cast<Data::ComplexGeoDataPy*>(pyobj)->getComplexGeoDataPtr(); |
| | geoData->getFaces(aPoints, aTopo, tol); |
| | it = meshCache.emplace(linked, MeshObject()).first; |
| | it->second.setFacets(aTopo, aPoints); |
| | it->second.setTransform(matrix); |
| | } |
| | Py_DECREF(pyobj); |
| | } |
| | } |
| | else if (it->second.getTransform() != matrix) { |
| | it->second.setTransform(matrix); |
| | } |
| |
|
| | |
| | if (it != meshCache.end()) { |
| | if (addMesh(sobj->Label.getValue(), it->second)) { |
| | ++count; |
| | } |
| | } |
| | } |
| | return count; |
| | } |
| |
|
| | void Exporter::throwIfNoPermission(const std::string& filename) |
| | { |
| | |
| | Base::FileInfo fi(filename); |
| | Base::FileInfo di(fi.dirPath()); |
| | if ((fi.exists() && !fi.isWritable()) || !di.exists() || !di.isWritable()) { |
| | throw Base::FileException("No write permission for file", fi); |
| | } |
| | } |
| |
|
| | |
| |
|
| | MergeExporter::MergeExporter(std::string fileName, MeshIO::Format) |
| | : fName(std::move(fileName)) |
| | {} |
| |
|
| | MergeExporter::~MergeExporter() |
| | { |
| | write(); |
| | } |
| |
|
| | void MergeExporter::write() |
| | { |
| | |
| | if (mergingMesh.countSegments() > 1) { |
| | for (unsigned long i = 0; i < mergingMesh.countSegments(); ++i) { |
| | mergingMesh.getSegment(i).save(true); |
| | } |
| | } |
| |
|
| | try { |
| | mergingMesh.save(fName.c_str()); |
| | } |
| | catch (const Base::Exception& e) { |
| | std::cerr << "Saving mesh failed: " << e.what() << std::endl; |
| | } |
| | } |
| |
|
| | bool MergeExporter::addMesh(const char* name, const MeshObject& mesh) |
| | { |
| | auto kernel = mesh.getKernel(); |
| | kernel.Transform(mesh.getTransform()); |
| | auto countFacets(mergingMesh.countFacets()); |
| | if (countFacets == 0) { |
| | mergingMesh.setKernel(kernel); |
| | } |
| | else { |
| | mergingMesh.addMesh(kernel); |
| | } |
| |
|
| | |
| | unsigned long numSegm = mesh.countSegments(); |
| | unsigned long canSave = 0; |
| | for (unsigned long i = 0; i < numSegm; i++) { |
| | if (mesh.getSegment(i).isSaved()) { |
| | canSave++; |
| | } |
| | } |
| |
|
| | if (canSave > 0) { |
| | for (unsigned long i = 0; i < numSegm; i++) { |
| | const Segment& segm = mesh.getSegment(i); |
| | if (segm.isSaved()) { |
| | std::vector<FacetIndex> indices = segm.getIndices(); |
| | std::for_each(indices.begin(), indices.end(), [countFacets](FacetIndex& v) { |
| | v += countFacets; |
| | }); |
| | Segment new_segm(&mergingMesh, indices, true); |
| | new_segm.setName(segm.getName()); |
| | mergingMesh.addSegment(new_segm); |
| | } |
| | } |
| | } |
| | else { |
| | |
| | std::vector<FacetIndex> indices; |
| | indices.resize(mergingMesh.countFacets() - countFacets); |
| | std::generate(indices.begin(), indices.end(), Base::iotaGen<FacetIndex>(countFacets)); |
| | Segment segm(&mergingMesh, indices, true); |
| | segm.setName(name); |
| | mergingMesh.addSegment(segm); |
| | } |
| |
|
| | return true; |
| | } |
| |
|
| | |
| |
|
| | AbstractFormatExtensionPtr GuiExtension3MFProducer::create() const |
| | { |
| | return nullptr; |
| | } |
| |
|
| | void GuiExtension3MFProducer::initialize() |
| | { |
| | Base::PyGILStateLocker lock; |
| | PyObject* module = PyImport_ImportModule("MeshGui"); |
| | if (module) { |
| | Py_DECREF(module); |
| | } |
| | else { |
| | PyErr_Clear(); |
| | } |
| | } |
| |
|
| | void Extension3MFFactory::addProducer(Extension3MFProducer* ext) |
| | { |
| | producer.emplace_back(ext); |
| | } |
| |
|
| | void Extension3MFFactory::initialize() |
| | { |
| | std::vector<Extension3MFProducerPtr> ext = producer; |
| | for (const auto& it : ext) { |
| | it->initialize(); |
| | } |
| | } |
| |
|
| | std::vector<Extension3MFPtr> Extension3MFFactory::createExtensions() |
| | { |
| | std::vector<Extension3MFPtr> ext; |
| | for (const auto& it : producer) { |
| | Extension3MFPtr ptr = std::dynamic_pointer_cast<Extension3MF>(it->create()); |
| | if (ptr) { |
| | ext.push_back(ptr); |
| | } |
| | } |
| | return ext; |
| | } |
| |
|
| | std::vector<Extension3MFProducerPtr> Extension3MFFactory::producer; |
| |
|
| | class Exporter3MF::Private |
| | { |
| | public: |
| | explicit Private(const std::string& filename, std::vector<Extension3MFPtr> ext) |
| | : writer3mf(filename) |
| | , ext(std::move(ext)) |
| | {} |
| | MeshCore::Writer3MF writer3mf; |
| | std::vector<Extension3MFPtr> ext; |
| | }; |
| |
|
| | Exporter3MF::Exporter3MF(std::string fileName, const std::vector<Extension3MFPtr>& ext) |
| | { |
| | throwIfNoPermission(fileName); |
| | d = std::make_unique<Private>(fileName, ext); |
| | } |
| |
|
| | Exporter3MF::~Exporter3MF() |
| | { |
| | write(); |
| | } |
| |
|
| | bool Exporter3MF::addMesh(const char* name, const MeshObject& mesh) |
| | { |
| | boost::ignore_unused(name); |
| | bool ok = d->writer3mf.AddMesh(mesh.getKernel(), mesh.getTransform()); |
| | if (ok) { |
| | for (const auto& it : d->ext) { |
| | d->writer3mf.AddResource(it->addMesh(mesh)); |
| | } |
| | } |
| |
|
| | return ok; |
| | } |
| |
|
| | void Exporter3MF::setForceModel(bool model) |
| | { |
| | d->writer3mf.SetForceModel(model); |
| | } |
| |
|
| | void Exporter3MF::write() |
| | { |
| | d->writer3mf.Save(); |
| | } |
| |
|
| | |
| |
|
| | ExporterAMF::ExporterAMF(std::string fileName, const std::map<std::string, std::string>& meta, bool compress) |
| | { |
| | |
| | throwIfNoPermission(fileName); |
| |
|
| | Base::FileInfo fi(fileName); |
| | if (compress) { |
| | auto* zipStreamPtr(new zipios::ZipOutputStream(fi.filePath())); |
| |
|
| | |
| | |
| | |
| | zipStreamPtr->putNextEntry(zipios::ZipCDirEntry(fi.fileName())); |
| |
|
| | |
| | outputStreamPtr = zipStreamPtr; |
| | } |
| | else { |
| | outputStreamPtr = new Base::ofstream(fi, std::ios::out | std::ios::binary); |
| | } |
| |
|
| | if (outputStreamPtr) { |
| | *outputStreamPtr << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" |
| | << "<amf unit=\"millimeter\">\n"; |
| | for (auto const& metaEntry : meta) { |
| | *outputStreamPtr << "\t<metadata type=\"" << metaEntry.first << "\">" |
| | << metaEntry.second << "</metadata>\n"; |
| | } |
| | } |
| | } |
| |
|
| | ExporterAMF::~ExporterAMF() |
| | { |
| | write(); |
| | } |
| |
|
| | void ExporterAMF::write() |
| | { |
| | if (outputStreamPtr) { |
| | *outputStreamPtr << "\t<constellation id=\"0\">\n"; |
| | for (auto objId(0); objId < nextObjectIndex; ++objId) { |
| | *outputStreamPtr << "\t\t<instance objectid=\"" << objId << "\">\n" |
| | << "\t\t\t<deltax>0</deltax>\n" |
| | << "\t\t\t<deltay>0</deltay>\n" |
| | << "\t\t\t<rz>0</rz>\n" |
| | << "\t\t</instance>\n"; |
| | } |
| | *outputStreamPtr << "\t</constellation>\n" |
| | << "</amf>\n"; |
| | delete outputStreamPtr; |
| | } |
| | } |
| |
|
| | class ExporterAMF::VertLess |
| | { |
| | public: |
| | bool operator()(const Base::Vector3f& a, const Base::Vector3f& b) const |
| | { |
| | if (a.x == b.x) { |
| | if (a.y == b.y) { |
| | if (a.z == b.z) { |
| | return false; |
| | } |
| |
|
| | return a.z < b.z; |
| | } |
| |
|
| | return a.y < b.y; |
| | } |
| |
|
| | return a.x < b.x; |
| | } |
| | }; |
| |
|
| | bool ExporterAMF::addMesh(const char* name, const MeshObject& mesh) |
| | { |
| | auto kernel = mesh.getKernel(); |
| | kernel.Transform(mesh.getTransform()); |
| |
|
| | if (!outputStreamPtr || outputStreamPtr->bad()) { |
| | return false; |
| | } |
| |
|
| | auto numFacets(kernel.CountFacets()); |
| |
|
| | if (numFacets == 0) { |
| | return false; |
| | } |
| |
|
| | MeshCore::MeshFacetIterator clIter(kernel), clEnd(kernel); |
| |
|
| | Base::SequencerLauncher seq("Saving...", 2 * numFacets + 1); |
| |
|
| | *outputStreamPtr << "\t<object id=\"" << nextObjectIndex << "\">\n"; |
| | *outputStreamPtr << "\t\t<metadata type=\"name\">" << xmlEscape(name) << "</metadata>\n"; |
| | *outputStreamPtr << "\t\t<mesh>\n" |
| | << "\t\t\t<vertices>\n"; |
| |
|
| | const MeshCore::MeshGeomFacet* facet {}; |
| |
|
| | |
| | |
| | |
| | |
| | std::map<Base::Vector3f, unsigned long, ExporterAMF::VertLess> vertices; |
| | auto vertItr(vertices.begin()); |
| | auto vertexCount(0UL); |
| |
|
| | |
| | std::vector<unsigned long> facets; |
| |
|
| | |
| | for (clIter.Begin(), clEnd.End(); clIter < clEnd; ++clIter) { |
| | facet = &(*clIter); |
| |
|
| | |
| | for (auto pnt : facet->_aclPoints) { |
| | vertItr = vertices.find(pnt); |
| |
|
| | if (vertItr == vertices.end()) { |
| | facets.push_back(vertexCount); |
| |
|
| | vertices[pnt] = vertexCount++; |
| |
|
| | |
| | *outputStreamPtr << "\t\t\t\t<vertex>\n" |
| | << "\t\t\t\t\t<coordinates>\n"; |
| | for (auto j(0); j < 3; ++j) { |
| | char axis('x' + j); |
| | *outputStreamPtr << "\t\t\t\t\t\t<" << axis << '>' << pnt[j] << "</" << axis |
| | << ">\n"; |
| | } |
| | *outputStreamPtr << "\t\t\t\t\t</coordinates>\n" |
| | << "\t\t\t\t</vertex>\n"; |
| | } |
| | else { |
| | facets.push_back(vertItr->second); |
| | } |
| | } |
| |
|
| | seq.next(true); |
| | } |
| |
|
| | *outputStreamPtr << "\t\t\t</vertices>\n" |
| | << "\t\t\t<volume>\n"; |
| |
|
| | |
| | |
| | for (auto triItr(facets.begin()); triItr != facets.end();) { |
| | *outputStreamPtr << "\t\t\t\t<triangle>\n"; |
| | for (auto i(1); i < 4; ++i) { |
| | *outputStreamPtr << "\t\t\t\t\t<v" << i << '>' << *(triItr++) << "</v" << i << ">\n"; |
| | } |
| | *outputStreamPtr << "\t\t\t\t</triangle>\n"; |
| | seq.next(true); |
| | } |
| |
|
| | *outputStreamPtr << "\t\t\t</volume>\n" |
| | << "\t\t</mesh>\n" |
| | << "\t</object>\n"; |
| |
|
| | ++nextObjectIndex; |
| | return true; |
| | } |
| |
|