From b6b6808fb6303c1a0ff15376946c117b61e42239 Mon Sep 17 00:00:00 2001 From: Themis Skamagkis Date: Fri, 6 Feb 2026 14:58:34 +0100 Subject: [PATCH 1/6] [Mapping] Create a SubsetTopologicalMultiMapping (points, edges, triangles supported) --- .../Component/Topology/Mapping/CMakeLists.txt | 2 + .../mapping/SubsetTopologicalMultiMapping.cpp | 212 ++++++++++++++++++ .../mapping/SubsetTopologicalMultiMapping.h | 92 ++++++++ .../sofa/component/topology/mapping/init.cpp | 2 + 4 files changed, 308 insertions(+) create mode 100644 Sofa/Component/Topology/Mapping/src/sofa/component/topology/mapping/SubsetTopologicalMultiMapping.cpp create mode 100644 Sofa/Component/Topology/Mapping/src/sofa/component/topology/mapping/SubsetTopologicalMultiMapping.h diff --git a/Sofa/Component/Topology/Mapping/CMakeLists.txt b/Sofa/Component/Topology/Mapping/CMakeLists.txt index f39826a9308..abbbc099e7f 100644 --- a/Sofa/Component/Topology/Mapping/CMakeLists.txt +++ b/Sofa/Component/Topology/Mapping/CMakeLists.txt @@ -13,6 +13,7 @@ set(HEADER_FILES ${SOFACOMPONENTTOPOLOGYMAPPING_SOURCE_DIR}/IdentityTopologicalMapping.h ${SOFACOMPONENTTOPOLOGYMAPPING_SOURCE_DIR}/Quad2TriangleTopologicalMapping.h ${SOFACOMPONENTTOPOLOGYMAPPING_SOURCE_DIR}/SubsetTopologicalMapping.h + ${SOFACOMPONENTTOPOLOGYMAPPING_SOURCE_DIR}/SubsetTopologicalMultiMapping.h ${SOFACOMPONENTTOPOLOGYMAPPING_SOURCE_DIR}/Tetra2TriangleTopologicalMapping.h ${SOFACOMPONENTTOPOLOGYMAPPING_SOURCE_DIR}/Triangle2EdgeTopologicalMapping.h ) @@ -26,6 +27,7 @@ set(SOURCE_FILES ${SOFACOMPONENTTOPOLOGYMAPPING_SOURCE_DIR}/IdentityTopologicalMapping.cpp ${SOFACOMPONENTTOPOLOGYMAPPING_SOURCE_DIR}/Quad2TriangleTopologicalMapping.cpp ${SOFACOMPONENTTOPOLOGYMAPPING_SOURCE_DIR}/SubsetTopologicalMapping.cpp + ${SOFACOMPONENTTOPOLOGYMAPPING_SOURCE_DIR}/SubsetTopologicalMultiMapping.cpp ${SOFACOMPONENTTOPOLOGYMAPPING_SOURCE_DIR}/Tetra2TriangleTopologicalMapping.cpp ${SOFACOMPONENTTOPOLOGYMAPPING_SOURCE_DIR}/Triangle2EdgeTopologicalMapping.cpp ) diff --git a/Sofa/Component/Topology/Mapping/src/sofa/component/topology/mapping/SubsetTopologicalMultiMapping.cpp b/Sofa/Component/Topology/Mapping/src/sofa/component/topology/mapping/SubsetTopologicalMultiMapping.cpp new file mode 100644 index 00000000000..519843f0cf9 --- /dev/null +++ b/Sofa/Component/Topology/Mapping/src/sofa/component/topology/mapping/SubsetTopologicalMultiMapping.cpp @@ -0,0 +1,212 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture * +* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#include + +#include +#include + +namespace sofa::component::topology::mapping +{ + +void registerSubsetTopologicalMultiMapping(sofa::core::ObjectFactory* factory) +{ + factory->registerObjects(core::ObjectRegistrationData( + "Merges multiple input topologies (points, edges, triangles) into a single " + "output topology with index remapping. Optionally populates indexPairs for " + "SubsetMultiMapping coordination.") + .add()); +} + +SubsetTopologicalMultiMapping::SubsetTopologicalMultiMapping() + : l_inputTopologies(initLink("input", "Input topology sources to merge")) + , l_outputTopology(initLink("output", "Output merged topology")) + , l_subsetMultiMapping(initLink("subsetMultiMapping", + "Optional link to a SubsetMultiMapping to auto-populate its indexPairs")) + , d_flipNormals(initData(&d_flipNormals, sofa::type::vector(), + "flipNormals", + "Per-source boolean flags to reverse triangle winding order")) + , d_indexPairs(initData(&d_indexPairs, sofa::type::vector(), + "indexPairs", + "Output: flat array of (source_index, coord_in_source) pairs")) +{ + d_indexPairs.setReadOnly(true); +} + +SubsetTopologicalMultiMapping::~SubsetTopologicalMultiMapping() = default; + +void SubsetTopologicalMultiMapping::init() +{ + BaseObject::init(); + + const auto numInputs = l_inputTopologies.size(); + if (numInputs == 0) + { + msg_error() << "No input topologies linked. Set the 'input' attribute with " + "space-separated paths to input topologies."; + d_componentState.setValue(sofa::core::objectmodel::ComponentState::Invalid); + return; + } + + for (std::size_t i = 0; i < numInputs; ++i) + { + if (l_inputTopologies.get(i) == nullptr) + { + msg_error() << "Input topology #" << i << " is null."; + d_componentState.setValue(sofa::core::objectmodel::ComponentState::Invalid); + return; + } + } + + if (l_outputTopology.get() == nullptr) + { + msg_error() << "Output topology is null. Set the 'output' attribute."; + d_componentState.setValue(sofa::core::objectmodel::ComponentState::Invalid); + return; + } + + doMerge(); + + if (l_subsetMultiMapping.get() != nullptr) + { + populateSubsetMultiMappingIndexPairs(); + } + + d_componentState.setValue(sofa::core::objectmodel::ComponentState::Valid); +} + +void SubsetTopologicalMultiMapping::reinit() +{ + if (d_componentState.getValue() == sofa::core::objectmodel::ComponentState::Invalid) + return; + + doMerge(); + + if (l_subsetMultiMapping.get() != nullptr) + { + populateSubsetMultiMappingIndexPairs(); + } +} + +void SubsetTopologicalMultiMapping::doMerge() +{ + BaseMeshTopology* output = l_outputTopology.get(); + const auto numInputs = l_inputTopologies.size(); + + // Phase 1: Clear output topology + output->clear(); + + // Phase 2: Compute per-source point offsets + m_pointOffsets.resize(numInputs); + Index totalPoints = 0; + for (std::size_t i = 0; i < numInputs; ++i) + { + m_pointOffsets[i] = totalPoints; + totalPoints += static_cast(l_inputTopologies.get(i)->getNbPoints()); + } + + // Phase 3: Set total point count on output + output->setNbPoints(static_cast(totalPoints)); + + // Phase 4: Build indexPairs for SubsetMultiMapping coordination + { + auto indexPairs = sofa::helper::getWriteOnlyAccessor(d_indexPairs); + indexPairs.clear(); + indexPairs.reserve(static_cast(totalPoints) * 2); + + for (std::size_t srcIdx = 0; srcIdx < numInputs; ++srcIdx) + { + const auto nbPts = static_cast(l_inputTopologies.get(srcIdx)->getNbPoints()); + for (Index p = 0; p < nbPts; ++p) + { + indexPairs.push_back(static_cast(srcIdx)); + indexPairs.push_back(static_cast(p)); + } + } + } + + // Phase 5: Read flip flags + const auto& flipVec = d_flipNormals.getValue(); + if (!flipVec.empty() && flipVec.size() != numInputs) + { + msg_warning() << "flipNormals has " << flipVec.size() << " entries but there are " + << numInputs << " input topologies. Missing entries default to false."; + } + + // Phase 6: Concatenate edges with offset remapping + for (std::size_t srcIdx = 0; srcIdx < numInputs; ++srcIdx) + { + BaseMeshTopology* input = l_inputTopologies.get(srcIdx); + const Index offset = m_pointOffsets[srcIdx]; + const auto& edges = input->getEdges(); + + for (std::size_t e = 0; e < edges.size(); ++e) + { + output->addEdge(edges[e][0] + offset, edges[e][1] + offset); + } + } + + // Phase 7: Concatenate triangles with offset remapping + optional flip + for (std::size_t srcIdx = 0; srcIdx < numInputs; ++srcIdx) + { + BaseMeshTopology* input = l_inputTopologies.get(srcIdx); + const Index offset = m_pointOffsets[srcIdx]; + const bool flip = (srcIdx < flipVec.size()) ? flipVec[srcIdx] : false; + const auto& triangles = input->getTriangles(); + + for (std::size_t t = 0; t < triangles.size(); ++t) + { + const auto& tri = triangles[t]; + if (flip) + output->addTriangle(tri[0] + offset, tri[2] + offset, tri[1] + offset); + else + output->addTriangle(tri[0] + offset, tri[1] + offset, tri[2] + offset); + } + } + + msg_info() << "Merged " << numInputs << " topologies: " + << totalPoints << " points, " + << output->getNbEdges() << " edges, " + << output->getNbTriangles() << " triangles."; +} + +void SubsetTopologicalMultiMapping::populateSubsetMultiMappingIndexPairs() +{ + auto* targetObj = l_subsetMultiMapping.get(); + if (!targetObj) + return; + + auto* targetData = targetObj->findData("indexPairs"); + if (!targetData) + { + msg_error() << "Linked object '" << targetObj->getName() + << "' has no 'indexPairs' Data field. " + "Ensure it is a SubsetMultiMapping."; + return; + } + + targetData->copyValueFrom(&d_indexPairs); + + msg_info() << "Set " << (d_indexPairs.getValue().size() / 2) + << " indexPairs on '" << targetObj->getName() << "'."; +} + +} // namespace sofa::component::topology::mapping diff --git a/Sofa/Component/Topology/Mapping/src/sofa/component/topology/mapping/SubsetTopologicalMultiMapping.h b/Sofa/Component/Topology/Mapping/src/sofa/component/topology/mapping/SubsetTopologicalMultiMapping.h new file mode 100644 index 00000000000..e55d6e86993 --- /dev/null +++ b/Sofa/Component/Topology/Mapping/src/sofa/component/topology/mapping/SubsetTopologicalMultiMapping.h @@ -0,0 +1,92 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture * +* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#pragma once +#include + +#include +#include + +namespace sofa::component::topology::mapping +{ + +/** + * @class SubsetTopologicalMultiMapping + * @brief Merges multiple input topologies (points, edges, triangles) into a single output topology. + * + * This is the topological counterpart to SubsetMultiMapping on the mechanical side. + * For each input topology, points are concatenated with index offsets, and edges/triangles + * are remapped accordingly. Triangle winding order can be reversed per source via flipNormals. + * + * The component can optionally auto-populate the indexPairs Data of a linked SubsetMultiMapping + * for mechanical-side coordination. + * + * Static only: merging is performed once during init(). Dynamic topology changes are not propagated. + */ +class SOFA_COMPONENT_TOPOLOGY_MAPPING_API SubsetTopologicalMultiMapping + : public sofa::core::objectmodel::BaseObject +{ +public: + SOFA_CLASS(SubsetTopologicalMultiMapping, sofa::core::objectmodel::BaseObject); + + using Index = sofa::Index; + using BaseMeshTopology = sofa::core::topology::BaseMeshTopology; + + void init() override; + void reinit() override; + + /// N input topologies + MultiLink + l_inputTopologies; + + /// Single output topology + SingleLink + l_outputTopology; + + /// Optional link to a SubsetMultiMapping to auto-populate its indexPairs. + /// Uses BaseObject to avoid template dependency; validated at runtime. + SingleLink + l_subsetMultiMapping; + + Data> d_flipNormals; ///< Per-source flags to reverse triangle winding order (v0,v1,v2) -> (v0,v2,v1) + Data> d_indexPairs; ///< Output: flat array of (source_index, coord_in_source) pairs for SubsetMultiMapping + +protected: + SubsetTopologicalMultiMapping(); + ~SubsetTopologicalMultiMapping() override; + +private: + void doMerge(); + void populateSubsetMultiMappingIndexPairs(); + + /// Per-source cumulative point offsets. m_pointOffsets[i] = total points from sources 0..i-1. + sofa::type::vector m_pointOffsets; +}; + +} // namespace sofa::component::topology::mapping diff --git a/Sofa/Component/Topology/Mapping/src/sofa/component/topology/mapping/init.cpp b/Sofa/Component/Topology/Mapping/src/sofa/component/topology/mapping/init.cpp index 7a520cdffd2..44dcfdc8f66 100644 --- a/Sofa/Component/Topology/Mapping/src/sofa/component/topology/mapping/init.cpp +++ b/Sofa/Component/Topology/Mapping/src/sofa/component/topology/mapping/init.cpp @@ -33,6 +33,7 @@ extern void registerHexa2TetraTopologicalMapping(sofa::core::ObjectFactory* fact extern void registerIdentityTopologicalMapping(sofa::core::ObjectFactory* factory); extern void registerQuad2TriangleTopologicalMapping(sofa::core::ObjectFactory* factory); extern void registerSubsetTopologicalMapping(sofa::core::ObjectFactory* factory); +extern void registerSubsetTopologicalMultiMapping(sofa::core::ObjectFactory* factory); extern void registerTetra2TriangleTopologicalMapping(sofa::core::ObjectFactory* factory); extern void registerTriangle2EdgeTopologicalMapping(sofa::core::ObjectFactory* factory); @@ -67,6 +68,7 @@ void registerObjects(sofa::core::ObjectFactory* factory) registerIdentityTopologicalMapping(factory); registerQuad2TriangleTopologicalMapping(factory); registerSubsetTopologicalMapping(factory); + registerSubsetTopologicalMultiMapping(factory); registerTetra2TriangleTopologicalMapping(factory); registerTriangle2EdgeTopologicalMapping(factory); } From 9a537243c953600c9d13b0451439cb92e9e9de04 Mon Sep 17 00:00:00 2001 From: Themis Skamagkis Date: Wed, 11 Feb 2026 00:32:41 +0100 Subject: [PATCH 2/6] [Mapping] Add support for tetrahedra --- .../mapping/SubsetTopologicalMultiMapping.cpp | 20 +++++++++++++++++-- .../mapping/SubsetTopologicalMultiMapping.h | 4 ++-- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/Sofa/Component/Topology/Mapping/src/sofa/component/topology/mapping/SubsetTopologicalMultiMapping.cpp b/Sofa/Component/Topology/Mapping/src/sofa/component/topology/mapping/SubsetTopologicalMultiMapping.cpp index 519843f0cf9..4560d93eb32 100644 --- a/Sofa/Component/Topology/Mapping/src/sofa/component/topology/mapping/SubsetTopologicalMultiMapping.cpp +++ b/Sofa/Component/Topology/Mapping/src/sofa/component/topology/mapping/SubsetTopologicalMultiMapping.cpp @@ -30,7 +30,7 @@ namespace sofa::component::topology::mapping void registerSubsetTopologicalMultiMapping(sofa::core::ObjectFactory* factory) { factory->registerObjects(core::ObjectRegistrationData( - "Merges multiple input topologies (points, edges, triangles) into a single " + "Merges multiple input topologies (points, edges, triangles, tetrahedra) into a single " "output topology with index remapping. Optionally populates indexPairs for " "SubsetMultiMapping coordination.") .add()); @@ -182,10 +182,26 @@ void SubsetTopologicalMultiMapping::doMerge() } } + // Phase 8: Concatenate tetrahedra with offset remapping + for (std::size_t srcIdx = 0; srcIdx < numInputs; ++srcIdx) + { + BaseMeshTopology* input = l_inputTopologies.get(srcIdx); + const Index offset = m_pointOffsets[srcIdx]; + const auto& tetrahedra = input->getTetrahedra(); + + for (std::size_t t = 0; t < tetrahedra.size(); ++t) + { + const auto& tet = tetrahedra[t]; + output->addTetra(tet[0] + offset, tet[1] + offset, + tet[2] + offset, tet[3] + offset); + } + } + msg_info() << "Merged " << numInputs << " topologies: " << totalPoints << " points, " << output->getNbEdges() << " edges, " - << output->getNbTriangles() << " triangles."; + << output->getNbTriangles() << " triangles, " + << output->getNbTetrahedra() << " tetrahedra."; } void SubsetTopologicalMultiMapping::populateSubsetMultiMappingIndexPairs() diff --git a/Sofa/Component/Topology/Mapping/src/sofa/component/topology/mapping/SubsetTopologicalMultiMapping.h b/Sofa/Component/Topology/Mapping/src/sofa/component/topology/mapping/SubsetTopologicalMultiMapping.h index e55d6e86993..47779d8a30c 100644 --- a/Sofa/Component/Topology/Mapping/src/sofa/component/topology/mapping/SubsetTopologicalMultiMapping.h +++ b/Sofa/Component/Topology/Mapping/src/sofa/component/topology/mapping/SubsetTopologicalMultiMapping.h @@ -30,10 +30,10 @@ namespace sofa::component::topology::mapping /** * @class SubsetTopologicalMultiMapping - * @brief Merges multiple input topologies (points, edges, triangles) into a single output topology. + * @brief Merges multiple input topologies (points, edges, triangles, tetrahedra) into a single output topology. * * This is the topological counterpart to SubsetMultiMapping on the mechanical side. - * For each input topology, points are concatenated with index offsets, and edges/triangles + * For each input topology, points are concatenated with index offsets, and edges/triangles/tetrahedra * are remapped accordingly. Triangle winding order can be reversed per source via flipNormals. * * The component can optionally auto-populate the indexPairs Data of a linked SubsetMultiMapping From a2b94fa7109fb4a86a196319cf035575ed3a63dd Mon Sep 17 00:00:00 2001 From: Themis Skamagkis Date: Wed, 11 Feb 2026 10:02:22 +0100 Subject: [PATCH 3/6] [Mapping] Add quads (allow flip normals) and hexahedra support --- .../mapping/SubsetTopologicalMultiMapping.cpp | 47 +++++++++++++++++-- .../mapping/SubsetTopologicalMultiMapping.h | 9 ++-- 2 files changed, 48 insertions(+), 8 deletions(-) diff --git a/Sofa/Component/Topology/Mapping/src/sofa/component/topology/mapping/SubsetTopologicalMultiMapping.cpp b/Sofa/Component/Topology/Mapping/src/sofa/component/topology/mapping/SubsetTopologicalMultiMapping.cpp index 4560d93eb32..ff91ea638f1 100644 --- a/Sofa/Component/Topology/Mapping/src/sofa/component/topology/mapping/SubsetTopologicalMultiMapping.cpp +++ b/Sofa/Component/Topology/Mapping/src/sofa/component/topology/mapping/SubsetTopologicalMultiMapping.cpp @@ -30,7 +30,7 @@ namespace sofa::component::topology::mapping void registerSubsetTopologicalMultiMapping(sofa::core::ObjectFactory* factory) { factory->registerObjects(core::ObjectRegistrationData( - "Merges multiple input topologies (points, edges, triangles, tetrahedra) into a single " + "Merges multiple input topologies (points, edges, triangles, quads, tetrahedra, hexahedra) into a single " "output topology with index remapping. Optionally populates indexPairs for " "SubsetMultiMapping coordination.") .add()); @@ -43,7 +43,7 @@ SubsetTopologicalMultiMapping::SubsetTopologicalMultiMapping() "Optional link to a SubsetMultiMapping to auto-populate its indexPairs")) , d_flipNormals(initData(&d_flipNormals, sofa::type::vector(), "flipNormals", - "Per-source boolean flags to reverse triangle winding order")) + "Per-source boolean flags to reverse triangle and quad winding order")) , d_indexPairs(initData(&d_indexPairs, sofa::type::vector(), "indexPairs", "Output: flat array of (source_index, coord_in_source) pairs")) @@ -182,7 +182,27 @@ void SubsetTopologicalMultiMapping::doMerge() } } - // Phase 8: Concatenate tetrahedra with offset remapping + // Phase 8: Concatenate quads with offset remapping + optional flip + for (std::size_t srcIdx = 0; srcIdx < numInputs; ++srcIdx) + { + BaseMeshTopology* input = l_inputTopologies.get(srcIdx); + const Index offset = m_pointOffsets[srcIdx]; + const bool flip = (srcIdx < flipVec.size()) ? flipVec[srcIdx] : false; + const auto& quads = input->getQuads(); + + for (std::size_t q = 0; q < quads.size(); ++q) + { + const auto& quad = quads[q]; + if (flip) + output->addQuad(quad[0] + offset, quad[3] + offset, + quad[2] + offset, quad[1] + offset); + else + output->addQuad(quad[0] + offset, quad[1] + offset, + quad[2] + offset, quad[3] + offset); + } + } + + // Phase 9: Concatenate tetrahedra with offset remapping for (std::size_t srcIdx = 0; srcIdx < numInputs; ++srcIdx) { BaseMeshTopology* input = l_inputTopologies.get(srcIdx); @@ -197,11 +217,30 @@ void SubsetTopologicalMultiMapping::doMerge() } } + // Phase 10: Concatenate hexahedra with offset remapping + for (std::size_t srcIdx = 0; srcIdx < numInputs; ++srcIdx) + { + BaseMeshTopology* input = l_inputTopologies.get(srcIdx); + const Index offset = m_pointOffsets[srcIdx]; + const auto& hexahedra = input->getHexahedra(); + + for (std::size_t h = 0; h < hexahedra.size(); ++h) + { + const auto& hex = hexahedra[h]; + output->addHexa(hex[0] + offset, hex[1] + offset, + hex[2] + offset, hex[3] + offset, + hex[4] + offset, hex[5] + offset, + hex[6] + offset, hex[7] + offset); + } + } + msg_info() << "Merged " << numInputs << " topologies: " << totalPoints << " points, " << output->getNbEdges() << " edges, " << output->getNbTriangles() << " triangles, " - << output->getNbTetrahedra() << " tetrahedra."; + << output->getNbQuads() << " quads, " + << output->getNbTetrahedra() << " tetrahedra, " + << output->getNbHexahedra() << " hexahedra."; } void SubsetTopologicalMultiMapping::populateSubsetMultiMappingIndexPairs() diff --git a/Sofa/Component/Topology/Mapping/src/sofa/component/topology/mapping/SubsetTopologicalMultiMapping.h b/Sofa/Component/Topology/Mapping/src/sofa/component/topology/mapping/SubsetTopologicalMultiMapping.h index 47779d8a30c..a27ae857d06 100644 --- a/Sofa/Component/Topology/Mapping/src/sofa/component/topology/mapping/SubsetTopologicalMultiMapping.h +++ b/Sofa/Component/Topology/Mapping/src/sofa/component/topology/mapping/SubsetTopologicalMultiMapping.h @@ -30,11 +30,12 @@ namespace sofa::component::topology::mapping /** * @class SubsetTopologicalMultiMapping - * @brief Merges multiple input topologies (points, edges, triangles, tetrahedra) into a single output topology. + * @brief Merges multiple input topologies (points, edges, triangles, quads, tetrahedra, hexahedra) into a single output topology. * * This is the topological counterpart to SubsetMultiMapping on the mechanical side. - * For each input topology, points are concatenated with index offsets, and edges/triangles/tetrahedra - * are remapped accordingly. Triangle winding order can be reversed per source via flipNormals. + * For each input topology, points are concatenated with index offsets, and edges/triangles/quads/ + * tetrahedra/hexahedra are remapped accordingly. Triangle winding order can be reversed per source + * via flipNormals. Quad winding order is also reversed when flipping. * * The component can optionally auto-populate the indexPairs Data of a linked SubsetMultiMapping * for mechanical-side coordination. @@ -74,7 +75,7 @@ class SOFA_COMPONENT_TOPOLOGY_MAPPING_API SubsetTopologicalMultiMapping sofa::core::objectmodel::BaseLink::FLAG_STOREPATH> l_subsetMultiMapping; - Data> d_flipNormals; ///< Per-source flags to reverse triangle winding order (v0,v1,v2) -> (v0,v2,v1) + Data> d_flipNormals; ///< Per-source flags to reverse triangle and quad winding order Data> d_indexPairs; ///< Output: flat array of (source_index, coord_in_source) pairs for SubsetMultiMapping protected: From 86763bfb4e7ff46bcb53929be9e7930352c93322 Mon Sep 17 00:00:00 2001 From: Themis Skamagkis Date: Wed, 11 Feb 2026 14:02:58 +0100 Subject: [PATCH 4/6] [Mapping] Simplify code and format with clang file --- .../mapping/SubsetTopologicalMultiMapping.cpp | 283 ++++++++---------- .../mapping/SubsetTopologicalMultiMapping.h | 91 +++--- 2 files changed, 172 insertions(+), 202 deletions(-) diff --git a/Sofa/Component/Topology/Mapping/src/sofa/component/topology/mapping/SubsetTopologicalMultiMapping.cpp b/Sofa/Component/Topology/Mapping/src/sofa/component/topology/mapping/SubsetTopologicalMultiMapping.cpp index ff91ea638f1..cec387951bc 100644 --- a/Sofa/Component/Topology/Mapping/src/sofa/component/topology/mapping/SubsetTopologicalMultiMapping.cpp +++ b/Sofa/Component/Topology/Mapping/src/sofa/component/topology/mapping/SubsetTopologicalMultiMapping.cpp @@ -1,26 +1,25 @@ /****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU Lesser General Public License as published by * -* the Free Software Foundation; either version 2.1 of the License, or (at * -* your option) any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * -* for more details. * -* * -* You should have received a copy of the GNU Lesser General Public License * -* along with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ + * SOFA, Simulation Open-Framework Architecture * + * (c) 2006 INRIA, USTL, UJF, CNRS, MGH * + * * + * This program is free software; you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as published by * + * the Free Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, but WITHOUT * + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * + * for more details. * + * * + * You should have received a copy of the GNU Lesser General Public License * + * along with this program. If not, see . * + ******************************************************************************* + * Authors: The SOFA Team and external contributors (see Authors.txt) * + * * + * Contact information: contact@sofa-framework.org * + ******************************************************************************/ #include - #include #include @@ -29,24 +28,26 @@ namespace sofa::component::topology::mapping void registerSubsetTopologicalMultiMapping(sofa::core::ObjectFactory* factory) { - factory->registerObjects(core::ObjectRegistrationData( - "Merges multiple input topologies (points, edges, triangles, quads, tetrahedra, hexahedra) into a single " - "output topology with index remapping. Optionally populates indexPairs for " - "SubsetMultiMapping coordination.") - .add()); + factory->registerObjects( + core::ObjectRegistrationData("Merges multiple input topologies (points, edges, triangles, " + "quads, tetrahedra, hexahedra) " + "into a single output topology with index remapping. " + "Optionally populates indexPairs for " + "SubsetMultiMapping.") + .add()); } SubsetTopologicalMultiMapping::SubsetTopologicalMultiMapping() - : l_inputTopologies(initLink("input", "Input topology sources to merge")) - , l_outputTopology(initLink("output", "Output merged topology")) - , l_subsetMultiMapping(initLink("subsetMultiMapping", - "Optional link to a SubsetMultiMapping to auto-populate its indexPairs")) - , d_flipNormals(initData(&d_flipNormals, sofa::type::vector(), - "flipNormals", - "Per-source boolean flags to reverse triangle and quad winding order")) - , d_indexPairs(initData(&d_indexPairs, sofa::type::vector(), - "indexPairs", - "Output: flat array of (source_index, coord_in_source) pairs")) + : l_inputs(initLink("input", "Input topology sources to merge")), + l_output(initLink("output", "Output merged topology")), + l_subsetMultiMapping( + initLink("subsetMultiMapping", + "Optional link to a SubsetMultiMapping to auto-populate its indexPairs")), + d_flipNormals( + initData(&d_flipNormals, sofa::type::vector(), "flipNormals", + "Per-source boolean flags to reverse triangle and quad winding order")), + d_indexPairs(initData(&d_indexPairs, sofa::type::vector(), "indexPairs", + "Output: flat array of (source_index, coord_in_source) pairs")) { d_indexPairs.setReadOnly(true); } @@ -57,197 +58,169 @@ void SubsetTopologicalMultiMapping::init() { BaseObject::init(); - const auto numInputs = l_inputTopologies.size(); - if (numInputs == 0) + if (l_inputs.size()) { - msg_error() << "No input topologies linked. Set the 'input' attribute with " - "space-separated paths to input topologies."; - d_componentState.setValue(sofa::core::objectmodel::ComponentState::Invalid); - return; - } - - for (std::size_t i = 0; i < numInputs; ++i) - { - if (l_inputTopologies.get(i) == nullptr) + for (const auto inputTopology : l_inputs) { - msg_error() << "Input topology #" << i << " is null."; - d_componentState.setValue(sofa::core::objectmodel::ComponentState::Invalid); - return; + if (!inputTopology.get()) + { + msg_error() << "Input topology '" << inputTopology.path << "' could not be found."; + d_componentState.setValue(sofa::core::objectmodel::ComponentState::Invalid); + return; + } } } - - if (l_outputTopology.get() == nullptr) + else { - msg_error() << "Output topology is null. Set the 'output' attribute."; + msg_error() << "No input topologies found to be linked. Set the 'input' Data with " + "paths to the topologies considered as inputs for the mapping."; d_componentState.setValue(sofa::core::objectmodel::ComponentState::Invalid); return; } - doMerge(); - - if (l_subsetMultiMapping.get() != nullptr) + if (!l_output.get()) { - populateSubsetMultiMappingIndexPairs(); + msg_error() + << "Null output topology. Set the 'output' Data with the path to the output topology."; + d_componentState.setValue(sofa::core::objectmodel::ComponentState::Invalid); + return; } + mapTopologies(); + + if (l_subsetMultiMapping.get()) populateSubsetMultiMappingIndexPairs(); + d_componentState.setValue(sofa::core::objectmodel::ComponentState::Valid); } void SubsetTopologicalMultiMapping::reinit() { - if (d_componentState.getValue() == sofa::core::objectmodel::ComponentState::Invalid) - return; + if (d_componentState.getValue() == sofa::core::objectmodel::ComponentState::Invalid) return; - doMerge(); + mapTopologies(); - if (l_subsetMultiMapping.get() != nullptr) - { - populateSubsetMultiMappingIndexPairs(); - } + if (l_subsetMultiMapping.get()) populateSubsetMultiMappingIndexPairs(); } -void SubsetTopologicalMultiMapping::doMerge() +void SubsetTopologicalMultiMapping::mapTopologies() { - BaseMeshTopology* output = l_outputTopology.get(); - const auto numInputs = l_inputTopologies.size(); + const auto numInputs = l_inputs.size(); - // Phase 1: Clear output topology - output->clear(); + l_output->clear(); + m_pointOffsets.clear(); - // Phase 2: Compute per-source point offsets - m_pointOffsets.resize(numInputs); - Index totalPoints = 0; - for (std::size_t i = 0; i < numInputs; ++i) + // Compute per-source point offsets + sofa::Size totalPoints{0}; + for (const auto input : l_inputs) { - m_pointOffsets[i] = totalPoints; - totalPoints += static_cast(l_inputTopologies.get(i)->getNbPoints()); + m_pointOffsets.push_back(totalPoints); + totalPoints += input->getNbPoints(); } + l_output->setNbPoints(totalPoints); - // Phase 3: Set total point count on output - output->setNbPoints(static_cast(totalPoints)); - - // Phase 4: Build indexPairs for SubsetMultiMapping coordination + // Build indexPairs necessary to attach mechanical mapping through SubsetMultiMapping link { auto indexPairs = sofa::helper::getWriteOnlyAccessor(d_indexPairs); indexPairs.clear(); - indexPairs.reserve(static_cast(totalPoints) * 2); + indexPairs.reserve(totalPoints * 2); - for (std::size_t srcIdx = 0; srcIdx < numInputs; ++srcIdx) + for (sofa::Size srcIdx = 0; srcIdx < numInputs; ++srcIdx) { - const auto nbPts = static_cast(l_inputTopologies.get(srcIdx)->getNbPoints()); - for (Index p = 0; p < nbPts; ++p) + const auto nbPts = l_inputs.get(srcIdx)->getNbPoints(); + for (sofa::Size p = 0; p < nbPts; ++p) { - indexPairs.push_back(static_cast(srcIdx)); - indexPairs.push_back(static_cast(p)); + indexPairs.push_back(srcIdx); + indexPairs.push_back(p); } } } - // Phase 5: Read flip flags - const auto& flipVec = d_flipNormals.getValue(); - if (!flipVec.empty() && flipVec.size() != numInputs) + // Pre-normalize flipNormals to exactly numInputs entries + auto flipVec = sofa::helper::getWriteAccessor(d_flipNormals); + if (flipVec.size() > numInputs) + { + msg_warning() << "flipNormals has " << flipVec.size() << " entries but there are only " + << numInputs << " input topologies. Extra entries will be discarded."; + flipVec.resize(numInputs); + } + else if (flipVec.size() < numInputs) { - msg_warning() << "flipNormals has " << flipVec.size() << " entries but there are " - << numInputs << " input topologies. Missing entries default to false."; + flipVec.resize(numInputs, false); } - // Phase 6: Concatenate edges with offset remapping - for (std::size_t srcIdx = 0; srcIdx < numInputs; ++srcIdx) + // Concatenate edges from sources with offset remapping + for (sofa::Size srcIdx = 0; srcIdx < numInputs; ++srcIdx) { - BaseMeshTopology* input = l_inputTopologies.get(srcIdx); - const Index offset = m_pointOffsets[srcIdx]; - const auto& edges = input->getEdges(); + const sofa::Size offset = m_pointOffsets[srcIdx]; - for (std::size_t e = 0; e < edges.size(); ++e) - { - output->addEdge(edges[e][0] + offset, edges[e][1] + offset); - } + for (const auto& edge : l_inputs.get(srcIdx)->getEdges()) + l_output->addEdge(edge[0] + offset, edge[1] + offset); } - // Phase 7: Concatenate triangles with offset remapping + optional flip - for (std::size_t srcIdx = 0; srcIdx < numInputs; ++srcIdx) + // Concatenate triangles with offset remapping; optionally flip normals + for (sofa::Size srcIdx = 0; srcIdx < numInputs; ++srcIdx) { - BaseMeshTopology* input = l_inputTopologies.get(srcIdx); - const Index offset = m_pointOffsets[srcIdx]; - const bool flip = (srcIdx < flipVec.size()) ? flipVec[srcIdx] : false; - const auto& triangles = input->getTriangles(); + const sofa::Size offset = m_pointOffsets[srcIdx]; + const bool flip = flipVec[srcIdx]; - for (std::size_t t = 0; t < triangles.size(); ++t) + for (const auto& tri : l_inputs.get(srcIdx)->getTriangles()) { - const auto& tri = triangles[t]; if (flip) - output->addTriangle(tri[0] + offset, tri[2] + offset, tri[1] + offset); + l_output->addTriangle(tri[0] + offset, tri[2] + offset, tri[1] + offset); else - output->addTriangle(tri[0] + offset, tri[1] + offset, tri[2] + offset); + l_output->addTriangle(tri[0] + offset, tri[1] + offset, tri[2] + offset); } } - // Phase 8: Concatenate quads with offset remapping + optional flip - for (std::size_t srcIdx = 0; srcIdx < numInputs; ++srcIdx) + // Concatenate quads with offset remapping; optionally flip normals + for (sofa::Size srcIdx = 0; srcIdx < numInputs; ++srcIdx) { - BaseMeshTopology* input = l_inputTopologies.get(srcIdx); - const Index offset = m_pointOffsets[srcIdx]; - const bool flip = (srcIdx < flipVec.size()) ? flipVec[srcIdx] : false; - const auto& quads = input->getQuads(); + const sofa::Size offset = m_pointOffsets[srcIdx]; + const bool flip = flipVec[srcIdx]; - for (std::size_t q = 0; q < quads.size(); ++q) + for (auto& quad : l_inputs.get(srcIdx)->getQuads()) { - const auto& quad = quads[q]; if (flip) - output->addQuad(quad[0] + offset, quad[3] + offset, - quad[2] + offset, quad[1] + offset); + l_output->addQuad(quad[0] + offset, quad[3] + offset, quad[2] + offset, + quad[1] + offset); else - output->addQuad(quad[0] + offset, quad[1] + offset, - quad[2] + offset, quad[3] + offset); + l_output->addQuad(quad[0] + offset, quad[1] + offset, quad[2] + offset, + quad[3] + offset); } } - // Phase 9: Concatenate tetrahedra with offset remapping - for (std::size_t srcIdx = 0; srcIdx < numInputs; ++srcIdx) + // Concatenate tetrahedra with offset remapping + for (sofa::Size srcIdx = 0; srcIdx < numInputs; ++srcIdx) { - BaseMeshTopology* input = l_inputTopologies.get(srcIdx); - const Index offset = m_pointOffsets[srcIdx]; - const auto& tetrahedra = input->getTetrahedra(); + const sofa::Size offset = m_pointOffsets[srcIdx]; - for (std::size_t t = 0; t < tetrahedra.size(); ++t) - { - const auto& tet = tetrahedra[t]; - output->addTetra(tet[0] + offset, tet[1] + offset, - tet[2] + offset, tet[3] + offset); - } + for (const auto& tet : l_inputs.get(srcIdx)->getTetrahedra()) + l_output->addTetra(tet[0] + offset, tet[1] + offset, tet[2] + offset, tet[3] + offset); } - // Phase 10: Concatenate hexahedra with offset remapping - for (std::size_t srcIdx = 0; srcIdx < numInputs; ++srcIdx) + // Concatenate hexahedra with offset remapping + for (sofa::Size srcIdx = 0; srcIdx < numInputs; ++srcIdx) { - BaseMeshTopology* input = l_inputTopologies.get(srcIdx); - const Index offset = m_pointOffsets[srcIdx]; - const auto& hexahedra = input->getHexahedra(); + const sofa::Size offset = m_pointOffsets[srcIdx]; - for (std::size_t h = 0; h < hexahedra.size(); ++h) + for (const auto& hex : l_inputs.get(srcIdx)->getHexahedra()) { - const auto& hex = hexahedra[h]; - output->addHexa(hex[0] + offset, hex[1] + offset, - hex[2] + offset, hex[3] + offset, - hex[4] + offset, hex[5] + offset, - hex[6] + offset, hex[7] + offset); + l_output->addHexa(hex[0] + offset, hex[1] + offset, hex[2] + offset, hex[3] + offset, + hex[4] + offset, hex[5] + offset, hex[6] + offset, hex[7] + offset); } } - msg_info() << "Merged " << numInputs << " topologies: " - << totalPoints << " points, " - << output->getNbEdges() << " edges, " - << output->getNbTriangles() << " triangles, " - << output->getNbQuads() << " quads, " - << output->getNbTetrahedra() << " tetrahedra, " - << output->getNbHexahedra() << " hexahedra."; + msg_info() << "Merged " << numInputs << " topologies: " << totalPoints << " points, " + << l_output->getNbEdges() << " edges, " << l_output->getNbTriangles() + << " triangles, " << l_output->getNbQuads() << " quads, " + << l_output->getNbTetrahedra() << " tetrahedra, " << l_output->getNbHexahedra() + << " hexahedra."; } void SubsetTopologicalMultiMapping::populateSubsetMultiMappingIndexPairs() { auto* targetObj = l_subsetMultiMapping.get(); - if (!targetObj) - return; + if (!targetObj) return; auto* targetData = targetObj->findData("indexPairs"); if (!targetData) @@ -260,8 +233,8 @@ void SubsetTopologicalMultiMapping::populateSubsetMultiMappingIndexPairs() targetData->copyValueFrom(&d_indexPairs); - msg_info() << "Set " << (d_indexPairs.getValue().size() / 2) - << " indexPairs on '" << targetObj->getName() << "'."; + msg_info() << "Set " << (d_indexPairs.getValue().size() / 2) << " indexPairs on '" + << targetObj->getName() << "'."; } -} // namespace sofa::component::topology::mapping +} // namespace sofa::component::topology::mapping diff --git a/Sofa/Component/Topology/Mapping/src/sofa/component/topology/mapping/SubsetTopologicalMultiMapping.h b/Sofa/Component/Topology/Mapping/src/sofa/component/topology/mapping/SubsetTopologicalMultiMapping.h index a27ae857d06..b2fc3fcdee1 100644 --- a/Sofa/Component/Topology/Mapping/src/sofa/component/topology/mapping/SubsetTopologicalMultiMapping.h +++ b/Sofa/Component/Topology/Mapping/src/sofa/component/topology/mapping/SubsetTopologicalMultiMapping.h @@ -1,27 +1,26 @@ /****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU Lesser General Public License as published by * -* the Free Software Foundation; either version 2.1 of the License, or (at * -* your option) any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * -* for more details. * -* * -* You should have received a copy of the GNU Lesser General Public License * -* along with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ + * SOFA, Simulation Open-Framework Architecture * + * (c) 2006 INRIA, USTL, UJF, CNRS, MGH * + * * + * This program is free software; you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as published by * + * the Free Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, but WITHOUT * + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * + * for more details. * + * * + * You should have received a copy of the GNU Lesser General Public License * + * along with this program. If not, see . * + ******************************************************************************* + * Authors: The SOFA Team and external contributors (see Authors.txt) * + * * + * Contact information: contact@sofa-framework.org * + ******************************************************************************/ #pragma once #include - #include #include @@ -30,22 +29,21 @@ namespace sofa::component::topology::mapping /** * @class SubsetTopologicalMultiMapping - * @brief Merges multiple input topologies (points, edges, triangles, quads, tetrahedra, hexahedra) into a single output topology. + * @brief Merges multiple input topologies into a single output topology. * - * This is the topological counterpart to SubsetMultiMapping on the mechanical side. - * For each input topology, points are concatenated with index offsets, and edges/triangles/quads/ - * tetrahedra/hexahedra are remapped accordingly. Triangle winding order can be reversed per source - * via flipNormals. Quad winding order is also reversed when flipping. + * For each input topology, points are concatenated with index offsets, and geometric primitives + * (edges/triangles/quads/tetrahedra/hexahedra are remapped accordingly. Triangle/quad winding + * order can be reversed per source via flipNormals. * - * The component can optionally auto-populate the indexPairs Data of a linked SubsetMultiMapping - * for mechanical-side coordination. + * This is the topological counterpart to SubsetMultiMapping. + * The component can optionally auto-populate the indexPairs Data of a linked SubsetMultiMapping. * - * Static only: merging is performed once during init(). Dynamic topology changes are not propagated. + * Merging is performed once during init(). Dynamic topology changes are not supported. */ class SOFA_COMPONENT_TOPOLOGY_MAPPING_API SubsetTopologicalMultiMapping : public sofa::core::objectmodel::BaseObject { -public: + public: SOFA_CLASS(SubsetTopologicalMultiMapping, sofa::core::objectmodel::BaseObject); using Index = sofa::Index; @@ -55,39 +53,38 @@ class SOFA_COMPONENT_TOPOLOGY_MAPPING_API SubsetTopologicalMultiMapping void reinit() override; /// N input topologies - MultiLink - l_inputTopologies; + sofa::core::objectmodel::BaseLink::FLAG_STRONGLINK> + l_inputs; /// Single output topology - SingleLink - l_outputTopology; + sofa::core::objectmodel::BaseLink::FLAG_STRONGLINK> + l_output; /// Optional link to a SubsetMultiMapping to auto-populate its indexPairs. - /// Uses BaseObject to avoid template dependency; validated at runtime. - SingleLink l_subsetMultiMapping; - Data> d_flipNormals; ///< Per-source flags to reverse triangle and quad winding order - Data> d_indexPairs; ///< Output: flat array of (source_index, coord_in_source) pairs for SubsetMultiMapping + Data> + d_flipNormals; ///< Per-source flags to reverse triangle and/or quad winding order + Data> + d_indexPairs; ///< Output: flat array of (source_index, coord_in_source) pairs for + ///< SubsetMultiMapping -protected: + protected: SubsetTopologicalMultiMapping(); ~SubsetTopologicalMultiMapping() override; -private: - void doMerge(); + private: + void mapTopologies(); void populateSubsetMultiMappingIndexPairs(); /// Per-source cumulative point offsets. m_pointOffsets[i] = total points from sources 0..i-1. sofa::type::vector m_pointOffsets; }; -} // namespace sofa::component::topology::mapping +} // namespace sofa::component::topology::mapping From 9b0d2497662538b0e1999a1e575bc6e53696b5bf Mon Sep 17 00:00:00 2001 From: Themis Skamagkis Date: Fri, 6 Feb 2026 15:02:32 +0100 Subject: [PATCH 5/6] [scene] Add an example for SubsetTopologicalMultiMapping --- .../Mapping/SubsetTopologicalMultiMapping.scn | 186 ++++++++++++++++++ 1 file changed, 186 insertions(+) create mode 100644 examples/Component/Topology/Mapping/SubsetTopologicalMultiMapping.scn diff --git a/examples/Component/Topology/Mapping/SubsetTopologicalMultiMapping.scn b/examples/Component/Topology/Mapping/SubsetTopologicalMultiMapping.scn new file mode 100644 index 00000000000..651f2db74d9 --- /dev/null +++ b/examples/Component/Topology/Mapping/SubsetTopologicalMultiMapping.scn @@ -0,0 +1,186 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 24ded1fd6e278624f452a2b1a27c56a3e6469cc2 Mon Sep 17 00:00:00 2001 From: Themis Skamagkis Date: Wed, 11 Feb 2026 16:43:00 +0100 Subject: [PATCH 6/6] [Mapping] Rely on a link to the indexPairs to couple mechanical - topological mappings [scenes] Update scene accordingly --- .../mapping/SubsetTopologicalMultiMapping.cpp | 30 +------------ .../mapping/SubsetTopologicalMultiMapping.h | 12 ++---- .../Mapping/SubsetTopologicalMultiMapping.scn | 42 +++++++------------ 3 files changed, 18 insertions(+), 66 deletions(-) diff --git a/Sofa/Component/Topology/Mapping/src/sofa/component/topology/mapping/SubsetTopologicalMultiMapping.cpp b/Sofa/Component/Topology/Mapping/src/sofa/component/topology/mapping/SubsetTopologicalMultiMapping.cpp index cec387951bc..05dc18bab92 100644 --- a/Sofa/Component/Topology/Mapping/src/sofa/component/topology/mapping/SubsetTopologicalMultiMapping.cpp +++ b/Sofa/Component/Topology/Mapping/src/sofa/component/topology/mapping/SubsetTopologicalMultiMapping.cpp @@ -32,17 +32,13 @@ void registerSubsetTopologicalMultiMapping(sofa::core::ObjectFactory* factory) core::ObjectRegistrationData("Merges multiple input topologies (points, edges, triangles, " "quads, tetrahedra, hexahedra) " "into a single output topology with index remapping. " - "Optionally populates indexPairs for " - "SubsetMultiMapping.") + "Exposes indexPairs Data for linking to SubsetMultiMapping.") .add()); } SubsetTopologicalMultiMapping::SubsetTopologicalMultiMapping() : l_inputs(initLink("input", "Input topology sources to merge")), l_output(initLink("output", "Output merged topology")), - l_subsetMultiMapping( - initLink("subsetMultiMapping", - "Optional link to a SubsetMultiMapping to auto-populate its indexPairs")), d_flipNormals( initData(&d_flipNormals, sofa::type::vector(), "flipNormals", "Per-source boolean flags to reverse triangle and quad winding order")), @@ -88,8 +84,6 @@ void SubsetTopologicalMultiMapping::init() mapTopologies(); - if (l_subsetMultiMapping.get()) populateSubsetMultiMappingIndexPairs(); - d_componentState.setValue(sofa::core::objectmodel::ComponentState::Valid); } @@ -98,8 +92,6 @@ void SubsetTopologicalMultiMapping::reinit() if (d_componentState.getValue() == sofa::core::objectmodel::ComponentState::Invalid) return; mapTopologies(); - - if (l_subsetMultiMapping.get()) populateSubsetMultiMappingIndexPairs(); } void SubsetTopologicalMultiMapping::mapTopologies() @@ -217,24 +209,4 @@ void SubsetTopologicalMultiMapping::mapTopologies() << " hexahedra."; } -void SubsetTopologicalMultiMapping::populateSubsetMultiMappingIndexPairs() -{ - auto* targetObj = l_subsetMultiMapping.get(); - if (!targetObj) return; - - auto* targetData = targetObj->findData("indexPairs"); - if (!targetData) - { - msg_error() << "Linked object '" << targetObj->getName() - << "' has no 'indexPairs' Data field. " - "Ensure it is a SubsetMultiMapping."; - return; - } - - targetData->copyValueFrom(&d_indexPairs); - - msg_info() << "Set " << (d_indexPairs.getValue().size() / 2) << " indexPairs on '" - << targetObj->getName() << "'."; -} - } // namespace sofa::component::topology::mapping diff --git a/Sofa/Component/Topology/Mapping/src/sofa/component/topology/mapping/SubsetTopologicalMultiMapping.h b/Sofa/Component/Topology/Mapping/src/sofa/component/topology/mapping/SubsetTopologicalMultiMapping.h index b2fc3fcdee1..fafaf746f45 100644 --- a/Sofa/Component/Topology/Mapping/src/sofa/component/topology/mapping/SubsetTopologicalMultiMapping.h +++ b/Sofa/Component/Topology/Mapping/src/sofa/component/topology/mapping/SubsetTopologicalMultiMapping.h @@ -36,7 +36,7 @@ namespace sofa::component::topology::mapping * order can be reversed per source via flipNormals. * * This is the topological counterpart to SubsetMultiMapping. - * The component can optionally auto-populate the indexPairs Data of a linked SubsetMultiMapping. + * The indexPairs output can be linked to a SubsetMultiMapping in scenes. * * Merging is performed once during init(). Dynamic topology changes are not supported. */ @@ -64,16 +64,11 @@ class SOFA_COMPONENT_TOPOLOGY_MAPPING_API SubsetTopologicalMultiMapping sofa::core::objectmodel::BaseLink::FLAG_STRONGLINK> l_output; - /// Optional link to a SubsetMultiMapping to auto-populate its indexPairs. - SingleLink - l_subsetMultiMapping; - Data> d_flipNormals; ///< Per-source flags to reverse triangle and/or quad winding order Data> - d_indexPairs; ///< Output: flat array of (source_index, coord_in_source) pairs for - ///< SubsetMultiMapping + d_indexPairs; ///< Output: flat array of (source_index, coord_in_source) pairs, + ///< linkable to SubsetMultiMapping protected: SubsetTopologicalMultiMapping(); @@ -81,7 +76,6 @@ class SOFA_COMPONENT_TOPOLOGY_MAPPING_API SubsetTopologicalMultiMapping private: void mapTopologies(); - void populateSubsetMultiMappingIndexPairs(); /// Per-source cumulative point offsets. m_pointOffsets[i] = total points from sources 0..i-1. sofa::type::vector m_pointOffsets; diff --git a/examples/Component/Topology/Mapping/SubsetTopologicalMultiMapping.scn b/examples/Component/Topology/Mapping/SubsetTopologicalMultiMapping.scn index 651f2db74d9..feac0cef0e4 100644 --- a/examples/Component/Topology/Mapping/SubsetTopologicalMultiMapping.scn +++ b/examples/Component/Topology/Mapping/SubsetTopologicalMultiMapping.scn @@ -1,13 +1,4 @@ - @@ -28,7 +19,6 @@ - @@ -47,15 +37,15 @@ - + output="@dofs" + indexPairs="@topoMapping.indexPairs" /> @@ -66,7 +56,6 @@ - - + output="@dofs" + indexPairs="@topoMapping.indexPairs" /> @@ -105,9 +94,7 @@ - - @@ -132,14 +119,14 @@ - + output="@topology" /> + output="@dofs" + indexPairs="@topoMapping.indexPairs" /> @@ -150,7 +137,6 @@ - @@ -166,14 +152,14 @@ - + output="@topology" /> + output="@dofs" + indexPairs="@topoMapping.indexPairs" />