From 3044218528671d18c7b30cd178e91f03b1837e1b Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Wed, 7 May 2025 09:53:09 +0900 Subject: [PATCH 1/7] create mapping combining quad creation and beam mapping wip fix different xtip wip wip2 wip3 wip4 working mapping optimize computations add timer remove bogus test not setting a topology is not an error --- CMakeLists.txt | 3 + .../InterventionalRadiologyController.inl | 1 - .../component/engine/WireRestShape.inl | 13 +- .../mapping/AdaptiveBeamVisualMapping.cpp | 21 ++ .../mapping/AdaptiveBeamVisualMapping.h | 76 ++++++ .../mapping/AdaptiveBeamVisualMapping.inl | 227 ++++++++++++++++++ src/BeamAdapter/initBeamAdapter.cpp | 2 + 7 files changed, 339 insertions(+), 4 deletions(-) create mode 100644 src/BeamAdapter/component/mapping/AdaptiveBeamVisualMapping.cpp create mode 100644 src/BeamAdapter/component/mapping/AdaptiveBeamVisualMapping.h create mode 100644 src/BeamAdapter/component/mapping/AdaptiveBeamVisualMapping.inl diff --git a/CMakeLists.txt b/CMakeLists.txt index d136f5563..d8594e730 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,6 +50,8 @@ set(HEADER_FILES ${BEAMADAPTER_SRC}/component/mapping/AdaptiveBeamMapping.h ${BEAMADAPTER_SRC}/component/mapping/AdaptiveBeamMapping.inl + ${BEAMADAPTER_SRC}/component/mapping/AdaptiveBeamVisualMapping.h + ${BEAMADAPTER_SRC}/component/mapping/AdaptiveBeamVisualMapping.inl ${BEAMADAPTER_SRC}/component/mapping/BeamLengthMapping.h ${BEAMADAPTER_SRC}/component/mapping/BeamLengthMapping.inl ${BEAMADAPTER_SRC}/component/mapping/BeamProjectionDifferenceMultiMapping.h @@ -92,6 +94,7 @@ set(SOURCE_FILES ${BEAMADAPTER_SRC}/component/forcefield/AdaptiveInflatableBeamForceField.cpp ${BEAMADAPTER_SRC}/component/mapping/AdaptiveBeamMapping.cpp + ${BEAMADAPTER_SRC}/component/mapping/AdaptiveBeamVisualMapping.cpp ${BEAMADAPTER_SRC}/component/mapping/BeamLengthMapping.cpp ${BEAMADAPTER_SRC}/component/mapping/BeamProjectionDifferenceMultiMapping.cpp ${BEAMADAPTER_SRC}/component/mapping/MultiAdaptiveBeamMapping.cpp diff --git a/src/BeamAdapter/component/controller/InterventionalRadiologyController.inl b/src/BeamAdapter/component/controller/InterventionalRadiologyController.inl index 3595ef1ad..658da1003 100644 --- a/src/BeamAdapter/component/controller/InterventionalRadiologyController.inl +++ b/src/BeamAdapter/component/controller/InterventionalRadiologyController.inl @@ -234,7 +234,6 @@ void InterventionalRadiologyController::init() m_idInstrumentCurvAbsTable.push_back(listTool); } - type::vector listInit; for(unsigned int i=0; i::init() } else { - msg_error() << "Cannot find topology container. Please specify the link to the topology or insert one in the same node."; - this->d_componentState.setValue(sofa::core::objectmodel::ComponentState::Invalid); - return; + // force print this message (an important message which is not a warning) + const auto printLogValue = f_printLog.getValue(); + f_printLog.setValue(true); + msg_info() << "A topology container has not been set (or included in the same node). This will throw an error only if it is intended to be used with Edge2QuadTopologyContainer further in your scene."; + f_printLog.setValue(printLogValue); } if (l_sectionMaterials.empty()) @@ -142,6 +144,11 @@ void WireRestShape::initLengths() template bool WireRestShape::initTopology() { + if(!l_topology) + { + return false; + } + /// fill topology : l_topology->clear(); l_topology->cleanup(); diff --git a/src/BeamAdapter/component/mapping/AdaptiveBeamVisualMapping.cpp b/src/BeamAdapter/component/mapping/AdaptiveBeamVisualMapping.cpp new file mode 100644 index 000000000..64e530a8f --- /dev/null +++ b/src/BeamAdapter/component/mapping/AdaptiveBeamVisualMapping.cpp @@ -0,0 +1,21 @@ +#define BEAMADAPTER_COMPONENT_MAPPING_BEAM2QUADMAPPING_CPP + +#include + + +#include +#include +#include + +namespace beamadapter +{ + +void registerAdaptiveBeamVisualMapping(sofa::core::ObjectFactory* factory) +{ + factory->registerObjects(core::ObjectRegistrationData("Special mapping where quads are created from beams.") + .add< AdaptiveBeamVisualMapping >()); +} + +template class SOFA_BEAMADAPTER_API AdaptiveBeamVisualMapping; + +} // namespace beamadapter diff --git a/src/BeamAdapter/component/mapping/AdaptiveBeamVisualMapping.h b/src/BeamAdapter/component/mapping/AdaptiveBeamVisualMapping.h new file mode 100644 index 000000000..ac581cb36 --- /dev/null +++ b/src/BeamAdapter/component/mapping/AdaptiveBeamVisualMapping.h @@ -0,0 +1,76 @@ +#pragma once + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include + +namespace beamadapter +{ + +template +class AdaptiveBeamVisualMapping : public Mapping +{ +public: + SOFA_CLASS(SOFA_TEMPLATE2(AdaptiveBeamVisualMapping, InputDataTypes, OutputDataTypes), SOFA_TEMPLATE2(Mapping, InputDataTypes, OutputDataTypes)); + + using Inherit = Mapping; + + using Index = sofa::Index; + using Edge = sofa::core::topology::BaseMeshTopology::Edge; + using SeqEdges = sofa::core::topology::BaseMeshTopology::SeqEdges; + using Quad = sofa::core::topology::BaseMeshTopology::Quad; + using SeqQuads = sofa::core::topology::BaseMeshTopology::SeqQuads; + using VecIndex = sofa::type::vector; + using InCoord = typename InputDataTypes::Coord; + using InVecCoord = typename InputDataTypes::VecCoord; + using InVecDeriv = typename InputDataTypes::VecDeriv; + using InMatrixDeriv = typename OutputDataTypes::MatrixDeriv; + using OutCoord = typename OutputDataTypes::Coord; + using OutVecCoord = typename OutputDataTypes::VecCoord; + using OutVecDeriv = typename OutputDataTypes::VecDeriv; + using OutMatrixDeriv = typename OutputDataTypes::MatrixDeriv; + +protected: + AdaptiveBeamVisualMapping(); + ~AdaptiveBeamVisualMapping() override = default; + + SeqEdges m_internalEdges; + typename sofa::component::statecontainer::MappedObject::SPtr m_centerState; // state by itself should be sufficient... + typename AdaptiveBeamMapping::SPtr m_beamMapping; + +public: + void init() override; + void bwdInit() override; + void apply(const MechanicalParams* mparams, Data& out, const Data& in) override; + void applyJ(const MechanicalParams*, Data& , const Data& ) override {}; + void applyJT(const MechanicalParams*, Data& , const Data& ) override {}; + //void applyJT(const core::ConstraintParams*, Data&, const Data&) override {}; + + //inputs + SingleLink, WireBeamInterpolation, BaseLink::FLAG_STOREPATH|BaseLink::FLAG_STRONGLINK> l_wireBeamInterpolation; + SingleLink, sofa::core::topology::BaseMeshTopology, BaseLink::FLAG_STOREPATH|BaseLink::FLAG_STRONGLINK> l_quadTopology; + + // internal data + Data d_localCenters; + Data d_circlePoints; + + // parameters + Data d_nbPointsOnEachCircle; ///< Discretization of created circles + Data d_radius; ///< Radius of created circles in yz plan + Data d_flipNormals; ///< Flip Normal ? (Inverse point order when creating quad) +}; + +#if !defined(BEAMADAPTER_COMPONENT_MAPPING_BEAM2QUADMAPPING_CPP) +extern template class SOFA_BEAMADAPTER_API Beam2QuadMapping; +#endif + +} // namespace beamadapter diff --git a/src/BeamAdapter/component/mapping/AdaptiveBeamVisualMapping.inl b/src/BeamAdapter/component/mapping/AdaptiveBeamVisualMapping.inl new file mode 100644 index 000000000..59a6395b2 --- /dev/null +++ b/src/BeamAdapter/component/mapping/AdaptiveBeamVisualMapping.inl @@ -0,0 +1,227 @@ +#pragma once + +#include + +#include +#include +#include + +#include +#include + +#include +#include + +namespace beamadapter +{ + +using Vec3 = sofa::type::Vec3; + +template +AdaptiveBeamVisualMapping::AdaptiveBeamVisualMapping() + : Inherit() + , m_beamMapping() + , l_wireBeamInterpolation(initLink("interpolation", "Path to the Interpolation component on scene")) + , l_quadTopology(initLink("quadTopology", "Path to the Quad topology")) + , d_nbPointsOnEachCircle( initData(&d_nbPointsOnEachCircle, "nbPointsOnEachCircle", "Discretization of created circles")) + , d_radius( initData(&d_radius, 1_sreal, "radius", "Radius of created circles in yz plan")) + , d_flipNormals(initData(&d_flipNormals, bool(false), "flipNormals", "Flip Normal ? (Inverse point order when creating quad)")) +{ +} + +template +void AdaptiveBeamVisualMapping::init() +{ + this->d_componentState.setValue(sofa::core::objectmodel::ComponentState::Invalid); + + if (!d_radius.isSet()) + { + msg_error() << "No radius defined"; + return; + } + + if (d_radius.isSet() && d_radius.getValue() < std::numeric_limits::min()) + { + msg_error() << "Radius is zero or negative"; + return; + } + + if (!d_nbPointsOnEachCircle.isSet()) + { + msg_error() << "nbPointsOnEachCircle is not defined"; + return; + } + + if (!l_wireBeamInterpolation) + { + msg_error() <<"No Beam Interpolation found, the component can not work."; + return; + } + + if (!l_quadTopology) + { + msg_error() <<"No Quad Topology found, the component can not work."; + return; + } + + + auto* wireShape = l_wireBeamInterpolation->m_restShape.get(); + assert(wireShape); + + // create internal topology at rest + m_centerState = sofa::core::objectmodel::New>(); + this->addSlave(m_centerState); + + auto localCenters = sofa::helper::getWriteOnlyAccessor(d_localCenters); + localCenters.clear(); + m_internalEdges.clear(); + + SReal prev_length = 0.0; + sofa::Index prev_edges = 0; + sofa::Index startPtId = 0; + const auto& keyPts = wireShape->d_keyPoints.getValue(); + for (sofa::Size i = 0; i < wireShape->l_sectionMaterials.size(); ++i) + { + // Add topology of the material + sofa::Size nbrVisuEdges = wireShape->l_sectionMaterials.get(i)->getNbVisualEdges(); + SReal length = fabs(keyPts[i + 1] - keyPts[i]); + SReal dx = length / nbrVisuEdges; + + // add points from the material + for (sofa::Index i = startPtId; i < nbrVisuEdges + 1; i++) + { + const InCoord p({prev_length + i * dx, 0.0, 0.0}, {0.0, 0.0, 0.0, 1.0}); + localCenters.push_back(p); + } + + // add segments from the material + for (sofa::Index i = prev_edges; i < prev_edges + nbrVisuEdges; i++) + { + m_internalEdges.emplace_back(i, i + 1); + } + + prev_length = length; + prev_edges = nbrVisuEdges; + startPtId = 1; // Assume the last point of mat[n] == first point of mat[n+1] + } + + auto mappedCenters = m_centerState->writeOnlyPositions(); + mappedCenters.wref() = localCenters; // initialize state + + // Cache the created vertices + constexpr Vec3 Y0 {0.0_sreal, 1.0_sreal, 0.0_sreal}; + constexpr Vec3 Z0 {0.0_sreal, 0.0_sreal, 1.0_sreal}; + const auto N = d_nbPointsOnEachCircle.getValue(); + const SReal rho = d_radius.getValue(); + + auto circlePoints = sofa::helper::getWriteOnlyAccessor(d_circlePoints); + circlePoints.resize(N * localCenters.size()); + for (sofa::Index i=0; i< localCenters.size(); ++i) + { + const auto p0 = i; + + for(unsigned int j=0; j>(this->fromModel, m_centerState.get(), l_wireBeamInterpolation.get()); + m_beamMapping->init(); + this->addSlave(m_beamMapping); + + Inherit::init(); + + this->d_componentState.setValue(sofa::core::objectmodel::ComponentState::Valid); + +} + + +template +void AdaptiveBeamVisualMapping::bwdInit() +{ + if (!this->isComponentStateValid()) + return; + + m_beamMapping->bwdInit(); + + const auto N = d_nbPointsOnEachCircle.getValue(); + const auto& edges = m_internalEdges; + + if(l_quadTopology->getNbQuads() == 0) + { + for(const auto& edge : edges) + { + const Index p0 = edge[0]; + const Index p1 = edge[1]; + + for(unsigned int j=0; jaddQuad(q3, q2, q1, q0); + } + else + { + l_quadTopology->addQuad(q0, q1, q2, q3); + } + } + } + } + +} + +template +void AdaptiveBeamVisualMapping::apply(const MechanicalParams* mparams, Data& dOut, const Data& dIn) +{ + SCOPED_TIMER("AdaptiveBeamVisualMapping_Apply"); + + if (!this->isComponentStateValid()) + return; + + const auto* beamState = this->l_wireBeamInterpolation->m_mstate; + + // workaround for l_wireBeamInterpolation->m_mstate is nullptr until bwdinit + if (!beamState) + { + return; + } + + // final quads positions + auto out = sofa::helper::getWriteOnlyAccessor(dOut); + + // 1 - apply the mapping on the circle "centers" from the beams, using the classical AdaptiveBeamMapping + auto toBeMappedCenters = m_centerState->writeOnlyPositions(); + auto localCenters = sofa::helper::getReadAccessor(d_localCenters); + toBeMappedCenters.wref() = localCenters; // initialize state + + m_beamMapping->apply(mparams, *m_centerState->write(core::vec_id::write_access::position), dIn); + + // 2 - create the points around the centers + const auto N = d_nbPointsOnEachCircle.getValue(); + + const auto& mappedCenters = m_centerState->readPositions(); + + out.clear(); + out.resize(mappedCenters.size() * N); + + auto circlePoints = sofa::helper::getReadAccessor(d_circlePoints); + for (sofa::Index i=0; i< mappedCenters.size(); ++i) + { + const auto p0 = i; + + for(unsigned int j=0; j Date: Wed, 14 May 2025 15:40:15 +0900 Subject: [PATCH 2/7] add thickness parameter --- .../mapping/AdaptiveBeamVisualMapping.h | 2 + .../mapping/AdaptiveBeamVisualMapping.inl | 144 ++++++++++++++---- 2 files changed, 116 insertions(+), 30 deletions(-) diff --git a/src/BeamAdapter/component/mapping/AdaptiveBeamVisualMapping.h b/src/BeamAdapter/component/mapping/AdaptiveBeamVisualMapping.h index ac581cb36..90eb577f8 100644 --- a/src/BeamAdapter/component/mapping/AdaptiveBeamVisualMapping.h +++ b/src/BeamAdapter/component/mapping/AdaptiveBeamVisualMapping.h @@ -66,6 +66,8 @@ class AdaptiveBeamVisualMapping : public Mapping d_nbPointsOnEachCircle; ///< Discretization of created circles Data d_radius; ///< Radius of created circles in yz plan + Data d_thickness; ///< if not 0, add a second layer (where this value will be the distance between the two layers) + ///< Data d_flipNormals; ///< Flip Normal ? (Inverse point order when creating quad) }; diff --git a/src/BeamAdapter/component/mapping/AdaptiveBeamVisualMapping.inl b/src/BeamAdapter/component/mapping/AdaptiveBeamVisualMapping.inl index 59a6395b2..bd6226082 100644 --- a/src/BeamAdapter/component/mapping/AdaptiveBeamVisualMapping.inl +++ b/src/BeamAdapter/component/mapping/AdaptiveBeamVisualMapping.inl @@ -25,6 +25,7 @@ AdaptiveBeamVisualMapping::AdaptiveBeamVisualMa , l_quadTopology(initLink("quadTopology", "Path to the Quad topology")) , d_nbPointsOnEachCircle( initData(&d_nbPointsOnEachCircle, "nbPointsOnEachCircle", "Discretization of created circles")) , d_radius( initData(&d_radius, 1_sreal, "radius", "Radius of created circles in yz plan")) + , d_thickness( initData(&d_thickness, 0_sreal, "thickness", "if not 0, add a second layer (where this value will be the distance between the two layers)")) , d_flipNormals(initData(&d_flipNormals, bool(false), "flipNormals", "Flip Normal ? (Inverse point order when creating quad)")) { } @@ -63,6 +64,12 @@ void AdaptiveBeamVisualMapping::init() msg_error() <<"No Quad Topology found, the component can not work."; return; } + + if(d_radius.getValue() < d_thickness.getValue()) + { + msg_error() << "Thickness ( " << d_thickness.getValue() << " ) cannot be bigger that the radius (" << d_radius.getValue() << " )."; + return; + } auto* wireShape = l_wireBeamInterpolation->m_restShape.get(); @@ -112,20 +119,43 @@ void AdaptiveBeamVisualMapping::init() constexpr Vec3 Y0 {0.0_sreal, 1.0_sreal, 0.0_sreal}; constexpr Vec3 Z0 {0.0_sreal, 0.0_sreal, 1.0_sreal}; const auto N = d_nbPointsOnEachCircle.getValue(); - const SReal rho = d_radius.getValue(); + const auto localCentersSize = static_cast(localCenters.size()); auto circlePoints = sofa::helper::getWriteOnlyAccessor(d_circlePoints); - circlePoints.resize(N * localCenters.size()); - for (sofa::Index i=0; i< localCenters.size(); ++i) + const auto thickness = d_thickness.getValue(); + const SReal rho = d_radius.getValue(); + if(thickness == 0.0) { - const auto p0 = i; - - for(unsigned int j=0; j>(this->fromModel, m_centerState.get(), l_wireBeamInterpolation.get()); m_beamMapping->init(); this->addSlave(m_beamMapping); @@ -150,30 +180,64 @@ void AdaptiveBeamVisualMapping::bwdInit() if(l_quadTopology->getNbQuads() == 0) { - for(const auto& edge : edges) + if(d_thickness.getValue() == 0.0) { - const Index p0 = edge[0]; - const Index p1 = edge[1]; - - for(unsigned int j=0; jaddQuad(q3, q2, q1, q0); + const Index q0 = p0*N+j; + const Index q1 = p1*N+j; + const Index q2 = p1*N+((j+1)%N); + const Index q3 = p0*N+((j+1)%N); + + if (d_flipNormals.getValue()) + { + l_quadTopology->addQuad(q3, q2, q1, q0); + } + else + { + l_quadTopology->addQuad(q0, q1, q2, q3); + } } - else + } + } + else + { + for(const auto& edge : edges) + { + const Index p0 = edge[0]; + const Index p1 = edge[1]; + + for(unsigned int j=0; jaddQuad(q0, q1, q2, q3); + const Index q0 = p0*2*N+j; + const Index q1 = p1*2*N+j; + const Index q2 = p1*2*N+((j+1)%N); + const Index q3 = p0*2*N+((j+1)%N); + + const Index q4 = p0*2*N+j + N; + const Index q5 = p1*2*N+j + N; + const Index q6 = p1*2*N+((j+1)%N) + N; + const Index q7 = p0*2*N+((j+1)%N) + N; + + if (d_flipNormals.getValue()) + { + l_quadTopology->addQuad(q3, q2, q1, q0); + l_quadTopology->addQuad(q7, q6, q5, q4); + } + else + { + l_quadTopology->addQuad(q0, q1, q2, q3); + l_quadTopology->addQuad(q4, q5, q6, q7); + } } } } } - } template @@ -203,24 +267,44 @@ void AdaptiveBeamVisualMapping::apply(const Mec m_beamMapping->apply(mparams, *m_centerState->write(core::vec_id::write_access::position), dIn); // 2 - create the points around the centers - const auto N = d_nbPointsOnEachCircle.getValue(); + + auto N = d_nbPointsOnEachCircle.getValue(); const auto& mappedCenters = m_centerState->readPositions(); out.clear(); - out.resize(mappedCenters.size() * N); - + auto circlePoints = sofa::helper::getReadAccessor(d_circlePoints); - for (sofa::Index i=0; i< mappedCenters.size(); ++i) + + if(d_thickness.getValue() == 0.0) { - const auto p0 = i; + out.resize(mappedCenters.size() * N); + for (sofa::Index i=0; i< mappedCenters.size(); ++i) + { + const auto p0 = i; - for(unsigned int j=0; j Date: Tue, 20 May 2025 08:53:07 +0900 Subject: [PATCH 3/7] fix normals for second layer and add quads to close extremities (useful for xray) --- .../mapping/AdaptiveBeamVisualMapping.inl | 35 ++++++++++++++++--- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/src/BeamAdapter/component/mapping/AdaptiveBeamVisualMapping.inl b/src/BeamAdapter/component/mapping/AdaptiveBeamVisualMapping.inl index bd6226082..586ec06c8 100644 --- a/src/BeamAdapter/component/mapping/AdaptiveBeamVisualMapping.inl +++ b/src/BeamAdapter/component/mapping/AdaptiveBeamVisualMapping.inl @@ -177,6 +177,7 @@ void AdaptiveBeamVisualMapping::bwdInit() const auto N = d_nbPointsOnEachCircle.getValue(); const auto& edges = m_internalEdges; + const bool flipNormals = d_flipNormals.getValue(); if(l_quadTopology->getNbQuads() == 0) { @@ -194,7 +195,7 @@ void AdaptiveBeamVisualMapping::bwdInit() const Index q2 = p1*N+((j+1)%N); const Index q3 = p0*N+((j+1)%N); - if (d_flipNormals.getValue()) + if (flipNormals) { l_quadTopology->addQuad(q3, q2, q1, q0); } @@ -224,18 +225,42 @@ void AdaptiveBeamVisualMapping::bwdInit() const Index q6 = p1*2*N+((j+1)%N) + N; const Index q7 = p0*2*N+((j+1)%N) + N; - if (d_flipNormals.getValue()) + if (flipNormals) { l_quadTopology->addQuad(q3, q2, q1, q0); - l_quadTopology->addQuad(q7, q6, q5, q4); + l_quadTopology->addQuad(q4, q5, q6, q7); // normal of the inner layer must be inverted from the outer layer } else { l_quadTopology->addQuad(q0, q1, q2, q3); - l_quadTopology->addQuad(q4, q5, q6, q7); + l_quadTopology->addQuad(q7, q6, q5, q4); } } } + + + // closing the extremities + const sofa::Size offset = static_cast(edges.size() * N * 2); + if(flipNormals) + { + for(unsigned int j=0; jaddQuad(j+N, j+N+1, j+1, j); + l_quadTopology->addQuad(offset + j, offset+j+1, offset+j+N+1, offset+j+N); + } + l_quadTopology->addQuad(N+N-1, N, 0, N-1); + l_quadTopology->addQuad(offset+N-1, offset+0, offset+N, offset+N+N-1); + } + else + { + for(unsigned int j=0; jaddQuad(j, j+1, j+N+1, j+N); + l_quadTopology->addQuad(offset+j+N, offset+j+N+1, offset+j+1, offset + j); + } + l_quadTopology->addQuad(N-1, 0, N, N+N-1); + l_quadTopology->addQuad(offset+N+N-1, offset+N, offset+0, offset+N-1); + } } } } @@ -248,7 +273,7 @@ void AdaptiveBeamVisualMapping::apply(const Mec if (!this->isComponentStateValid()) return; - const auto* beamState = this->l_wireBeamInterpolation->m_mstate; + const auto* beamState = this->l_wireBeamInterpolation->getMState(); // workaround for l_wireBeamInterpolation->m_mstate is nullptr until bwdinit if (!beamState) From 31e5a32d484b61edb05f777bc9d52e7c2e53522e Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Thu, 30 Jul 2026 10:19:43 +0900 Subject: [PATCH 4/7] fix length computations and loop index --- .../mapping/AdaptiveBeamVisualMapping.inl | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/BeamAdapter/component/mapping/AdaptiveBeamVisualMapping.inl b/src/BeamAdapter/component/mapping/AdaptiveBeamVisualMapping.inl index 586ec06c8..53d7806f0 100644 --- a/src/BeamAdapter/component/mapping/AdaptiveBeamVisualMapping.inl +++ b/src/BeamAdapter/component/mapping/AdaptiveBeamVisualMapping.inl @@ -83,8 +83,8 @@ void AdaptiveBeamVisualMapping::init() localCenters.clear(); m_internalEdges.clear(); - SReal prev_length = 0.0; - sofa::Index prev_edges = 0; + SReal prev_length = 0.0; // cumulated length of the previous materials + sofa::Index prev_edges = 0; // cumulated number of edges of the previous materials sofa::Index startPtId = 0; const auto& keyPts = wireShape->d_keyPoints.getValue(); for (sofa::Size i = 0; i < wireShape->l_sectionMaterials.size(); ++i) @@ -95,20 +95,20 @@ void AdaptiveBeamVisualMapping::init() SReal dx = length / nbrVisuEdges; // add points from the material - for (sofa::Index i = startPtId; i < nbrVisuEdges + 1; i++) + for (sofa::Index j = startPtId; j < nbrVisuEdges + 1; j++) { - const InCoord p({prev_length + i * dx, 0.0, 0.0}, {0.0, 0.0, 0.0, 1.0}); + const InCoord p({prev_length + j * dx, 0.0, 0.0}, {0.0, 0.0, 0.0, 1.0}); localCenters.push_back(p); } // add segments from the material - for (sofa::Index i = prev_edges; i < prev_edges + nbrVisuEdges; i++) + for (sofa::Index e = prev_edges; e < prev_edges + nbrVisuEdges; e++) { - m_internalEdges.emplace_back(i, i + 1); + m_internalEdges.emplace_back(e, e + 1); } - prev_length = length; - prev_edges = nbrVisuEdges; + prev_length += length; + prev_edges += nbrVisuEdges; startPtId = 1; // Assume the last point of mat[n] == first point of mat[n+1] } From 7c2fcf0eea0b74d5ab5a1ecb8560f10ae81a90f0 Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Thu, 30 Jul 2026 11:12:01 +0900 Subject: [PATCH 5/7] add example (using 3instrument_collis.scn) --- examples/3instruments_collis_vmap.scn | 152 ++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 examples/3instruments_collis_vmap.scn diff --git a/examples/3instruments_collis_vmap.scn b/examples/3instruments_collis_vmap.scn new file mode 100644 index 000000000..fca80c7de --- /dev/null +++ b/examples/3instruments_collis_vmap.scn @@ -0,0 +1,152 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 1caab814280b2a4d51bd981375a5711d1f6a5850 Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Tue, 25 Aug 2026 10:18:12 +0900 Subject: [PATCH 6/7] AdaptiveBeamVisualMapping: declare non-mechanical in the constructor The mapping only implements apply() (applyJ/applyJT are empty), so it must never be treated as a mechanical mapping. Setting mapForces/mapConstraints/ mapMasses in the scene file is too late: Node::addObject categorizes the mapping before parse() reads the attributes. With the defaults false, the mapping can feed a MechanicalObject (e.g. an X-ray tube surface) without the constraint pass emitting an applyJT error every step. --- .../component/mapping/AdaptiveBeamVisualMapping.inl | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/BeamAdapter/component/mapping/AdaptiveBeamVisualMapping.inl b/src/BeamAdapter/component/mapping/AdaptiveBeamVisualMapping.inl index 53d7806f0..f135ce2db 100644 --- a/src/BeamAdapter/component/mapping/AdaptiveBeamVisualMapping.inl +++ b/src/BeamAdapter/component/mapping/AdaptiveBeamVisualMapping.inl @@ -28,6 +28,12 @@ AdaptiveBeamVisualMapping::AdaptiveBeamVisualMa , d_thickness( initData(&d_thickness, 0_sreal, "thickness", "if not 0, add a second layer (where this value will be the distance between the two layers)")) , d_flipNormals(initData(&d_flipNormals, bool(false), "flipNormals", "Flip Normal ? (Inverse point order when creating quad)")) { + // Geometry-only mapping: applyJ/applyJT are not implemented, so it must + // never be treated as a mechanical mapping (also allows mapping into a + // MechanicalObject without the constraint pass spamming errors). + this->f_mapForces.setValue(false); + this->f_mapConstraints.setValue(false); + this->f_mapMasses.setValue(false); } template From ea8fed43314b5ba06e784c8f686eeddb713b08ad Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Tue, 25 Aug 2026 10:52:20 +0900 Subject: [PATCH 7/7] fix bwdinit/init shenaningans --- .../mapping/AdaptiveBeamVisualMapping.h | 1 + .../mapping/AdaptiveBeamVisualMapping.inl | 29 ++++++++++++------- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/BeamAdapter/component/mapping/AdaptiveBeamVisualMapping.h b/src/BeamAdapter/component/mapping/AdaptiveBeamVisualMapping.h index 90eb577f8..0b2059c85 100644 --- a/src/BeamAdapter/component/mapping/AdaptiveBeamVisualMapping.h +++ b/src/BeamAdapter/component/mapping/AdaptiveBeamVisualMapping.h @@ -46,6 +46,7 @@ class AdaptiveBeamVisualMapping : public Mapping::SPtr m_centerState; // state by itself should be sufficient... typename AdaptiveBeamMapping::SPtr m_beamMapping; + bool m_bwdInitDone { false }; // m_beamMapping is usable only after bwdInit public: void init() override; diff --git a/src/BeamAdapter/component/mapping/AdaptiveBeamVisualMapping.inl b/src/BeamAdapter/component/mapping/AdaptiveBeamVisualMapping.inl index f135ce2db..0539f5d2b 100644 --- a/src/BeamAdapter/component/mapping/AdaptiveBeamVisualMapping.inl +++ b/src/BeamAdapter/component/mapping/AdaptiveBeamVisualMapping.inl @@ -28,9 +28,7 @@ AdaptiveBeamVisualMapping::AdaptiveBeamVisualMa , d_thickness( initData(&d_thickness, 0_sreal, "thickness", "if not 0, add a second layer (where this value will be the distance between the two layers)")) , d_flipNormals(initData(&d_flipNormals, bool(false), "flipNormals", "Flip Normal ? (Inverse point order when creating quad)")) { - // Geometry-only mapping: applyJ/applyJT are not implemented, so it must - // never be treated as a mechanical mapping (also allows mapping into a - // MechanicalObject without the constraint pass spamming errors). + // Geometry-only mapping this->f_mapForces.setValue(false); this->f_mapConstraints.setValue(false); this->f_mapMasses.setValue(false); @@ -165,11 +163,13 @@ void AdaptiveBeamVisualMapping::init() m_beamMapping = sofa::core::objectmodel::New>(this->fromModel, m_centerState.get(), l_wireBeamInterpolation.get()); m_beamMapping->init(); this->addSlave(m_beamMapping); - - Inherit::init(); - + + // Must be Valid before Inherit::init(): Mapping::init() calls apply(), which + // sizes the output (pre-bwdInit branch) only for a valid component. this->d_componentState.setValue(sofa::core::objectmodel::ComponentState::Valid); - + + Inherit::init(); + } @@ -180,7 +180,8 @@ void AdaptiveBeamVisualMapping::bwdInit() return; m_beamMapping->bwdInit(); - + m_bwdInitDone = true; + const auto N = d_nbPointsOnEachCircle.getValue(); const auto& edges = m_internalEdges; const bool flipNormals = d_flipNormals.getValue(); @@ -280,10 +281,16 @@ void AdaptiveBeamVisualMapping::apply(const Mec return; const auto* beamState = this->l_wireBeamInterpolation->getMState(); - - // workaround for l_wireBeamInterpolation->m_mstate is nullptr until bwdinit - if (!beamState) + + // Before bwdInit, the internal beam mapping has no points yet (and the beam + // state may still be nullptr): only size the output. + if (!beamState || !m_bwdInitDone) { + auto out = sofa::helper::getWriteOnlyAccessor(dOut); + const auto N = d_nbPointsOnEachCircle.getValue(); + const auto nCenters = sofa::helper::getReadAccessor(d_localCenters).size(); + const std::size_t layers = (d_thickness.getValue() == 0.0) ? 1 : 2; + out.resize(nCenters * N * layers); // zero-filled: degenerate, invisible return; }