From 999b7238f5e2540499b05d59a34763ca3468645d Mon Sep 17 00:00:00 2001 From: Jan Orend <56254096+3dJan@users.noreply.github.com> Date: Thu, 10 Oct 2024 14:14:13 +0200 Subject: [PATCH] Write resources in topological order --- Include/Common/Graph/DirectedGraph.h | 35 +++-- Include/Common/Graph/IDirectedGraph.h | 30 +++-- Include/Model/Classes/NMR_Model.h | 2 + .../Model/Classes/NMR_ModelComponentsObject.h | 2 + .../Model/Classes/NMR_ModelImplicitFunction.h | 2 + .../Model/Classes/NMR_ModelLevelSetObject.h | 2 + Include/Model/Classes/NMR_ModelMeshObject.h | 2 + Include/Model/Classes/NMR_ModelObject.h | 1 - Include/Model/Classes/NMR_ModelResource.h | 10 +- Include/Model/Classes/NMR_ModelVolumeData.h | 4 + .../v100/NMR_ResourceDependencySorter.h | 65 ++++++++++ Source/Common/Graph/DirectedGraph.cpp | 36 +++--- Source/Model/Classes/NMR_Model.cpp | 35 +++-- .../Classes/NMR_ModelComponentsObject.cpp | 24 +++- .../Classes/NMR_ModelImplicitFunction.cpp | 32 ++++- .../Model/Classes/NMR_ModelLevelSetObject.cpp | 19 +++ Source/Model/Classes/NMR_ModelMeshObject.cpp | 12 ++ Source/Model/Classes/NMR_ModelObject.cpp | 2 - Source/Model/Classes/NMR_ModelResource.cpp | 9 +- Source/Model/Classes/NMR_ModelVolumeData.cpp | 56 +++++++- .../v100/NMR_ModelWriterNode100_Model.cpp | 24 +++- .../v100/NMR_ResourceDependencySorter.cpp | 120 ++++++++++++++++++ 22 files changed, 458 insertions(+), 66 deletions(-) create mode 100644 Include/Model/Writer/v100/NMR_ResourceDependencySorter.h create mode 100644 Source/Model/Writer/v100/NMR_ResourceDependencySorter.cpp diff --git a/Include/Common/Graph/DirectedGraph.h b/Include/Common/Graph/DirectedGraph.h index 8a317b46d..a6a6c0ee1 100644 --- a/Include/Common/Graph/DirectedGraph.h +++ b/Include/Common/Graph/DirectedGraph.h @@ -4,8 +4,9 @@ Copyright (C) 2023 3MF Consortium All rights reserved. -Redistribution and use in source and binary forms, with or without modification,276529 -are permitted provided that the following conditions are met: +Redistribution and use in source and binary forms, with or without +modification,276529 are permitted provided that the following conditions are +met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. @@ -33,26 +34,34 @@ namespace NMR::common::graph { class DirectedGraph : public IDirectedGraph { - public: + public: explicit DirectedGraph(std::size_t size); - void addDependency(Identifier id, Identifier idOfDependency) override; + void removeDependency(Identifier id, + Identifier idOfDependency) override; + [[nodiscard]] auto isDirectlyDependingOn( + Identifier id, + Identifier dependencyInQuestion) const -> bool override; - void removeDependency(Identifier id, Identifier idOfDependency) override; - [[nodiscard]] bool isDirectlyDependingOn(Identifier id, - Identifier dependencyInQuestion) const override; - - [[nodiscard]] std::size_t getSize() const override; + [[nodiscard]] auto getSize() const -> std::size_t override; void removeVertex(Identifier id) override; - [[nodiscard]] DependencySet const & getVertices() const override; + [[nodiscard]] auto getVertices() const -> const DependencySet& override; void addVertex(Identifier id) override; - private: + private: std::size_t m_size; std::vector m_graphData; - DependencySet m_vertices; + DependencySet m_vertices; // Possible performance improvement: We could + // try out a std::set + + using PredecessorList = std::vector; + std::vector m_predecessors; + + // Inherited via IDirectedGraph + [[nodiscard]] auto hasPredecessors(Identifier id) const + -> bool override; }; -} +} // namespace NMR::common::graph diff --git a/Include/Common/Graph/IDirectedGraph.h b/Include/Common/Graph/IDirectedGraph.h index e2d08aed1..aa109ff36 100644 --- a/Include/Common/Graph/IDirectedGraph.h +++ b/Include/Common/Graph/IDirectedGraph.h @@ -4,8 +4,9 @@ Copyright (C) 2023 3MF Consortium All rights reserved. -Redistribution and use in source and binary forms, with or without modification,276529 -are permitted provided that the following conditions are met: +Redistribution and use in source and binary forms, with or without +modification,276529 are permitted provided that the following conditions are +met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. @@ -39,18 +40,23 @@ namespace NMR::common::graph class IDirectedGraph { - public: - explicit IDirectedGraph(std::size_t const /*unused*/){}; + public: + explicit IDirectedGraph(std::size_t const /*unused*/) {}; virtual ~IDirectedGraph() = default; - virtual void addDependency(Identifier id, Identifier idOfDependency) = 0; - virtual void removeDependency(Identifier id, Identifier idOfDependency) = 0; - [[nodiscard]] virtual bool isDirectlyDependingOn(Identifier id, - Identifier dependencyInQuestion) const = 0; - - [[nodiscard]] virtual std::size_t getSize() const = 0; + virtual void addDependency(Identifier id, + Identifier idOfDependency) = 0; + virtual void removeDependency(Identifier id, + Identifier idOfDependency) = 0; + [[nodiscard]] virtual auto isDirectlyDependingOn( + Identifier id, Identifier dependencyInQuestion) const -> bool = 0; + + [[nodiscard]] virtual auto getSize() const -> std::size_t = 0; virtual void removeVertex(Identifier id) = 0; virtual void addVertex(Identifier id) = 0; + [[nodiscard]] virtual auto hasPredecessors(Identifier id) const + -> bool = 0; - [[nodiscard]] virtual DependencySet const & getVertices() const = 0; + [[nodiscard]] virtual auto getVertices() const + -> const DependencySet& = 0; }; -} // namespace gladius::nodes::graph +} // namespace NMR::common::graph diff --git a/Include/Model/Classes/NMR_Model.h b/Include/Model/Classes/NMR_Model.h index 9580a6544..4d879078f 100644 --- a/Include/Model/Classes/NMR_Model.h +++ b/Include/Model/Classes/NMR_Model.h @@ -375,6 +375,8 @@ namespace NMR { nfBool hasCryptoRandCallbak() const; nfUint64 generateRandomBytes(nfByte *, nfUint64); + [[nodiscard]] ModelResourceID getMaxModelResourceID(); + }; typedef std::shared_ptr PModel; diff --git a/Include/Model/Classes/NMR_ModelComponentsObject.h b/Include/Model/Classes/NMR_ModelComponentsObject.h index 8b4d1f0c7..86a2882bf 100644 --- a/Include/Model/Classes/NMR_ModelComponentsObject.h +++ b/Include/Model/Classes/NMR_ModelComponentsObject.h @@ -70,6 +70,8 @@ namespace NMR { void calculateComponentDepthLevel(nfUint32 nLevel) override; void extendOutbox(_Out_ NOUTBOX3& vOutBox, _In_ const NMATRIX3 mAccumulatedMatrix) override; + + ResourceDependencies getDependencies() override; }; typedef std::shared_ptr PModelComponentsObject; diff --git a/Include/Model/Classes/NMR_ModelImplicitFunction.h b/Include/Model/Classes/NMR_ModelImplicitFunction.h index ae90a4719..e3600e9f9 100644 --- a/Include/Model/Classes/NMR_ModelImplicitFunction.h +++ b/Include/Model/Classes/NMR_ModelImplicitFunction.h @@ -126,6 +126,8 @@ namespace NMR void sortNodesTopologically(); PModelImplicitPort findPort(const ImplicitIdentifier& sIdentifier) const; + + ResourceDependencies getDependencies() override; }; using PModelImplicitFunction = std::shared_ptr; diff --git a/Include/Model/Classes/NMR_ModelLevelSetObject.h b/Include/Model/Classes/NMR_ModelLevelSetObject.h index 6394edcc9..6a8832bd5 100644 --- a/Include/Model/Classes/NMR_ModelLevelSetObject.h +++ b/Include/Model/Classes/NMR_ModelLevelSetObject.h @@ -105,6 +105,8 @@ namespace NMR { void setMeshBBoxOnly(bool bMeshBBoxOnly); bool getMeshBBoxOnly() const; + + ResourceDependencies getDependencies() override; }; typedef std::shared_ptr PModelLevelSetObject; diff --git a/Include/Model/Classes/NMR_ModelMeshObject.h b/Include/Model/Classes/NMR_ModelMeshObject.h index 88f46b0e7..f3cbf34af 100644 --- a/Include/Model/Classes/NMR_ModelMeshObject.h +++ b/Include/Model/Classes/NMR_ModelMeshObject.h @@ -85,6 +85,8 @@ namespace NMR { _Ret_notnull_ PModelVolumeData getVolumeData(); void setVolumeData(_In_ PModelVolumeData pVolumeData); + + ResourceDependencies getDependencies() override; }; typedef std::shared_ptr PModelMeshObject; diff --git a/Include/Model/Classes/NMR_ModelObject.h b/Include/Model/Classes/NMR_ModelObject.h index 8195083e3..7d64f751d 100644 --- a/Include/Model/Classes/NMR_ModelObject.h +++ b/Include/Model/Classes/NMR_ModelObject.h @@ -118,7 +118,6 @@ namespace NMR { virtual void calculateComponentDepthLevel (nfUint32 nLevel); virtual void extendOutbox(_Out_ NOUTBOX3& vOutBox, _In_ const NMATRIX3 mAccumulatedMatrix) = 0; - }; typedef std::shared_ptr PModelObject; diff --git a/Include/Model/Classes/NMR_ModelResource.h b/Include/Model/Classes/NMR_ModelResource.h index f1fe7f663..54b96e94c 100644 --- a/Include/Model/Classes/NMR_ModelResource.h +++ b/Include/Model/Classes/NMR_ModelResource.h @@ -41,9 +41,12 @@ resource object. #include "Model/Classes/NMR_Model.h" #include +#include namespace NMR { + using ResourceDependencies = std::vector; + class CModelResource { private: CModel * m_pModel; @@ -52,8 +55,7 @@ namespace NMR { protected: std::vector m_ResourceIndexMap; nfBool m_bHasResourceIndexMap; - CModel * Model(); - + CModel * Model(); public: CModelResource() = delete; // CModelResource(_In_ const PPackageResourceID sResourceID, _In_ CModel * pModel); @@ -70,6 +72,10 @@ namespace NMR { _Ret_notnull_ CModel * getModel(); void setModel(CModel * pModel); + + /// Returns all dependencies of this resource, usd for topological sorting during writing + virtual ResourceDependencies getDependencies(); + }; typedef std::shared_ptr PModelResource; diff --git a/Include/Model/Classes/NMR_ModelVolumeData.h b/Include/Model/Classes/NMR_ModelVolumeData.h index 892539a49..f4d403244 100644 --- a/Include/Model/Classes/NMR_ModelVolumeData.h +++ b/Include/Model/Classes/NMR_ModelVolumeData.h @@ -78,6 +78,10 @@ namespace NMR { PVolumeDataComposite createComposite(/* basematerialgroupd*/); void setComposite(PVolumeDataComposite pComposite); void removeComposite(); + + ResourceDependencies getDependencies() override; + + PPackageResourceID packageResourceIDFromModelResourceID(ModelResourceID modelResourceID); }; typedef std::shared_ptr PModelVolumeData; diff --git a/Include/Model/Writer/v100/NMR_ResourceDependencySorter.h b/Include/Model/Writer/v100/NMR_ResourceDependencySorter.h new file mode 100644 index 000000000..2f83d54a3 --- /dev/null +++ b/Include/Model/Writer/v100/NMR_ResourceDependencySorter.h @@ -0,0 +1,65 @@ +/*++ + +Copyright (C) 2024 3MF Consortium + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Abstract: + +NMR_ResourcDependencySorter resolves the dependencies between resources by +sorting them topologically. +--*/ + +#pragma once + +#include + +#include "Common/Graph/DirectedGraph.h" +#include "Common/Graph/GraphAlgorithms.h" +#include "Model/Classes/NMR_Model.h" + +namespace NMR +{ + using TopologicalSortResult = std::vector; + + class CResourceDependencySorter + { + private: + common::graph::DirectedGraph buildGraph(); + void buildIndexMaps(); + + [[nodiscard]] PPackageResourceID indexToModelResourceID(size_t index) const; + [[nodiscard]] size_t modelResourceIDToIndex(PPackageResourceID resourceID) const; + + CModel * m_pModel; + + std::unordered_map m_indexToResourceID; + std::unordered_map m_resourceIDToIndex; + + public: + explicit CResourceDependencySorter(CModel * pModel); + TopologicalSortResult sort(); + + }; + +} // namespace NMR \ No newline at end of file diff --git a/Source/Common/Graph/DirectedGraph.cpp b/Source/Common/Graph/DirectedGraph.cpp index 79be626df..a37614cf1 100644 --- a/Source/Common/Graph/DirectedGraph.cpp +++ b/Source/Common/Graph/DirectedGraph.cpp @@ -4,8 +4,9 @@ Copyright (C) 2023 3MF Consortium All rights reserved. -Redistribution and use in source and binary forms, with or without modification,276529 -are permitted provided that the following conditions are met: +Redistribution and use in source and binary forms, with or without +modification,276529 are permitted provided that the following conditions are +met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. @@ -33,17 +34,15 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace NMR::common::graph { DirectedGraph::DirectedGraph(size_t const size) - : IDirectedGraph(size) - , m_size(size) - , m_graphData(m_size * m_size, false) + : IDirectedGraph(size), + m_size(size), + m_graphData((m_size + 1) * (m_size + 1), false) { } void DirectedGraph::addDependency(Identifier id, Identifier idOfDependency) { - addVertex(id); - addVertex(idOfDependency); - if (id == idOfDependency) + if(id == idOfDependency) { return; } @@ -51,13 +50,15 @@ namespace NMR::common::graph m_graphData[index] = true; } - void DirectedGraph::removeDependency(Identifier id, Identifier idOfDependency) + void DirectedGraph::removeDependency(Identifier id, + Identifier idOfDependency) { auto const index = id * m_size + idOfDependency; m_graphData[index] = false; } - bool DirectedGraph::isDirectlyDependingOn(Identifier id, Identifier dependencyInQuestion) const + bool DirectedGraph::isDirectlyDependingOn( + Identifier id, Identifier dependencyInQuestion) const { auto const index = id * m_size + dependencyInQuestion; return m_graphData[index]; @@ -70,21 +71,22 @@ namespace NMR::common::graph void DirectedGraph::removeVertex(Identifier id) { - auto const iterElemToRemove = std::find(std::begin(m_vertices), std::end(m_vertices), id); - if (iterElemToRemove == std::end(m_vertices)) + auto const iterElemToRemove = + std::find(std::begin(m_vertices), std::end(m_vertices), id); + if(iterElemToRemove == std::end(m_vertices)) { return; } m_vertices.erase(iterElemToRemove); - for (auto vertex : m_vertices) + for(auto vertex : m_vertices) { removeDependency(id, vertex); removeDependency(vertex, id); } } - DependencySet const& DirectedGraph::getVertices() const + auto DirectedGraph::getVertices() const -> const DependencySet& { return m_vertices; } @@ -94,4 +96,8 @@ namespace NMR::common::graph m_vertices.insert(id); } -} // namespace gladius::nodes::graph + auto DirectedGraph::hasPredecessors(Identifier id) const -> bool + { + return !m_predecessors[id].empty(); + } +} // namespace NMR::common::graph diff --git a/Source/Model/Classes/NMR_Model.cpp b/Source/Model/Classes/NMR_Model.cpp index a9c25e3c8..f116fe3fd 100644 --- a/Source/Model/Classes/NMR_Model.cpp +++ b/Source/Model/Classes/NMR_Model.cpp @@ -1573,16 +1573,35 @@ namespace NMR { return size; } - PModelFunction CModel::findFunction(_In_ UniqueResourceID nResourceID) { - for (size_t i = 0; i < m_FunctionLookup.size(); i++) { - PModelFunction pFunction = std::dynamic_pointer_cast(m_FunctionLookup[i]); - if (pFunction->getPackageResourceID()->getUniqueID() == nResourceID) - return pFunction; - } - return nullptr; + ModelResourceID CModel::getMaxModelResourceID() + { + auto maxResourceID = std::max_element( + m_Resources.begin(), m_Resources.end(), + [](const PModelResource &a, const PModelResource &b) + { + return a->getPackageResourceID()->getModelResourceID() < + b->getPackageResourceID()->getModelResourceID(); + }); + if(maxResourceID == m_Resources.end()) return 0; + + return (*maxResourceID)->getPackageResourceID()->getModelResourceID(); } - nfUint32 CModel::getFunctionCount() { + PModelFunction CModel::findFunction(_In_ UniqueResourceID nResourceID) + { + for(size_t i = 0; i < m_FunctionLookup.size(); i++) + { + PModelFunction pFunction = + std::dynamic_pointer_cast( + m_FunctionLookup[i]); + if(pFunction->getPackageResourceID()->getUniqueID() == + nResourceID) + return pFunction; + } + return nullptr; + } + + nfUint32 CModel::getFunctionCount() { return (nfUint32)m_FunctionLookup.size(); } diff --git a/Source/Model/Classes/NMR_ModelComponentsObject.cpp b/Source/Model/Classes/NMR_ModelComponentsObject.cpp index d77b8a369..b2f71f891 100644 --- a/Source/Model/Classes/NMR_ModelComponentsObject.cpp +++ b/Source/Model/Classes/NMR_ModelComponentsObject.cpp @@ -149,8 +149,26 @@ namespace NMR { void CModelComponentsObject::extendOutbox(_Out_ NOUTBOX3& vOutBox, _In_ const NMATRIX3 mAccumulatedMatrix) { - for (auto iIterator = m_Components.begin(); iIterator != m_Components.end(); iIterator++) { - (*iIterator)->getObject()->extendOutbox(vOutBox, fnMATRIX3_multiply(mAccumulatedMatrix, (*iIterator)->getTransform())); + for(auto & component : m_Components) + { + component->getObject()->extendOutbox(vOutBox, fnMATRIX3_multiply(mAccumulatedMatrix, component->getTransform())); } } -} + + ResourceDependencies CModelComponentsObject::getDependencies() + { + ResourceDependencies dependencies; + + for(auto & component : m_Components) + { + auto objectResource = component->getObject(); + + if (objectResource) + { + dependencies.push_back(objectResource->getPackageResourceID()); + } + } + + return dependencies; + } +} // namespace NMR diff --git a/Source/Model/Classes/NMR_ModelImplicitFunction.cpp b/Source/Model/Classes/NMR_ModelImplicitFunction.cpp index dafa2c74e..448ccb208 100644 --- a/Source/Model/Classes/NMR_ModelImplicitFunction.cpp +++ b/Source/Model/Classes/NMR_ModelImplicitFunction.cpp @@ -440,6 +440,36 @@ namespace NMR } return node->findOutput(portName); } -} + ResourceDependencies CModelImplicitFunction::getDependencies() + { + ResourceDependencies dependencies; + for (auto const& node : *m_nodes) + { + if (!node) + { + throw ELib3MFInterfaceException(LIB3MF_ERROR_INVALIDPARAM, + "Node must not be nullptr."); + } + if (node->getNodeType() == Lib3MF::eImplicitNodeType::ConstResourceID) + { + auto * model = getModel(); + if (!model) + { + throw ELib3MFInterfaceException(LIB3MF_ERROR_INVALIDPARAM, + "Model must not be nullptr."); + } + + auto referendedResource = model->findResource(model->currentPath(), node->getModelResourceID()); + if (!referendedResource) + { + throw ELib3MFInterfaceException(LIB3MF_ERROR_INVALIDPARAM, + "Referenced resource must not be nullptr."); + } + dependencies.push_back(referendedResource->getPackageResourceID()); + } + } + return dependencies; + } +} diff --git a/Source/Model/Classes/NMR_ModelLevelSetObject.cpp b/Source/Model/Classes/NMR_ModelLevelSetObject.cpp index 0e329962c..59eb36387 100644 --- a/Source/Model/Classes/NMR_ModelLevelSetObject.cpp +++ b/Source/Model/Classes/NMR_ModelLevelSetObject.cpp @@ -178,4 +178,23 @@ namespace NMR { return m_meshBBoxOnly; } + + ResourceDependencies CModelLevelSetObject::getDependencies() + { + ResourceDependencies dependencies; + if (m_pFunction) + { + dependencies.push_back(m_pFunction->getPackageResourceID()); + } + + if (m_pVolumeData) + { + // concatenate dependencies + auto volumeDataDependencies = m_pVolumeData->getDependencies(); + dependencies.insert(dependencies.end(), volumeDataDependencies.begin(), volumeDataDependencies.end()); + } + + return dependencies; + } } // namespace NMR + diff --git a/Source/Model/Classes/NMR_ModelMeshObject.cpp b/Source/Model/Classes/NMR_ModelMeshObject.cpp index 337fd0520..8100bd280 100644 --- a/Source/Model/Classes/NMR_ModelMeshObject.cpp +++ b/Source/Model/Classes/NMR_ModelMeshObject.cpp @@ -238,6 +238,18 @@ namespace NMR { { m_pMesh->extendOutbox(vOutBox, mAccumulatedMatrix); } + + ResourceDependencies CModelMeshObject::getDependencies() + { + ResourceDependencies dependencies; + if(m_pVolumeData) + { + dependencies.push_back( + m_pVolumeData->getPackageResourceID()); + } + + return dependencies; + } } diff --git a/Source/Model/Classes/NMR_ModelObject.cpp b/Source/Model/Classes/NMR_ModelObject.cpp index ccfd31e63..b1aa7da30 100644 --- a/Source/Model/Classes/NMR_ModelObject.cpp +++ b/Source/Model/Classes/NMR_ModelObject.cpp @@ -208,6 +208,4 @@ namespace NMR { if (nLevel >= m_nComponentDepthLevel) m_nComponentDepthLevel = nLevel; } - - } diff --git a/Source/Model/Classes/NMR_ModelResource.cpp b/Source/Model/Classes/NMR_ModelResource.cpp index 95ca2ed8f..8776154a9 100644 --- a/Source/Model/Classes/NMR_ModelResource.cpp +++ b/Source/Model/Classes/NMR_ModelResource.cpp @@ -34,7 +34,7 @@ resource object. #include "Model/Classes/NMR_Model.h" #include "Model/Classes/NMR_ModelResource.h" -#include "Common/NMR_Exception.h" +#include "Common/NMR_Exception.h" namespace NMR { @@ -82,7 +82,12 @@ namespace NMR { m_pModel = pModel; } - void CModelResource::clearResourceIndexMap() + ResourceDependencies CModelResource::getDependencies() + { + return {}; + } + + void CModelResource::clearResourceIndexMap() { m_ResourceIndexMap.clear(); m_bHasResourceIndexMap = false; diff --git a/Source/Model/Classes/NMR_ModelVolumeData.cpp b/Source/Model/Classes/NMR_ModelVolumeData.cpp index dd6be60b2..af85be90f 100644 --- a/Source/Model/Classes/NMR_ModelVolumeData.cpp +++ b/Source/Model/Classes/NMR_ModelVolumeData.cpp @@ -33,6 +33,7 @@ NMR_ModelVolumeData.cpp implements the class CModelVolumeData. #include "Model/Classes/NMR_ModelVolumeData.h" #include "Model/Classes/NMR_ModelFunction.h" +#include "lib3mf_interfaceexception.hpp" namespace NMR { @@ -174,4 +175,57 @@ namespace NMR m_pComposite.reset(); } -} + ResourceDependencies CModelVolumeData::getDependencies() + { + ResourceDependencies dependencies; + + if (m_pColor) + { + dependencies.push_back(packageResourceIDFromModelResourceID(m_pColor->getFunctionResourceID())); + } + + for (auto iIterator = m_mapProperties.begin(); iIterator != m_mapProperties.end(); iIterator++) + { + PVolumeDataProperty pProperty = iIterator->second; + if (pProperty) + { + dependencies.push_back(packageResourceIDFromModelResourceID(pProperty->getFunctionResourceID())); + } + } + + if (m_pComposite) + { + for (nfUint32 i = 0u; i < m_pComposite->materialMappingCount(); i++) + { + dependencies.push_back(packageResourceIDFromModelResourceID(m_pComposite->getMaterialMapping(i)->getFunctionResourceID())); + } + + } + + return dependencies; + } + + + PPackageResourceID + CModelVolumeData::packageResourceIDFromModelResourceID( + ModelResourceID modelResourceID) + { + auto* model = getModel(); + if(!model) + { + throw ELib3MFInterfaceException(LIB3MF_ERROR_INVALIDPARAM, + "Model must not be nullptr."); + } + + auto referendedResource = model->findResource( + model->currentPath(), modelResourceID); + if(!referendedResource) + { + throw ELib3MFInterfaceException( + LIB3MF_ERROR_INVALIDPARAM, + "Referenced resource must not be nullptr."); + } + + return referendedResource->getPackageResourceID(); + } +} // namespace NMR diff --git a/Source/Model/Writer/v100/NMR_ModelWriterNode100_Model.cpp b/Source/Model/Writer/v100/NMR_ModelWriterNode100_Model.cpp index 45101d75c..35b9965de 100644 --- a/Source/Model/Writer/v100/NMR_ModelWriterNode100_Model.cpp +++ b/Source/Model/Writer/v100/NMR_ModelWriterNode100_Model.cpp @@ -33,6 +33,7 @@ This is the class for exporting the 3mf model stream root node. #include "Model/Writer/v100/NMR_ModelWriterNode100_Mesh.h" #include "Model/Writer/v100/NMR_ModelWriterNode100_Model.h" +#include "Model/Writer/v100/NMR_ResourceDependencySorter.h" #include "Model/Classes/NMR_ModelAttachment.h" #include "Model/Classes/NMR_ModelBuildItem.h" @@ -53,6 +54,8 @@ This is the class for exporting the 3mf model stream root node. #include "Common/NMR_Exception_Windows.h" #include "Common/NMR_StringUtils.h" #include "Common/MeshInformation/NMR_MeshInformation_Properties.h" +#include "Common/Graph/DirectedGraph.h" +#include "Common/Graph/GraphAlgorithms.h" #include "Model/Classes/NMR_ModelConstants_Slices.h" #include "Model/Classes/NMR_ModelImplicitFunction.h" #include "Model/Classes/NMR_ModelLevelSetObject.h" @@ -1043,19 +1046,28 @@ namespace NMR { writeSliceStacks(); } + // Create topological order of resources + CResourceDependencySorter sorter(m_pModel); + auto sortedResources = sorter.sort(); - for(auto resId = 0u; resId < m_pModel->getResourceCount(); resId++) + for(auto &resId : sortedResources) { - auto pResource = m_pModel->getResource(resId); + if(resId->getPath() != m_pModel->currentPath()) + { + continue; + } + auto pResource = + m_pModel->findResource(resId->getUniqueID()); - if (!pResource) + if(!pResource) { - throw CNMRException(NMR_ERROR_INVALID_RESOURCE_INDEX, "Invalid Resource"); + throw CNMRException( + NMR_ERROR_INVALID_RESOURCE_INDEX, + "Invalid Resource"); } writeResource(pResource.get()); - } - } + } else { if (m_bWriteSliceExtension) { writeSliceStacks(); diff --git a/Source/Model/Writer/v100/NMR_ResourceDependencySorter.cpp b/Source/Model/Writer/v100/NMR_ResourceDependencySorter.cpp new file mode 100644 index 000000000..b7905ca14 --- /dev/null +++ b/Source/Model/Writer/v100/NMR_ResourceDependencySorter.cpp @@ -0,0 +1,120 @@ +/*++ + +Copyright (C) 2024 3MF Consortium + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Abstract: + +NMR_ResourcDependencySorter resolves the dependencies between resources by +sorting them topologically. +--*/ + +#include "Model/Writer/v100/NMR_ResourceDependencySorter.h" + +#include "Common/Graph/DirectedGraph.h" +#include "Common/Graph/GraphAlgorithms.h" +#include "Model/Classes/NMR_Model.h" +#include "Model/Classes/NMR_ModelResource.h" + +namespace NMR +{ + + CResourceDependencySorter::CResourceDependencySorter(CModel* pModel) + : m_pModel(pModel) + { + buildIndexMaps(); + buildGraph(); + } + + TopologicalSortResult CResourceDependencySorter::sort() + { + // gather list of resources that are standalone + TopologicalSortResult sortedResources; + + for(nfUint32 i = 0; i < m_pModel->getResourceCount(); i++) + { + auto pResource = m_pModel->getResource(i); + if(pResource->getDependencies().empty()) + { + sortedResources.push_back( + pResource->getPackageResourceID()); + } + } + + auto sortedIndices = common::graph::topologicalSort( + buildGraph()); // only contains resources that have or are a + // dependency + for(auto index : sortedIndices) + { + + auto modelResourceID = indexToModelResourceID(index); + if (std::find(sortedResources.begin(), sortedResources.end(), modelResourceID) == sortedResources.end()) { + sortedResources.push_back(modelResourceID); + } + } + return sortedResources; + } + + common::graph::DirectedGraph CResourceDependencySorter::buildGraph() + { + common::graph::DirectedGraph graph(m_pModel->getResourceCount()); + + for(nfUint32 i = 0; i < m_pModel->getResourceCount(); i++) + { + auto pResource = m_pModel->getResource(i); + auto dependencies = pResource->getDependencies(); + for(auto dependency : dependencies) + { + auto dependencyIndex = modelResourceIDToIndex(dependency); + graph.addDependency(i, dependencyIndex); + } + } + + return graph; + } + + void CResourceDependencySorter::buildIndexMaps() + { + for(nfUint32 i = 0; i < m_pModel->getResourceCount(); i++) + { + auto pResource = m_pModel->getResource(i); + m_indexToResourceID[i] = + pResource->getPackageResourceID(); + m_resourceIDToIndex[pResource->getPackageResourceID()] = i; + } + } + + PPackageResourceID CResourceDependencySorter::indexToModelResourceID( + size_t index) const + { + return m_indexToResourceID.at(index); + } + + size_t CResourceDependencySorter::modelResourceIDToIndex( + PPackageResourceID resourceID) const + { + return m_resourceIDToIndex.at(resourceID); + } + +} // namespace NMR \ No newline at end of file