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/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 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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..0b2059c85
--- /dev/null
+++ b/src/BeamAdapter/component/mapping/AdaptiveBeamVisualMapping.h
@@ -0,0 +1,79 @@
+#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;
+ bool m_bwdInitDone { false }; // m_beamMapping is usable only after bwdInit
+
+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_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)
+};
+
+#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..0539f5d2b
--- /dev/null
+++ b/src/BeamAdapter/component/mapping/AdaptiveBeamVisualMapping.inl
@@ -0,0 +1,349 @@
+#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_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
+ this->f_mapForces.setValue(false);
+ this->f_mapConstraints.setValue(false);
+ this->f_mapMasses.setValue(false);
+}
+
+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;
+ }
+
+ 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();
+ 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; // 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)
+ {
+ // 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 j = startPtId; j < nbrVisuEdges + 1; j++)
+ {
+ 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 e = prev_edges; e < prev_edges + nbrVisuEdges; e++)
+ {
+ m_internalEdges.emplace_back(e, e + 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 auto localCentersSize = static_cast(localCenters.size());
+ auto circlePoints = sofa::helper::getWriteOnlyAccessor(d_circlePoints);
+ const auto thickness = d_thickness.getValue();
+ const SReal rho = d_radius.getValue();
+ if(thickness == 0.0)
+ {
+ circlePoints.resize(N * localCentersSize);
+ for (sofa::Index i=0; i< localCentersSize; ++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);
+
+ // 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();
+
+}
+
+
+template
+void AdaptiveBeamVisualMapping::bwdInit()
+{
+ if (!this->isComponentStateValid())
+ return;
+
+ m_beamMapping->bwdInit();
+ m_bwdInitDone = true;
+
+ const auto N = d_nbPointsOnEachCircle.getValue();
+ const auto& edges = m_internalEdges;
+ const bool flipNormals = d_flipNormals.getValue();
+
+ if(l_quadTopology->getNbQuads() == 0)
+ {
+ if(d_thickness.getValue() == 0.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);
+ }
+ }
+ }
+ }
+ else
+ {
+ 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);
+ 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(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);
+ }
+ }
+ }
+}
+
+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->getMState();
+
+ // 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;
+ }
+
+ // 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
+
+ auto N = d_nbPointsOnEachCircle.getValue();
+
+ const auto& mappedCenters = m_centerState->readPositions();
+
+ out.clear();
+
+ auto circlePoints = sofa::helper::getReadAccessor(d_circlePoints);
+
+ if(d_thickness.getValue() == 0.0)
+ {
+ out.resize(mappedCenters.size() * N);
+ for (sofa::Index i=0; i< mappedCenters.size(); ++i)
+ {
+ const auto p0 = i;
+
+ for(unsigned int j=0; j