From d1b0952c73607f7ea9e67c519349c987f4b4cf9e Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Mon, 31 Mar 2025 14:07:05 +0900 Subject: [PATCH 01/24] fix warnings --- .../component/BaseBeamInterpolation.inl | 5 ++--- src/BeamAdapter/component/BeamInterpolation.h | 21 ++++++++++++++----- .../component/BeamInterpolation.inl | 10 +++++---- .../component/WireBeamInterpolation.inl | 3 ++- .../component/engine/WireRestShape.inl | 2 +- .../AdaptiveBeamForceFieldAndMass.inl | 6 ++++-- .../AdaptiveInflatableBeamForceField.inl | 1 - .../component/mapping/AdaptiveBeamMapping.inl | 4 ++-- src/BeamAdapter/utils/BeamActions.h | 5 ++++- 9 files changed, 37 insertions(+), 20 deletions(-) diff --git a/src/BeamAdapter/component/BaseBeamInterpolation.inl b/src/BeamAdapter/component/BaseBeamInterpolation.inl index 233bba526..9fd21a2a9 100644 --- a/src/BeamAdapter/component/BaseBeamInterpolation.inl +++ b/src/BeamAdapter/component/BaseBeamInterpolation.inl @@ -97,7 +97,8 @@ void BaseBeamInterpolation::RotateFrameForAlignNormalizedX(const Quat template BaseBeamInterpolation::BaseBeamInterpolation(/*sofa::component::engine::WireRestShape *_restShape*/) - : d_edgeList(initData(&d_edgeList, "edgeList", "list of the edge in the topology that are concerned by the Interpolation")) + : m_StateNodes(sofa::core::objectmodel::New< sofa::component::statecontainer::MechanicalObject >()) + , d_edgeList(initData(&d_edgeList, "edgeList", "list of the edge in the topology that are concerned by the Interpolation")) , d_lengthList(initData(&d_lengthList, "lengthList", "list of the length of each beam")) , d_DOF0TransformNode0(initData(&d_DOF0TransformNode0, "DOF0TransformNode0", "Optional rigid transformation between the degree of Freedom and the first node of the beam")) , d_DOF1TransformNode1(initData(&d_DOF1TransformNode1, "DOF1TransformNode1", "Optional rigid transformation between the degree of Freedom and the second node of the beam")) @@ -107,7 +108,6 @@ BaseBeamInterpolation::BaseBeamInterpolation(/*sofa::component::engin "if false, a transformation for each beam is computed between the DOF and the beam nodes")) , m_topology(nullptr) , m_mstate(nullptr) - , m_StateNodes(sofa::core::objectmodel::New< sofa::component::statecontainer::MechanicalObject >()) { @@ -147,7 +147,6 @@ void BaseBeamInterpolation::bwdInit() } m_topologyEdges = &m_topology->getEdges(); - Size nbEdges = m_topology->getNbEdges(); } diff --git a/src/BeamAdapter/component/BeamInterpolation.h b/src/BeamAdapter/component/BeamInterpolation.h index 9e367d5f8..2e17b70df 100644 --- a/src/BeamAdapter/component/BeamInterpolation.h +++ b/src/BeamAdapter/component/BeamInterpolation.h @@ -149,8 +149,19 @@ class BeamInterpolation : public BaseBeamInterpolation /// computeActualLength => given the 4 control points of the spline, it provides an estimate of the length (using gauss points integration) - virtual void getCurvAbsAtBeam(const unsigned int& edgeInList_input, const Real& baryCoord_input, Real& x_output) {} - virtual void getBeamAtCurvAbs(const Real& x_input, unsigned int& edgeInList_output, Real& baryCoord_output, unsigned int start = 0) {} + virtual void getCurvAbsAtBeam(const unsigned int& edgeInList_input, const Real& baryCoord_input, Real& x_output) override + { + SOFA_UNUSED(edgeInList_input); + SOFA_UNUSED(baryCoord_input); + SOFA_UNUSED(x_output); + } + virtual void getBeamAtCurvAbs(const Real& x_input, unsigned int& edgeInList_output, Real& baryCoord_output, unsigned int start = 0) override + { + SOFA_UNUSED(x_input); + SOFA_UNUSED(edgeInList_output); + SOFA_UNUSED(baryCoord_output); + SOFA_UNUSED(start); + } Data crossSectionShape; @@ -182,10 +193,10 @@ class BeamInterpolation : public BaseBeamInterpolation Data d_straight; - virtual void clear(); - virtual void addBeam(const BaseMeshTopology::EdgeID &eID , const Real &length, const Real &x0, const Real &x1, const Real &angle); + virtual void clear() override; + virtual void addBeam(const BaseMeshTopology::EdgeID &eID , const Real &length, const Real &x0, const Real &x1, const Real &angle) override; virtual void getSamplingParameters(type::vector& xP_noticeable, - type::vector& nbP_density) ; + type::vector& nbP_density) override; Real getRestTotalLength() override; void getCollisionSampling(Real &dx, const Real& x_localcurv_abs) override; void getNumberOfCollisionSegment(Real &dx, unsigned int &numLines) override; diff --git a/src/BeamAdapter/component/BeamInterpolation.inl b/src/BeamAdapter/component/BeamInterpolation.inl index ecf325c99..16d8aeb91 100644 --- a/src/BeamAdapter/component/BeamInterpolation.inl +++ b/src/BeamAdapter/component/BeamInterpolation.inl @@ -149,13 +149,13 @@ void BeamInterpolation::computeCrossSectionInertiaMatrix() { const auto& lengthY = helper::getReadAccessor(d_lengthY); const auto& lengthZ = helper::getReadAccessor(d_lengthZ); - for (int beamId=0; beamId::getInterpolationParameters(sofa::Index beamId template void BeamInterpolation::getMechanicalParameters(sofa::Index beamId, Real& youngModulus, Real& cPoisson, Real& massDensity) { + SOFA_UNUSED(massDensity); + const auto& defaultYoungModuli = d_defaultYoungModulus.getValue(); - youngModulus = (beamId < int(defaultYoungModuli.size()))? defaultYoungModuli[beamId]: m_defaultYoungModulus; + youngModulus = (beamId < static_cast(defaultYoungModuli.size()))? defaultYoungModuli[beamId]: m_defaultYoungModulus; const auto& poissonRatios = d_poissonRatio.getValue(); - cPoisson = (beamId < int(defaultYoungModuli.size()))? poissonRatios[beamId]: m_defaultPoissonRatio; + cPoisson = (beamId < static_cast(defaultYoungModuli.size()))? poissonRatios[beamId]: m_defaultPoissonRatio; //TODO: massDensity?? } diff --git a/src/BeamAdapter/component/WireBeamInterpolation.inl b/src/BeamAdapter/component/WireBeamInterpolation.inl index c57b7ee3f..bc1beaf83 100644 --- a/src/BeamAdapter/component/WireBeamInterpolation.inl +++ b/src/BeamAdapter/component/WireBeamInterpolation.inl @@ -175,7 +175,6 @@ void WireBeamInterpolation::getInterpolationParameters(sofa::Index be Real& _Asy, Real& _Asz, Real& _J) { _L = this->d_lengthList.getValue()[beamId]; - Real _rho; Real x_curv = 0; this->getAbsCurvXFromBeam(beamId, x_curv); @@ -264,6 +263,8 @@ template template typename T::SPtr WireBeamInterpolation::create(T* tObj, core::objectmodel::BaseContext* context, core::objectmodel::BaseObjectDescription* arg) { + SOFA_UNUSED(tObj); + WireRestShape* _restShape = nullptr; std::string _restShapePath; diff --git a/src/BeamAdapter/component/engine/WireRestShape.inl b/src/BeamAdapter/component/engine/WireRestShape.inl index ddf411c4a..586bcb60c 100644 --- a/src/BeamAdapter/component/engine/WireRestShape.inl +++ b/src/BeamAdapter/component/engine/WireRestShape.inl @@ -301,7 +301,7 @@ void WireRestShape::getMechanicalParametersAtX(const Real& x_curv, Re const type::vector& keyPts = d_keyPoints.getValue(); // Check in which section x_used belongs to and get access to this section material - for (auto i = 1; i < keyPts.size(); ++i) + for (std::size_t i = 1; i < keyPts.size(); ++i) { if (x_used <= keyPts[i]) { diff --git a/src/BeamAdapter/component/forcefield/AdaptiveBeamForceFieldAndMass.inl b/src/BeamAdapter/component/forcefield/AdaptiveBeamForceFieldAndMass.inl index c2286d8da..757b96a45 100644 --- a/src/BeamAdapter/component/forcefield/AdaptiveBeamForceFieldAndMass.inl +++ b/src/BeamAdapter/component/forcefield/AdaptiveBeamForceFieldAndMass.inl @@ -125,6 +125,8 @@ void AdaptiveBeamForceFieldAndMass::computeGravityVector() template void AdaptiveBeamForceFieldAndMass::computeStiffness(int beamId, BeamLocalMatrices& beamLocalMatrices) { + SOFA_UNUSED(beamId); + /// material parameters Real _G = beamLocalMatrices._youngM / (2.0 * (1.0 + beamLocalMatrices._cPoisson)); @@ -506,7 +508,7 @@ void AdaptiveBeamForceFieldAndMass::addForce (const MechanicalParams* ///* Calculer la matrice "locale" ///* Calculer la force exercée par chaque beam ///* Calculer la force exercée par la gravitée - for (unsigned int beamId=0; beamId ::addForce (const MechanicalParams* const auto& massDensity = helper::getReadAccessor(d_massDensity); // for BeamInterpolation which is not overidding the _rho - if (beamId < int(massDensity.size())) + if (beamId < static_cast(massDensity.size())) { beamMatrices._rho = massDensity[beamId]; } diff --git a/src/BeamAdapter/component/forcefield/AdaptiveInflatableBeamForceField.inl b/src/BeamAdapter/component/forcefield/AdaptiveInflatableBeamForceField.inl index 85da90d6b..9566c6019 100644 --- a/src/BeamAdapter/component/forcefield/AdaptiveInflatableBeamForceField.inl +++ b/src/BeamAdapter/component/forcefield/AdaptiveInflatableBeamForceField.inl @@ -122,7 +122,6 @@ void AdaptiveInflatableBeamForceField::computeGravityVector() template void AdaptiveInflatableBeamForceField::computeStiffness(int beam, BeamLocalMatrices& beamLocalMatrices) { - Real x_curv = 0.0 ; Real _rho = 0.0 ; Real _nu = 0.0 ; Real _E = 0.0 ; diff --git a/src/BeamAdapter/component/mapping/AdaptiveBeamMapping.inl b/src/BeamAdapter/component/mapping/AdaptiveBeamMapping.inl index 7c1b7bc51..67416ee7d 100644 --- a/src/BeamAdapter/component/mapping/AdaptiveBeamMapping.inl +++ b/src/BeamAdapter/component/mapping/AdaptiveBeamMapping.inl @@ -273,9 +273,9 @@ void AdaptiveBeamMapping< TIn, TOut>::apply(const MechanicalParams* mparams, Dat // HACK for init: In case the number of output points is bigger to the number of distribution, set all points to 0 - for (int i = m_pointBeamDistribution.size(); i < d_points.getValue().size(); i++) + for (std::size_t i = m_pointBeamDistribution.size(); i < d_points.getValue().size(); i++) { - out[i] = Vec<3, InReal>(0.0, 0.0, 0.0); + out[i].clear(); } diff --git a/src/BeamAdapter/utils/BeamActions.h b/src/BeamAdapter/utils/BeamActions.h index 465979fe2..531a1cc0c 100644 --- a/src/BeamAdapter/utils/BeamActions.h +++ b/src/BeamAdapter/utils/BeamActions.h @@ -21,6 +21,9 @@ ******************************************************************************/ #pragma once +#include +#include + namespace beamadapter { @@ -55,7 +58,7 @@ namespace beamadapter }; /// static method to convert an action as string into enum class using @sa beamActionNames - static BeamAdapterAction convertBeamAdapterAction(const std::string& sAction) + inline BeamAdapterAction convertBeamAdapterAction(const std::string& sAction) { auto bAction = beamActionNames.find(sAction); From 50b5c390ccd9d3c05c6fa73b50b0ea42155c57cf Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Wed, 16 Apr 2025 15:45:05 +0900 Subject: [PATCH 02/24] clean methods, types, etc --- .../component/model/WireRestShape_test.cpp | 6 +- .../component/BaseBeamInterpolation.h | 79 +++++++++---------- .../component/BaseBeamInterpolation.inl | 68 ++++++++-------- src/BeamAdapter/component/BeamInterpolation.h | 21 ++--- .../component/BeamInterpolation.inl | 24 +++--- .../component/WireBeamInterpolation.h | 18 +++-- .../component/WireBeamInterpolation.inl | 14 ++-- .../InterventionalRadiologyController.inl | 4 +- .../component/controller/SutureController.inl | 4 +- .../component/engine/WireRestShape.h | 4 +- .../component/engine/WireRestShape.inl | 2 +- 11 files changed, 123 insertions(+), 121 deletions(-) diff --git a/BeamAdapter_test/component/model/WireRestShape_test.cpp b/BeamAdapter_test/component/model/WireRestShape_test.cpp index 118895742..05c5f1d8a 100644 --- a/BeamAdapter_test/component/model/WireRestShape_test.cpp +++ b/BeamAdapter_test/component/model/WireRestShape_test.cpp @@ -163,10 +163,10 @@ void WireRestShape_test::testParameterInit() Real straightLength = 95.0; EXPECT_EQ(fullLength, 100.0); - int nbrE0 = 50; - int nbrE1 = 10; + sofa::Size nbrE0 = 50; + sofa::Size nbrE1 = 10; vector keysPoints, keysPoints_ref = { 0, straightLength, fullLength }; - vector nbP_density, nbP_density_ref = { nbrE0, nbrE1 }; + vector nbP_density, nbP_density_ref = { nbrE0, nbrE1 }; wire->getSamplingParameters(keysPoints, nbP_density); EXPECT_EQ(keysPoints.size(), 3); diff --git a/src/BeamAdapter/component/BaseBeamInterpolation.h b/src/BeamAdapter/component/BaseBeamInterpolation.h index f8c75e0fd..f49c7522e 100644 --- a/src/BeamAdapter/component/BaseBeamInterpolation.h +++ b/src/BeamAdapter/component/BaseBeamInterpolation.h @@ -90,7 +90,7 @@ class BaseBeamInterpolation : public virtual sofa::core::objectmodel::BaseObject static void getControlPointsFromFrame( const Transform& global_H_local0, const Transform& global_H_local1, - const Real& L, + const Real L, Vec3& P0, Vec3& P1, Vec3& P2, Vec3& P3); @@ -103,99 +103,98 @@ class BaseBeamInterpolation : public virtual sofa::core::objectmodel::BaseObject virtual void clear(); public: - virtual void addBeam(const BaseMeshTopology::EdgeID& eID, const Real& length, const Real& x0, const Real& x1, const Real& angle); - unsigned int getNumBeams() { return this->d_edgeList.getValue().size(); } + virtual void addBeam(const EdgeID eID, const Real length, const Real x0, const Real x1, const Real angle); + sofa::Size getNumBeams() const { return static_cast(this->d_edgeList.getValue().size()); } - void getAbsCurvXFromBeam(int beam, Real& x_curv); - void getAbsCurvXFromBeam(int beam, Real& x_curv_start, Real& x_curv_end); + void getAbsCurvXFromBeam(const sofa::Index beam, Real& x_curv); + void getAbsCurvXFromBeam(const sofa::Index beam, Real& x_curv_start, Real& x_curv_end); /// getLength / setLength => provides the rest length of each spline using @sa d_lengthList virtual Real getRestTotalLength() = 0; - Real getLength(unsigned int edgeInList); - void setLength(unsigned int edgeInList, Real& length); + Real getLength(const EdgeID edgeInList); + void setLength(const EdgeID edgeInList, Real& length); /// Collision information using @sa d_beamCollision - virtual void getCollisionSampling(Real& dx, const Real& x_localcurv_abs) = 0; - void addCollisionOnBeam(unsigned int b); + virtual void getCollisionSampling(Real& dx, const Real x_localcurv_abs) = 0; + void addCollisionOnBeam(const sofa::Index beam); void clearCollisionOnBeam(); virtual void getSamplingParameters(type::vector& xP_noticeable, - type::vector& nbP_density) = 0; - virtual void getNumberOfCollisionSegment(Real& dx, unsigned int& numLines) = 0; + type::vector& nbP_density) = 0; + virtual void getNumberOfCollisionSegment(Real& dx, sofa::Size& numLines) = 0; - - virtual void getCurvAbsAtBeam(const unsigned int& edgeInList_input, const Real& baryCoord_input, Real& x_output) = 0; - virtual void getSplineRestTransform(unsigned int edgeInList, Transform& local_H_local0_rest, Transform& local_H_local1_rest) = 0; + virtual void getCurvAbsAtBeam(const EdgeID edgeInList_input, const Real baryCoord_input, Real& x_output) = 0; + virtual void getSplineRestTransform(const EdgeID edgeInList, Transform& local_H_local0_rest, Transform& local_H_local1_rest) = 0; /// Returns the BeamSection @sa m_beamSection corresponding to the given beam - virtual const BeamSection& getBeamSection(sofa::Index beamId) = 0; + virtual const BeamSection& getBeamSection(const sofa::Index beamId) = 0; /// Returns the BeamSection data depending on the beam position at the given beam, similar to @getBeamSection - virtual void getInterpolationParameters(sofa::Index beamId, Real& _L, Real& _A, Real& _Iy, Real& _Iz, Real& _Asy, Real& _Asz, Real& J) = 0; + virtual void getInterpolationParameters(const sofa::Index beamId, Real& _L, Real& _A, Real& _Iy, Real& _Iz, Real& _Asy, Real& _Asz, Real& J) = 0; /// Returns the Young modulus, Poisson's ratio and massDensity coefficient of the section at the given curvilinear abscissa - virtual void getMechanicalParameters(sofa::Index beamId, Real& youngModulus, Real& cPoisson, Real& massDensity) = 0; + virtual void getMechanicalParameters(const sofa::Index beamId, Real& youngModulus, Real& cPoisson, Real& massDensity) = 0; - virtual void getBeamAtCurvAbs(const Real& x_input, unsigned int& edgeInList_output, Real& baryCoord_output, unsigned int start = 0); + virtual void getBeamAtCurvAbs(const Real x_input, sofa::Index& edgeInList_output, Real& baryCoord_output, unsigned int start = 0); int computeTransform(const EdgeID edgeInList, Transform& global_H_local0, Transform& global_H_local1, const VecCoord& x); int computeTransform(const EdgeID edgeInList, const PointID node0Idx, const PointID node1Idx, Transform& global_H_local0, Transform& global_H_local1, const VecCoord& x); - void getDOFtoLocalTransform(unsigned int edgeInList, Transform& DOF0_H_local0, Transform& DOF1_H_local1); - void getDOFtoLocalTransformInGlobalFrame(unsigned int edgeInList, Transform& DOF0Global_H_local0, Transform& DOF1Global_H_local1, const VecCoord& x); - void setTransformBetweenDofAndNode(int beam, const Transform& DOF_H_Node, unsigned int zeroORone); + void getDOFtoLocalTransform(const EdgeID edgeInList, Transform& DOF0_H_local0, Transform& DOF1_H_local1); + void getDOFtoLocalTransformInGlobalFrame(const EdgeID edgeInList, Transform& DOF0Global_H_local0, Transform& DOF1Global_H_local1, const VecCoord& x); + void setTransformBetweenDofAndNode(const sofa::Index beam, const Transform& DOF_H_Node, unsigned int zeroORone); - void getTangent(Vec3& t, const Real& baryCoord, - const Transform& global_H_local0, const Transform& global_H_local1, const Real& L); + void getTangent(Vec3& t, const Real baryCoord, + const Transform& global_H_local0, const Transform& global_H_local1, const Real L); - int getNodeIndices(unsigned int edgeInList, unsigned int& node0Idx, unsigned int& node1Idx); + int getNodeIndices(const EdgeID edgeInList, unsigned int& node0Idx, unsigned int& node1Idx); - void getSplinePoints(unsigned int edgeInList, const VecCoord& x, Vec3& P0, Vec3& P1, Vec3& P2, Vec3& P3); + void getSplinePoints(const EdgeID edgeInList, const VecCoord& x, Vec3& P0, Vec3& P1, Vec3& P2, Vec3& P3); unsigned int getStateSize() const; void computeActualLength(Real& length, const Vec3& P0, const Vec3& P1, const Vec3& P2, const Vec3& P3); - void computeStrechAndTwist(unsigned int edgeInList, const VecCoord& x, Vec3& ResultNodeO, Vec3& ResultNode1); + void computeStrechAndTwist(const EdgeID edgeInList, const VecCoord& x, Vec3& ResultNodeO, Vec3& ResultNode1); ///vId_Out provides the id of the multiVecId which stores the position of the Bezier Points void updateBezierPoints(const VecCoord& x, sofa::core::VecCoordId& vId_Out); - void updateBezierPoints(const VecCoord& x, unsigned int index, VectorVec3& v); + void updateBezierPoints(const VecCoord& x, sofa::Index index, VectorVec3& v); /// spline base interpolation of points and transformation - void interpolatePointUsingSpline(unsigned int edgeInList, const Real& baryCoord, const Vec3& localPos, const VecCoord& x, Vec3& posResult) { + void interpolatePointUsingSpline(const EdgeID edgeInList, const Real baryCoord, const Vec3& localPos, const VecCoord& x, Vec3& posResult) { interpolatePointUsingSpline(edgeInList, baryCoord, localPos, x, posResult, true, sofa::core::vec_id::read_access::position); } - void interpolatePointUsingSpline(unsigned int edgeInList, const Real& baryCoord, const Vec3& localPos, + void interpolatePointUsingSpline(const EdgeID edgeInList, const Real baryCoord, const Vec3& localPos, const VecCoord& x, Vec3& posResult, bool recompute, const sofa::core::ConstVecCoordId& vecXId); - void InterpolateTransformUsingSpline(unsigned int edgeInList, const Real& baryCoord, const Vec3& localPos, + void InterpolateTransformUsingSpline(const EdgeID edgeInList, const Real baryCoord, const Vec3& localPos, const VecCoord& x, Transform& global_H_localInterpol); - void InterpolateTransformUsingSpline(Transform& global_H_localResult, const Real& baryCoord, - const Transform& global_H_local0, const Transform& global_H_local1, const Real& L); + void InterpolateTransformUsingSpline(Transform& global_H_localResult, const Real baryCoord, + const Transform& global_H_local0, const Transform& global_H_local1, const Real L); - void InterpolateTransformAndVelUsingSpline(unsigned int edgeInList, const Real& baryCoord, const Vec3& localPos, + void InterpolateTransformAndVelUsingSpline(const EdgeID edgeInList, const Real baryCoord, const Vec3& localPos, const VecCoord& x, const VecDeriv& v, Transform& global_H_localInterpol, Deriv& v_interpol); /// compute the total bending Rotation Angle while going through the Spline (to estimate the curvature) - Real ComputeTotalBendingRotationAngle(const Real& dx_computation, const Transform& global_H_local0, - const Transform& global_H_local1, const Real& L, - const Real& baryCoordMin, const Real& baryCoordMax); + Real ComputeTotalBendingRotationAngle(const Real dx_computation, const Transform& global_H_local0, + const Transform& global_H_local1, const Real L, + const Real baryCoordMin, const Real baryCoordMax); /// 3DOF mapping - void MapForceOnNodeUsingSpline(unsigned int edgeInList, const Real& baryCoord, const Vec3& localPos, + void MapForceOnNodeUsingSpline(const EdgeID edgeInList, const Real baryCoord, const Vec3& localPos, const VecCoord& x, const Vec3& finput, SpatialVector& FNode0output, SpatialVector& FNode1output); /// 6DoF mapping - void MapForceOnNodeUsingSpline(unsigned int edgeInList, const Real& baryCoord, const Vec3& localPos, + void MapForceOnNodeUsingSpline(const EdgeID edgeInList, const Real baryCoord, const Vec3& localPos, const VecCoord& x, const SpatialVector& f6DofInput, SpatialVector& FNode0output, SpatialVector& FNode1output); @@ -212,7 +211,7 @@ class BaseBeamInterpolation : public virtual sofa::core::objectmodel::BaseObject const VecEdges* m_topologyEdges{ nullptr }; ///2. Vector of length of each beam. Same size as @sa d_edgeList - Data< type::vector< double > > d_lengthList; + Data< type::vector< Real > > d_lengthList; ///3. (optional) apply a rigid Transform between the degree of Freedom and the first node of the beam. Indexation based on the num of Edge Data< type::vector< Transform > > d_DOF0TransformNode0; @@ -224,7 +223,7 @@ class BaseBeamInterpolation : public virtual sofa::core::objectmodel::BaseObject Data< type::vector< Vec2 > > d_curvAbsList; ///5. (optional) list of the beams in m_edgeList that need to be considered for collision - Data< sofa::type::vector > d_beamCollision; + Data< sofa::type::vector > d_beamCollision; Data d_dofsAndBeamsAligned; diff --git a/src/BeamAdapter/component/BaseBeamInterpolation.inl b/src/BeamAdapter/component/BaseBeamInterpolation.inl index 9fd21a2a9..b76b3f0e4 100644 --- a/src/BeamAdapter/component/BaseBeamInterpolation.inl +++ b/src/BeamAdapter/component/BaseBeamInterpolation.inl @@ -36,7 +36,7 @@ using sofa::core::objectmodel::BaseContext; template void BaseBeamInterpolation::getControlPointsFromFrame(const Transform& global_H_local0, const Transform& global_H_local1, - const Real& L, + const Real L, Vec3& P0, Vec3& P1, Vec3& P2, Vec3& P3) { @@ -168,7 +168,7 @@ void BaseBeamInterpolation::clear() template -void BaseBeamInterpolation::addBeam(const BaseMeshTopology::EdgeID& eID, const Real& length, const Real& x0, const Real& x1, const Real& angle) +void BaseBeamInterpolation::addBeam(const EdgeID eID, const Real length, const Real x0, const Real x1, const Real angle) { auto edgeList = sofa::helper::getWriteOnlyAccessor(d_edgeList); auto lengthList = sofa::helper::getWriteOnlyAccessor(d_lengthList); @@ -193,14 +193,14 @@ void BaseBeamInterpolation::addBeam(const BaseMeshTopology::EdgeID& e template -void BaseBeamInterpolation::getAbsCurvXFromBeam(int beam, Real& x_curv) +void BaseBeamInterpolation::getAbsCurvXFromBeam(const sofa::Index beam, Real& x_curv) { x_curv = d_curvAbsList.getValue()[beam].y(); } template -void BaseBeamInterpolation::getAbsCurvXFromBeam(int beam, Real& x_curv_start, Real& x_curv_end) +void BaseBeamInterpolation::getAbsCurvXFromBeam(const sofa::Index beam, Real& x_curv_start, Real& x_curv_end) { Vec2 ca = d_curvAbsList.getValue()[beam]; x_curv_start = ca.x(); @@ -208,14 +208,14 @@ void BaseBeamInterpolation::getAbsCurvXFromBeam(int beam, Real& x_cur } template -typename BaseBeamInterpolation::Real BaseBeamInterpolation::getLength(unsigned int edgeInList) +typename BaseBeamInterpolation::Real BaseBeamInterpolation::getLength(const EdgeID edgeInList) { Real _L = d_lengthList.getValue()[edgeInList]; return _L; } template -void BaseBeamInterpolation::setLength(unsigned int edgeInList, Real& length) +void BaseBeamInterpolation::setLength(const EdgeID edgeInList, Real& length) { auto lengthList = sofa::helper::getWriteOnlyAccessor(d_lengthList); lengthList[edgeInList] = length; @@ -257,7 +257,7 @@ int BaseBeamInterpolation::computeTransform(const EdgeID edgeInList, template -void BaseBeamInterpolation::getBeamAtCurvAbs(const Real& x_input, unsigned int& edgeInList_output, Real& baryCoord_output, unsigned int start) +void BaseBeamInterpolation::getBeamAtCurvAbs(const Real x_input, unsigned int& edgeInList_output, Real& baryCoord_output, unsigned int start) { /// lTotalRest = total length of the Real lTotalRest = getRestTotalLength(); @@ -321,7 +321,7 @@ int BaseBeamInterpolation::computeTransform(const EdgeID edgeInList, template -void BaseBeamInterpolation::getDOFtoLocalTransform(unsigned int edgeInList, Transform& DOF0_H_local0, Transform& DOF1_H_local1) +void BaseBeamInterpolation::getDOFtoLocalTransform(const EdgeID edgeInList, Transform& DOF0_H_local0, Transform& DOF1_H_local1) { if (d_dofsAndBeamsAligned.getValue()) { @@ -336,7 +336,7 @@ void BaseBeamInterpolation::getDOFtoLocalTransform(unsigned int edgeI template -void BaseBeamInterpolation::getDOFtoLocalTransformInGlobalFrame(unsigned int edgeInList, Transform& DOF0Global_H_local0, Transform& DOF1Global_H_local1, const VecCoord& x) +void BaseBeamInterpolation::getDOFtoLocalTransformInGlobalFrame(const EdgeID edgeInList, Transform& DOF0Global_H_local0, Transform& DOF1Global_H_local1, const VecCoord& x) { Transform DOF0_H_local0, DOF1_H_local1; @@ -356,7 +356,7 @@ void BaseBeamInterpolation::getDOFtoLocalTransformInGlobalFrame(unsig template -void BaseBeamInterpolation::setTransformBetweenDofAndNode(int beam, const Transform& DOF_H_Node, unsigned int zeroORone) +void BaseBeamInterpolation::setTransformBetweenDofAndNode(const sofa::Index beam, const Transform& DOF_H_Node, unsigned int zeroORone) { if (beam > int(d_DOF0TransformNode0.getValue().size() - 1) || beam > int(d_DOF1TransformNode1.getValue().size() - 1)) { @@ -379,9 +379,9 @@ void BaseBeamInterpolation::setTransformBetweenDofAndNode(int beam, c /// getTangent : Computation of a Tangent for the beam (it is given by the derivative of the spline formulation) template -void BaseBeamInterpolation::getTangent(Vec3& t, const Real& baryCoord, +void BaseBeamInterpolation::getTangent(Vec3& t, const Real baryCoord, const Transform& global_H_local0, - const Transform& global_H_local1, const Real& L) + const Transform& global_H_local1, const Real L) { Vec3 P0, P1, P2, P3; @@ -396,7 +396,7 @@ void BaseBeamInterpolation::getTangent(Vec3& t, const Real& baryCoord template -int BaseBeamInterpolation::getNodeIndices(unsigned int edgeInList, +int BaseBeamInterpolation::getNodeIndices(const EdgeID edgeInList, unsigned int& node0Idx, unsigned int& node1Idx) { @@ -417,7 +417,7 @@ int BaseBeamInterpolation::getNodeIndices(unsigned int edgeInList, template -void BaseBeamInterpolation::getSplinePoints(unsigned int edgeInList, const VecCoord& x, Vec3& P0, Vec3& P1, Vec3& P2, Vec3& P3) +void BaseBeamInterpolation::getSplinePoints(const EdgeID edgeInList, const VecCoord& x, Vec3& P0, Vec3& P1, Vec3& P2, Vec3& P3) { Transform global_H_local0, global_H_local1; if (computeTransform(edgeInList, global_H_local0, global_H_local1, x) == -1) @@ -426,7 +426,7 @@ void BaseBeamInterpolation::getSplinePoints(unsigned int edgeInList, return; } - const Real& _L = d_lengthList.getValue()[edgeInList]; + const Real _L = d_lengthList.getValue()[edgeInList]; this->getControlPointsFromFrame(global_H_local0, global_H_local1, _L, P0, P1, P2, P3); } @@ -491,7 +491,7 @@ void BaseBeamInterpolation::computeActualLength(Real& length, const V template -void BaseBeamInterpolation::computeStrechAndTwist(unsigned int edgeInList, const VecCoord& x, Vec3& ResultNodeO, Vec3& ResultNode1) +void BaseBeamInterpolation::computeStrechAndTwist(const EdgeID edgeInList, const VecCoord& x, Vec3& ResultNodeO, Vec3& ResultNode1) { /// ResultNode = [half length of the beam (m), geometrical Twist angle (rad), additional mechanical Twist angle (rad)] @@ -596,7 +596,7 @@ template void BaseBeamInterpolation::updateBezierPoints(const VecCoord& x, unsigned int index, VectorVec3& bezierPosVec) { /// <<" interpolatePointUsingSpline : "<< edgeInList<<" xbary="< make something faster ! @@ -612,8 +612,8 @@ void BaseBeamInterpolation::updateBezierPoints(const VecCoord& x, uns template -void BaseBeamInterpolation::interpolatePointUsingSpline(unsigned int edgeInList, - const Real& baryCoord, +void BaseBeamInterpolation::interpolatePointUsingSpline(const EdgeID edgeInList, + const Real baryCoord, const Vec3& localPos, const VecCoord& x, Vec3& posResult, @@ -623,7 +623,7 @@ void BaseBeamInterpolation::interpolatePointUsingSpline(unsigned int if (recompute) { /// <<" interpolatePointUsingSpline : "<< edgeInList<<" xbary="< _L * 1e-10) { @@ -660,7 +660,7 @@ void BaseBeamInterpolation::interpolatePointUsingSpline(unsigned int else { /// no need to recompute the positions of the Spline => we will read their value in the vector which id is vecXId - const Real& _L = d_lengthList.getValue()[edgeInList]; + const Real _L = d_lengthList.getValue()[edgeInList]; if (localPos.norm() > _L * 1e-10) { @@ -698,8 +698,8 @@ void BaseBeamInterpolation::interpolatePointUsingSpline(unsigned int template -void BaseBeamInterpolation::InterpolateTransformUsingSpline(unsigned int edgeInList, - const Real& baryCoord, +void BaseBeamInterpolation::InterpolateTransformUsingSpline(const EdgeID edgeInList, + const Real baryCoord, const Vec3& localPos, const VecCoord& x, Transform& global_H_localInterpol) @@ -711,7 +711,7 @@ void BaseBeamInterpolation::InterpolateTransformUsingSpline(unsigned return; } - const Real& _L = d_lengthList.getValue()[edgeInList]; + const Real _L = d_lengthList.getValue()[edgeInList]; if (localPos.norm() > 1e-10 * _L) msg_warning() << "Interpolate frame only on the central curve of the beam. "; @@ -725,10 +725,10 @@ void BaseBeamInterpolation::InterpolateTransformUsingSpline(unsigned /// the location of the frame is given by baryCoord template void BaseBeamInterpolation::InterpolateTransformUsingSpline(Transform& global_H_localResult, - const Real& baryCoord, + const Real baryCoord, const Transform& global_H_local0, const Transform& global_H_local1, - const Real& L) + const Real L) { Vec3NoInit P0, P1, P2, P3; @@ -787,7 +787,7 @@ void BaseBeamInterpolation::InterpolateTransformUsingSpline(Transform template -void BaseBeamInterpolation::InterpolateTransformAndVelUsingSpline(unsigned int edgeInList, const Real& baryCoord, +void BaseBeamInterpolation::InterpolateTransformAndVelUsingSpline(const EdgeID edgeInList, const Real baryCoord, const Vec3& localPos, const VecCoord& x, const VecDeriv& v, Transform& global_H_localInterpol, Deriv& v_interpol) { @@ -813,7 +813,7 @@ void BaseBeamInterpolation::InterpolateTransformAndVelUsingSpline(uns /// 4. Computes the transformation - const Real& _L = d_lengthList.getValue()[edgeInList]; + const Real _L = d_lengthList.getValue()[edgeInList]; if (localPos.norm() > 1e-10 * _L) msg_warning() << "Interpolate frame only on the central curve of the beam"; @@ -853,9 +853,9 @@ void BaseBeamInterpolation::InterpolateTransformAndVelUsingSpline(uns /// Principle: computation of several tangent between node0 to the node1 (given by numComputationPoints) /// and computation of an angle (acos) between these successive tangents template -typename BaseBeamInterpolation::Real BaseBeamInterpolation::ComputeTotalBendingRotationAngle(const Real& dx_computation, - const Transform& global_H_local0, const Transform& global_H_local1, const Real& L, - const Real& baryCoordMin, const Real& baryCoordMax) +typename BaseBeamInterpolation::Real BaseBeamInterpolation::ComputeTotalBendingRotationAngle(const Real dx_computation, + const Transform& global_H_local0, const Transform& global_H_local1, const Real L, + const Real baryCoordMin, const Real baryCoordMax) { Vec3 P0, P1, P2, P3, t0, t1; @@ -897,8 +897,8 @@ typename BaseBeamInterpolation::Real BaseBeamInterpolation /// Result: Forces (and Torques) on the nodes of this beam //////////////////////////////////////////////////////////////////////////////////////////////////// template -void BaseBeamInterpolation::MapForceOnNodeUsingSpline(unsigned int edgeInList, - const Real& baryCoord, const Vec3& localPos, +void BaseBeamInterpolation::MapForceOnNodeUsingSpline(const EdgeID edgeInList, + const Real baryCoord, const Vec3& localPos, const VecCoord& x, const Vec3& finput, SpatialVector& FNode0output, SpatialVector& FNode1output) { @@ -961,7 +961,7 @@ void BaseBeamInterpolation::MapForceOnNodeUsingSpline(unsigned int ed /// Result: Forces (and Torques) on the nodes of this beam //////////////////////////////////////////////////////////////////////////////////////////////////// template -void BaseBeamInterpolation::MapForceOnNodeUsingSpline(unsigned int edgeInList, const Real& baryCoord, +void BaseBeamInterpolation::MapForceOnNodeUsingSpline(const EdgeID edgeInList, const Real baryCoord, const Vec3& localPos, const VecCoord& x, const SpatialVector& f6DofInput, SpatialVector& FNode0output, SpatialVector& FNode1output) diff --git a/src/BeamAdapter/component/BeamInterpolation.h b/src/BeamAdapter/component/BeamInterpolation.h index 2e17b70df..2be0fc5b9 100644 --- a/src/BeamAdapter/component/BeamInterpolation.h +++ b/src/BeamAdapter/component/BeamInterpolation.h @@ -88,6 +88,7 @@ class BeamInterpolation : public BaseBeamInterpolation using PointID = BaseMeshTopology::PointID; using ElementID = BaseMeshTopology::EdgeID; + using EdgeID = BaseMeshTopology::EdgeID; using VecElementID = type::vector; using VecEdges = type::vector; @@ -145,17 +146,17 @@ class BeamInterpolation : public BaseBeamInterpolation Real &_Asy, Real &_Asz, Real &J) override; void getMechanicalParameters(sofa::Index beamId, Real& youngModulus, Real& cPoisson, Real& massDensity) override; - void getTangentUsingSplinePoints(unsigned int edgeInList, const Real& baryCoord, const sofa::core::ConstVecCoordId &vecXId, Vec3& t ); + void getTangentUsingSplinePoints(const EdgeID edgeInList, const Real baryCoord, const sofa::core::ConstVecCoordId &vecXId, Vec3& t ); /// computeActualLength => given the 4 control points of the spline, it provides an estimate of the length (using gauss points integration) - virtual void getCurvAbsAtBeam(const unsigned int& edgeInList_input, const Real& baryCoord_input, Real& x_output) override + virtual void getCurvAbsAtBeam(const EdgeID edgeInList_input, const Real baryCoord_input, Real& x_output) override { SOFA_UNUSED(edgeInList_input); SOFA_UNUSED(baryCoord_input); SOFA_UNUSED(x_output); } - virtual void getBeamAtCurvAbs(const Real& x_input, unsigned int& edgeInList_output, Real& baryCoord_output, unsigned int start = 0) override + virtual void getBeamAtCurvAbs(const Real x_input, unsigned int& edgeInList_output, Real& baryCoord_output, unsigned int start = 0) override { SOFA_UNUSED(x_input); SOFA_UNUSED(edgeInList_output); @@ -194,15 +195,15 @@ class BeamInterpolation : public BaseBeamInterpolation Data d_straight; virtual void clear() override; - virtual void addBeam(const BaseMeshTopology::EdgeID &eID , const Real &length, const Real &x0, const Real &x1, const Real &angle) override; + virtual void addBeam(const EdgeID eID , const Real length, const Real x0, const Real x1, const Real angle) override; virtual void getSamplingParameters(type::vector& xP_noticeable, - type::vector& nbP_density) override; + type::vector& nbP_density) override; Real getRestTotalLength() override; - void getCollisionSampling(Real &dx, const Real& x_localcurv_abs) override; + void getCollisionSampling(Real &dx, const Real x_localcurv_abs) override; void getNumberOfCollisionSegment(Real &dx, unsigned int &numLines) override; - void setTransformBetweenDofAndNode(int beam, const Transform &DOF_H_Node, unsigned int zeroORone ); - void getSplineRestTransform(unsigned int edgeInList, Transform &local_H_local0_rest, Transform &local_H_local1_rest) override; + void setTransformBetweenDofAndNode(const sofa::Index beam, const Transform &DOF_H_Node, unsigned int zeroORone ); + void getSplineRestTransform(const EdgeID edgeInList, Transform &local_H_local0_rest, Transform &local_H_local1_rest) override; /////////////////////////// Deprecated Methods ////////////////////////////////////////// [[deprecated("Releasing catheter or brokenIn2 mode is not anymore supported. Feature has been removed after release v23.06")]] @@ -229,8 +230,8 @@ protected : void checkDataSize(Real& defaultValue, Data>& dataList, const size_t& nbEdges); - void computeRectangularCrossSectionInertiaMatrix(const Real &Ly, const Real &Lz, BeamSection §ion); - void computeCircularCrossSectionInertiaMatrix(const Real &r, const Real &rInner, BeamSection §ion); + void computeRectangularCrossSectionInertiaMatrix(const Real Ly, const Real Lz, BeamSection §ion); + void computeCircularCrossSectionInertiaMatrix(const Real r, const Real rInner, BeamSection §ion); }; #if !defined(SOFA_PLUGIN_BEAMADAPTER_BEAMINTERPOLATION_CPP) diff --git a/src/BeamAdapter/component/BeamInterpolation.inl b/src/BeamAdapter/component/BeamInterpolation.inl index 16d8aeb91..9af09e6c6 100644 --- a/src/BeamAdapter/component/BeamInterpolation.inl +++ b/src/BeamAdapter/component/BeamInterpolation.inl @@ -90,7 +90,7 @@ BeamInterpolation::BeamInterpolation() : } template -void BeamInterpolation::computeRectangularCrossSectionInertiaMatrix(const Real& Ly, const Real& Lz, BeamSection& section) +void BeamInterpolation::computeRectangularCrossSectionInertiaMatrix(const Real Ly, const Real Lz, BeamSection& section) { section._Iy=Ly*Lz*Lz*Lz/12.0; section._Iz=Lz*Ly*Ly*Ly/12.0; @@ -102,7 +102,7 @@ void BeamInterpolation::computeRectangularCrossSectionInertiaMatrix(c } template -void BeamInterpolation::computeCircularCrossSectionInertiaMatrix(const Real &r, const Real &rInner, BeamSection §ion) +void BeamInterpolation::computeCircularCrossSectionInertiaMatrix(const Real r, const Real rInner, BeamSection §ion) { section._r = r; section._rInner = rInner; @@ -235,7 +235,7 @@ void BeamInterpolation::bwdInit() auto lengthList = sofa::helper::getWriteOnlyAccessor(this->d_lengthList); lengthList.clear(); - const unsigned int edgeListSize = this->d_edgeList.getValue().size(); + const auto edgeListSize = this->d_edgeList.getValue().size(); unsigned int nd0Id=0, nd1Id=0; @@ -321,7 +321,7 @@ bool BeamInterpolation::interpolationIsAlreadyInitialized() if (this->d_edgeList.getValue().size() == 0) return false; - const unsigned int nbEdges = this->d_edgeList.getValue().size(); + const auto nbEdges = this->d_edgeList.getValue().size(); if (this->d_DOF0TransformNode0.getValue().size() != nbEdges) return false; @@ -378,7 +378,7 @@ void BeamInterpolation::clear() template -void BeamInterpolation::addBeam(const BaseMeshTopology::EdgeID &eID , const Real &length, const Real &x0, const Real &x1, const Real &angle) +void BeamInterpolation::addBeam(const EdgeID eID, const Real length, const Real x0, const Real x1, const Real angle) { auto edgeList = sofa::helper::getWriteOnlyAccessor(this->d_edgeList); auto lengthList = sofa::helper::getWriteOnlyAccessor(this->d_lengthList); @@ -403,7 +403,7 @@ void BeamInterpolation::addBeam(const BaseMeshTopology::EdgeID &eID template -void BeamInterpolation::getSamplingParameters(type::vector& /*xP_noticeable*/, type::vector< int>& /*nbP_density*/) +void BeamInterpolation::getSamplingParameters(type::vector& /*xP_noticeable*/, type::vector& /*nbP_density*/) { msg_error()<<"getSamplingParameters is not implemented when _restShape== nullptr : TODO !! "; } @@ -421,7 +421,7 @@ typename BeamInterpolation::Real BeamInterpolation::getRes } template -void BeamInterpolation::getCollisionSampling(Real &dx, const Real& /*x_localcurv_abs*/) +void BeamInterpolation::getCollisionSampling(Real &dx, const Real /*x_localcurv_abs*/) { unsigned int numLines = 30; dx = getRestTotalLength()/numLines; @@ -469,9 +469,9 @@ void BeamInterpolation::getMechanicalParameters(sofa::Index beamId, R template -void BeamInterpolation::setTransformBetweenDofAndNode(int beam, const Transform &DOF_H_Node, unsigned int zeroORone ) +void BeamInterpolation::setTransformBetweenDofAndNode(const sofa::Index beam, const Transform &DOF_H_Node, unsigned int zeroORone) { - if (beam > int(this->d_DOF0TransformNode0.getValue().size()-1) || beam > int(this->d_DOF1TransformNode1.getValue().size()-1)) + if (beam > this->d_DOF0TransformNode0.getValue().size()-1 || beam > this->d_DOF1TransformNode1.getValue().size()-1) { msg_error()<<"WARNING setTransformBetweenDofAndNode on non existing beam"; return; @@ -491,7 +491,7 @@ void BeamInterpolation::setTransformBetweenDofAndNode(int beam, const template -void BeamInterpolation::getSplineRestTransform(unsigned int edgeInList, Transform &local_H_local0_rest, Transform &local_H_local1_rest) +void BeamInterpolation::getSplineRestTransform(const EdgeID edgeInList, Transform &local_H_local0_rest, Transform &local_H_local1_rest) { if(d_straight.getValue()) { @@ -538,7 +538,7 @@ void BeamInterpolation::getSplineRestTransform(unsigned int edgeInLis template -void BeamInterpolation::getTangentUsingSplinePoints(unsigned int edgeInList, const Real& baryCoord, +void BeamInterpolation::getTangentUsingSplinePoints(const EdgeID edgeInList, const Real baryCoord, const sofa::core::ConstVecCoordId &vecXId, Vec3& t ) { @@ -575,7 +575,7 @@ void BeamInterpolation::updateInterpolation(){ dmsg_info() <<"entering updateInterpolation" ; const type::vector< Vec2 > &interpolationInputs = d_InterpolationInputs.getValue(); - unsigned int numInterpolatedPositions = interpolationInputs.size(); + const auto numInterpolatedPositions = interpolationInputs.size(); auto interpolatedPos = sofa::helper::getWriteOnlyAccessor(d_InterpolatedPos); auto interpolatedVel = sofa::helper::getWriteOnlyAccessor(d_InterpolatedVel); diff --git a/src/BeamAdapter/component/WireBeamInterpolation.h b/src/BeamAdapter/component/WireBeamInterpolation.h index cfdcd3854..6e7b0ba71 100644 --- a/src/BeamAdapter/component/WireBeamInterpolation.h +++ b/src/BeamAdapter/component/WireBeamInterpolation.h @@ -91,6 +91,8 @@ class WireBeamInterpolation : public BaseBeamInterpolation typedef typename Inherited::Vec2 Vec2; typedef typename Inherited::Vec3 Vec3; typedef typename Inherited::Quat Quat; + + using EdgeID = BaseMeshTopology::EdgeID; WireBeamInterpolation(WireRestShape *_restShape = nullptr); @@ -102,10 +104,10 @@ class WireBeamInterpolation : public BaseBeamInterpolation using BaseBeamInterpolation::addBeam; - void addBeam(const BaseMeshTopology::EdgeID &eID , const Real &length, const Real &x0, const Real &x1, + void addBeam(const EdgeID eID, const Real length, const Real x0, const Real x1, const Transform &DOF0_H_Node0, const Transform &DOF1_H_Node1); - void getSamplingParameters(type::vector& xP_noticeable, type::vector< int>& nbP_density) override + void getSamplingParameters(type::vector& xP_noticeable, type::vector& nbP_density) override { this->m_restShape->getSamplingParameters(xP_noticeable, nbP_density); } @@ -115,7 +117,7 @@ class WireBeamInterpolation : public BaseBeamInterpolation return this->m_restShape->getLength(); } - void getCollisionSampling(Real &dx, const Real& x_localcurv_abs) override + void getCollisionSampling(Real &dx, const Real x_localcurv_abs) override { this->m_restShape->getCollisionSampling(dx,x_localcurv_abs); } @@ -126,10 +128,10 @@ class WireBeamInterpolation : public BaseBeamInterpolation } - virtual void getRestTransform(unsigned int edgeInList, Transform &local0_H_local1_rest); + virtual void getRestTransform(const EdgeID edgeInList, Transform &local0_H_local1_rest); - void getCurvAbsAtBeam(const unsigned int& edgeInList_input, const Real& baryCoord_input, Real& x_output) override; - void getSplineRestTransform(unsigned int edgeInList, Transform &local_H_local0_rest, Transform &local_H_local1_rest) override; + void getCurvAbsAtBeam(const EdgeID edgeInList_input, const Real baryCoord_input, Real& x_output) override; + void getSplineRestTransform(const EdgeID edgeInList, Transform &local_H_local0_rest, Transform &local_H_local1_rest) override; const BeamSection& getBeamSection(sofa::Index beamId) override; void getInterpolationParameters(sofa::Index beamId, Real& _L, Real& _A, Real& _Iy, Real& _Iz, Real& _Asy, Real& _Asz, Real& _J) override; @@ -141,7 +143,7 @@ class WireBeamInterpolation : public BaseBeamInterpolation void setPathToRestShape(const std::string &o){m_restShape.setPath(o);} - void getRestTransformOnX(Transform &global_H_local, const Real &x) + void getRestTransformOnX(Transform &global_H_local, const Real x) { if(this->m_restShape) { @@ -185,7 +187,7 @@ class WireBeamInterpolation : public BaseBeamInterpolation /////////////////////////// Deprecated Methods ////////////////////////////////////////// /// For coils: a part of the coil instrument can be brokenIn2 (by default the point of release is the end of the straight length) [[deprecated("Releasing catheter or brokenIn2 mode is not anymore supported. Feature has been removed after release v23.06")]] - bool breaksInTwo(const Real& x_min_out, Real& x_break, int& numBeamsNotUnderControlled) { + bool breaksInTwo(const Real x_min_out, Real& x_break, int& numBeamsNotUnderControlled) { SOFA_UNUSED(x_min_out); SOFA_UNUSED(x_break); SOFA_UNUSED(numBeamsNotUnderControlled); diff --git a/src/BeamAdapter/component/WireBeamInterpolation.inl b/src/BeamAdapter/component/WireBeamInterpolation.inl index bc1beaf83..fdd3443e3 100644 --- a/src/BeamAdapter/component/WireBeamInterpolation.inl +++ b/src/BeamAdapter/component/WireBeamInterpolation.inl @@ -59,7 +59,7 @@ void WireBeamInterpolation::init() } type::vector xP_noticeable; - type::vector< int> nbP_density; + type::vector nbP_density; m_restShape.get()->getSamplingParameters(xP_noticeable, nbP_density); @@ -81,7 +81,7 @@ template template -void WireBeamInterpolation::addBeam(const BaseMeshTopology::EdgeID &eID , const Real &length, const Real &x0, const Real &x1, +void WireBeamInterpolation::addBeam(const EdgeID eID, const Real length, const Real x0, const Real x1, const Transform &DOF0_H_Node0, const Transform &DOF1_H_Node1) { auto edgeList = sofa::helper::getWriteOnlyAccessor(this->d_edgeList); @@ -104,7 +104,7 @@ void WireBeamInterpolation::addBeam(const BaseMeshTopology::EdgeID &e template -void WireBeamInterpolation::getRestTransform(unsigned int edgeInList, Transform &local0_H_local1_rest) +void WireBeamInterpolation::getRestTransform(const EdgeID edgeInList, Transform &local0_H_local1_rest) { msg_warning() << "GetRestTransform not implemented for not straightRestShape" ; @@ -114,7 +114,7 @@ void WireBeamInterpolation::getRestTransform(unsigned int edgeInList, template -void WireBeamInterpolation::getSplineRestTransform(unsigned int edgeInList, Transform &local_H_local0_rest, Transform &local_H_local1_rest) +void WireBeamInterpolation::getSplineRestTransform(const EdgeID edgeInList, Transform &local_H_local0_rest, Transform &local_H_local1_rest) { if (this->isControlled() && this->m_restShape!=nullptr) { @@ -147,7 +147,7 @@ void WireBeamInterpolation::getSplineRestTransform(unsigned int edgeI template -void WireBeamInterpolation::getCurvAbsAtBeam(const unsigned int &edgeInList_input, const Real& baryCoord_input, Real& x_output) +void WireBeamInterpolation::getCurvAbsAtBeam(const EdgeID edgeInList_input, const Real baryCoord_input, Real& x_output) { ///TODO(dmarchal 2017-05-17): Please tell who and when it will be done. // TODO : version plus complete prenant en compte les coupures et autres particularites de ce modele ? @@ -207,11 +207,11 @@ bool WireBeamInterpolation::getApproximateCurvAbs(const Vec3& x_input Real closestDist = (x_input-globalHlocal0.getOrigin()).norm2(); Real beamBary = 0.0; bool projected = false; - unsigned int beamIndex = 0; + sofa::Index beamIndex = 0; // Just look for the closest point on the curve // Returns false if this point is not a projection on the curve - unsigned int nb = this->getNumBeams(); + const auto nb = this->getNumBeams(); for(unsigned int i=0; icomputeTransform(i, globalHlocal0, globalHlocal1, x); diff --git a/src/BeamAdapter/component/controller/InterventionalRadiologyController.inl b/src/BeamAdapter/component/controller/InterventionalRadiologyController.inl index 7dce303b0..026b06fd1 100644 --- a/src/BeamAdapter/component/controller/InterventionalRadiologyController.inl +++ b/src/BeamAdapter/component/controller/InterventionalRadiologyController.inl @@ -219,7 +219,7 @@ void InterventionalRadiologyController::bwdInit() for (unsigned int i = 0; i < m_instrumentsList.size(); i++) { type::vector xP_noticeable_I; - type::vector< int > density_I; + type::vector density_I; m_instrumentsList[i]->getSamplingParameters(xP_noticeable_I, density_I); for (auto nb : density_I) @@ -467,7 +467,7 @@ void InterventionalRadiologyController::computeInstrumentsCurvAbs(typ for (sofa::Size i=0; i xP_noticeable_I; - type::vector< int > density_I; + type::vector density_I; m_instrumentsList[i]->getSamplingParameters(xP_noticeable_I, density_I); // sampling of the different section of this instrument // check each interval of noticeable point to see if they go out (>0) and use corresponding density to sample the interval. diff --git a/src/BeamAdapter/component/controller/SutureController.inl b/src/BeamAdapter/component/controller/SutureController.inl index 85bbedfb9..cb0aad4de 100644 --- a/src/BeamAdapter/component/controller/SutureController.inl +++ b/src/BeamAdapter/component/controller/SutureController.inl @@ -121,7 +121,7 @@ void SutureController::initWireModel() } type::vector< Real > xP_noticeable; - type::vector< int > nbP_density; + type::vector nbP_density; l_adaptiveInterpolation->getSamplingParameters(xP_noticeable, nbP_density); // computation of the number of node on the structure: @@ -913,7 +913,7 @@ template void SutureController::computeSampling(type::vector &newCurvAbs, VecCoord &x) { type::vector xP_noticeable; - type::vector nbP_density; + type::vector nbP_density; l_adaptiveInterpolation->getSamplingParameters(xP_noticeable, nbP_density); diff --git a/src/BeamAdapter/component/engine/WireRestShape.h b/src/BeamAdapter/component/engine/WireRestShape.h index 8f56a7267..86d69ce66 100644 --- a/src/BeamAdapter/component/engine/WireRestShape.h +++ b/src/BeamAdapter/component/engine/WireRestShape.h @@ -99,7 +99,7 @@ class WireRestShape : public core::objectmodel::BaseObject * This function provides a type::vector with the curviliar abscissa of the noticeable point(s) * and the minimum density (number of points) between them. (Nb. nbP_density.size() == xP_noticeable.size() - 1) */ - void getSamplingParameters(type::vector& xP_noticeable, type::vector& nbP_density) const ; + void getSamplingParameters(type::vector& xP_noticeable, type::vector& nbP_density) const ; /// Functions enabling to load and use a geometry given from OBJ external file @@ -134,7 +134,7 @@ class WireRestShape : public core::objectmodel::BaseObject public: - Data > d_density; + Data > d_density; Data > d_keyPoints; /// Vector or links to the Wire section material. The order of the linked material will define the WireShape structure. diff --git a/src/BeamAdapter/component/engine/WireRestShape.inl b/src/BeamAdapter/component/engine/WireRestShape.inl index 586bcb60c..8e4e12a8a 100644 --- a/src/BeamAdapter/component/engine/WireRestShape.inl +++ b/src/BeamAdapter/component/engine/WireRestShape.inl @@ -174,7 +174,7 @@ bool WireRestShape::initTopology() template void WireRestShape::getSamplingParameters(type::vector& xP_noticeable, - type::vector& nbP_density) const + type::vector& nbP_density) const { xP_noticeable = d_keyPoints.getValue(); nbP_density = d_density.getValue(); From 2e7e9ecb51ee7ad626845e0a0615c0f9dcbd82ab Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Wed, 16 Apr 2025 16:13:40 +0900 Subject: [PATCH 03/24] same with shape --- src/BeamAdapter/component/engine/WireRestShape.h | 12 ++++++------ .../component/engine/WireRestShape.inl | 12 ++++++------ .../component/model/BaseRodSectionMaterial.h | 8 ++++---- src/BeamAdapter/component/model/RodMeshSection.h | 2 +- .../component/model/RodMeshSection.inl | 2 +- src/BeamAdapter/component/model/RodSpireSection.h | 2 +- .../component/model/RodSpireSection.inl | 15 +-------------- .../component/model/RodStraightSection.h | 2 +- .../component/model/RodStraightSection.inl | 15 +-------------- 9 files changed, 22 insertions(+), 48 deletions(-) diff --git a/src/BeamAdapter/component/engine/WireRestShape.h b/src/BeamAdapter/component/engine/WireRestShape.h index 86d69ce66..55dd6dc29 100644 --- a/src/BeamAdapter/component/engine/WireRestShape.h +++ b/src/BeamAdapter/component/engine/WireRestShape.h @@ -83,16 +83,16 @@ class WireRestShape : public core::objectmodel::BaseObject /////////////////////////// Methods of WireRestShape ////////////////////////////////////////// /// This function is called by the force field to evaluate the rest position of each beam - void getRestTransformOnX(Transform &global_H_local, const Real &x); + void getRestTransformOnX(Transform &global_H_local, const Real x); /// Returns the BeamSection @sa m_beamSection corresponding to the given curvilinear abscissa, will call @sa BaseRodSectionMaterial::getBeamSection - [[nodiscard]] const BeamSection& getBeamSectionAtX(const Real& x_curv) const; + [[nodiscard]] const BeamSection& getBeamSectionAtX(const Real x_curv) const; /// Returns the BeamSection data depending on the beam position at the given curvilinear abscissa, will call @sa BaseRodSectionMaterial::getInterpolationParameters - void getInterpolationParametersAtX(const Real& x_curv, Real &_A, Real &_Iy , Real &_Iz, Real &_Asy, Real &_Asz, Real &_J) const; + void getInterpolationParametersAtX(const Real x_curv, Real &_A, Real &_Iy , Real &_Iz, Real &_Asy, Real &_Asz, Real &_J) const; /// Returns the Young modulus, Poisson's ratio and massDensity coefficient of the section at the given curvilinear abscissa, will call @sa BaseRodSectionMaterial::getMechanicalParameters - void getMechanicalParametersAtX(const Real& x_curv, Real& youngModulus, Real& cPoisson, Real& massDensity) const; + void getMechanicalParametersAtX(const Real x_curv, Real& youngModulus, Real& cPoisson, Real& massDensity) const; /** @@ -107,8 +107,8 @@ class WireRestShape : public core::objectmodel::BaseObject Real getLength() ; - void getCollisionSampling(Real &dx, const Real &x_curv); - void getNumberOfCollisionSegment(Real &dx, unsigned int &numLines) ; + void getCollisionSampling(Real &dx, const Real x_curv); + void getNumberOfCollisionSegment(Real &dx, sofa::Size& numLines) ; diff --git a/src/BeamAdapter/component/engine/WireRestShape.inl b/src/BeamAdapter/component/engine/WireRestShape.inl index 8e4e12a8a..61a3e1377 100644 --- a/src/BeamAdapter/component/engine/WireRestShape.inl +++ b/src/BeamAdapter/component/engine/WireRestShape.inl @@ -182,7 +182,7 @@ void WireRestShape::getSamplingParameters(type::vector& xP_noti template -void WireRestShape::getCollisionSampling(Real &dx, const Real &x_curv) +void WireRestShape::getCollisionSampling(Real &dx, const Real x_curv) { unsigned int numLines; Real x_used = x_curv - EPSILON; @@ -229,7 +229,7 @@ void WireRestShape::getCollisionSampling(Real &dx, const Real &x_curv template -void WireRestShape::getRestTransformOnX(Transform &global_H_local, const Real &x) +void WireRestShape::getRestTransformOnX(Transform &global_H_local, const Real x) { Real x_used = x - EPSILON; @@ -255,7 +255,7 @@ void WireRestShape::getRestTransformOnX(Transform &global_H_local, co template -const BeamSection& WireRestShape::getBeamSectionAtX(const Real& x_curv) const +const BeamSection& WireRestShape::getBeamSectionAtX(const Real x_curv) const { const Real x_used = x_curv - Real(EPSILON); const type::vector& keyPts = d_keyPoints.getValue(); @@ -276,7 +276,7 @@ const BeamSection& WireRestShape::getBeamSectionAtX(const Real& x_cur template -void WireRestShape::getInterpolationParametersAtX(const Real& x_curv, Real &_A, Real &_Iy , Real &_Iz, Real &_Asy, Real &_Asz, Real &_J) const +void WireRestShape::getInterpolationParametersAtX(const Real x_curv, Real &_A, Real &_Iy , Real &_Iz, Real &_Asy, Real &_Asz, Real &_J) const { const Real x_used = x_curv - Real(EPSILON); const type::vector& keyPts = d_keyPoints.getValue(); @@ -295,7 +295,7 @@ void WireRestShape::getInterpolationParametersAtX(const Real& x_curv, template -void WireRestShape::getMechanicalParametersAtX(const Real& x_curv, Real& youngModulus, Real& cPoisson, Real& massDensity) const +void WireRestShape::getMechanicalParametersAtX(const Real x_curv, Real& youngModulus, Real& cPoisson, Real& massDensity) const { const Real x_used = x_curv - Real(EPSILON); const type::vector& keyPts = d_keyPoints.getValue(); @@ -321,7 +321,7 @@ typename WireRestShape::Real WireRestShape::getLength() template -void WireRestShape::getNumberOfCollisionSegment(Real &dx, unsigned int &numLines) +void WireRestShape::getNumberOfCollisionSegment(Real &dx, sofa::Size& numLines) { numLines = 0; for (sofa::Size i = 0; i < l_sectionMaterials.size(); ++i) diff --git a/src/BeamAdapter/component/model/BaseRodSectionMaterial.h b/src/BeamAdapter/component/model/BaseRodSectionMaterial.h index 6f03bd7fc..280e48d26 100644 --- a/src/BeamAdapter/component/model/BaseRodSectionMaterial.h +++ b/src/BeamAdapter/component/model/BaseRodSectionMaterial.h @@ -72,13 +72,13 @@ class BaseRodSectionMaterial : public core::objectmodel::BaseObject /////////////////////////// Geometry and physics Getter ////////////////////////////////////////// /// Returns the number of visual edges of this section. To be set or computed by child. - [[nodiscard]] int getNbVisualEdges() const { return d_nbEdgesVisu.getValue(); } + [[nodiscard]] auto getNbVisualEdges() const { return d_nbEdgesVisu.getValue(); } /// Returns the number of collision edges of this section. To be set or computed by child. - [[nodiscard]] int getNbCollisionEdges() const { return d_nbEdgesCollis.getValue(); } + [[nodiscard]] auto getNbCollisionEdges() const { return d_nbEdgesCollis.getValue(); } /// Returns the total length of this section. To be set or computed by child. - [[nodiscard]] Real getLength() const { return d_length.getValue(); } + [[nodiscard]] auto getLength() const { return d_length.getValue(); } /// Returns the BeamSection @sa m_beamSection corresponding to this section @@ -91,7 +91,7 @@ class BaseRodSectionMaterial : public core::objectmodel::BaseObject void getMechanicalParameters(Real& youngModulus, Real& cPoisson, Real& massDensity) const; /// This function is called to get the rest position of the beam depending on the current curved abscisse given in parameter - virtual void getRestTransformOnX(Transform& global_H_local, const Real& x_used, const Real& x_start) + virtual void getRestTransformOnX(Transform& global_H_local, const Real x_used, const Real x_start) { SOFA_UNUSED(global_H_local); SOFA_UNUSED(x_used); diff --git a/src/BeamAdapter/component/model/RodMeshSection.h b/src/BeamAdapter/component/model/RodMeshSection.h index 476cfcf73..da8183b9f 100644 --- a/src/BeamAdapter/component/model/RodMeshSection.h +++ b/src/BeamAdapter/component/model/RodMeshSection.h @@ -54,7 +54,7 @@ class RodMeshSection : public BaseRodSectionMaterial RodMeshSection(); /// Override method to get the rest position of the beam. In this implementation, it will interpolate along the loaded mesh geometry - void getRestTransformOnX(Transform& global_H_local, const Real& x_used, const Real& x_start) override; + void getRestTransformOnX(Transform& global_H_local, const Real x_used, const Real x_start) override; protected: /// Internal method to init the section. Called by @sa BaseRodSectionMaterial::init() method diff --git a/src/BeamAdapter/component/model/RodMeshSection.inl b/src/BeamAdapter/component/model/RodMeshSection.inl index e7dd498b3..81b57bd0d 100644 --- a/src/BeamAdapter/component/model/RodMeshSection.inl +++ b/src/BeamAdapter/component/model/RodMeshSection.inl @@ -62,7 +62,7 @@ bool RodMeshSection::initSection() template -void RodMeshSection::getRestTransformOnX(Transform& global_H_local, const Real& x_used, const Real& x_start) +void RodMeshSection::getRestTransformOnX(Transform& global_H_local, const Real x_used, const Real x_start) { Real abs_curr = x_used - x_start; abs_curr = abs_curr /(this->d_length.getValue()) * m_absOfGeometry; diff --git a/src/BeamAdapter/component/model/RodSpireSection.h b/src/BeamAdapter/component/model/RodSpireSection.h index b333c0a41..83a9c631e 100644 --- a/src/BeamAdapter/component/model/RodSpireSection.h +++ b/src/BeamAdapter/component/model/RodSpireSection.h @@ -51,7 +51,7 @@ class RodSpireSection : public BaseRodSectionMaterial RodSpireSection(); /// Override method to get the rest position of the beam. In this implementation, it will compute the current position given the spire parameters - void getRestTransformOnX(Transform& global_H_local, const Real& x_used, const Real& x_start) override; + void getRestTransformOnX(Transform& global_H_local, const Real x_used, const Real x_start) override; protected: /// Internal method to init the section. Called by @sa BaseRodSectionMaterial::init() method diff --git a/src/BeamAdapter/component/model/RodSpireSection.inl b/src/BeamAdapter/component/model/RodSpireSection.inl index 93ca2a933..8bc409a67 100644 --- a/src/BeamAdapter/component/model/RodSpireSection.inl +++ b/src/BeamAdapter/component/model/RodSpireSection.inl @@ -48,25 +48,12 @@ bool RodSpireSection::initSection() return false; } - if (int nbrEdgesVisu = this->d_nbEdgesVisu.getValue() <= 0) - { - msg_warning() << "Number of visual edges has been set to an invalid value: " << nbrEdgesVisu << ". Value should be a positive integer. Setting to default value: 10"; - this->d_nbEdgesVisu.setValue(10); - } - - - if (int nbEdgesCollis = this->d_nbEdgesCollis.getValue() <= 0) - { - msg_warning() << "Number of collision edges has been set to an invalid value: " << nbEdgesCollis << ". Value should be a positive integer. Setting to default value: 20"; - this->d_nbEdgesCollis.setValue(10); - } - return true; } template -void RodSpireSection::getRestTransformOnX(Transform& global_H_local, const Real& x_used, const Real& x_start) +void RodSpireSection::getRestTransformOnX(Transform& global_H_local, const Real x_used, const Real x_start) { Real projetedLength = d_spireDiameter.getValue() * M_PI; Real lengthSpire = sqrt(d_spireHeight.getValue() * d_spireHeight.getValue() + projetedLength * projetedLength); diff --git a/src/BeamAdapter/component/model/RodStraightSection.h b/src/BeamAdapter/component/model/RodStraightSection.h index 7da823642..e5fe157bb 100644 --- a/src/BeamAdapter/component/model/RodStraightSection.h +++ b/src/BeamAdapter/component/model/RodStraightSection.h @@ -48,7 +48,7 @@ class RodStraightSection : public BaseRodSectionMaterial RodStraightSection(); /// Override method to get the rest position of the beam. In this implementation, it will basically returns Vec3(x_start + x_used, 0 0) - void getRestTransformOnX(Transform& global_H_local, const Real& x_used, const Real& x_start) override; + void getRestTransformOnX(Transform& global_H_local, const Real x_used, const Real x_start) override; protected: /// Internal method to init the section. Called by @sa BaseRodSectionMaterial::init() method diff --git a/src/BeamAdapter/component/model/RodStraightSection.inl b/src/BeamAdapter/component/model/RodStraightSection.inl index 67b96249a..7d939fd00 100644 --- a/src/BeamAdapter/component/model/RodStraightSection.inl +++ b/src/BeamAdapter/component/model/RodStraightSection.inl @@ -46,25 +46,12 @@ bool RodStraightSection::initSection() return false; } - if (int nbrEdgesVisu = this->d_nbEdgesVisu.getValue() <= 0) - { - msg_warning() << "Number of visual edges has been set to an invalid value: " << nbrEdgesVisu << ". Value should be a positive integer. Setting to default value: 10"; - this->d_nbEdgesVisu.setValue(10); - } - - - if (int nbEdgesCollis = this->d_nbEdgesCollis.getValue() <= 0) - { - msg_warning() << "Number of collision edges has been set to an invalid value: " << nbEdgesCollis << ". Value should be a positive integer. Setting to default value: 20"; - this->d_nbEdgesCollis.setValue(10); - } - return true; } template -void RodStraightSection::getRestTransformOnX(Transform& global_H_local, const Real& x_used, const Real& x_start) +void RodStraightSection::getRestTransformOnX(Transform& global_H_local, const Real x_used, const Real x_start) { global_H_local.set(type::Vec3(x_start + x_used, 0.0, 0.0), Quat()); } From d019b6973fe748564412c70ae8e6a5a106cff8b9 Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Fri, 25 Apr 2025 01:16:22 +0900 Subject: [PATCH 04/24] [InterventionalRadiologyController] Remove remnant of the "broken" feature (#176) --- .../InterventionalRadiologyController.h | 1 - .../InterventionalRadiologyController.inl | 63 +++++++++---------- 2 files changed, 29 insertions(+), 35 deletions(-) diff --git a/src/BeamAdapter/component/controller/InterventionalRadiologyController.h b/src/BeamAdapter/component/controller/InterventionalRadiologyController.h index 359f5b5f0..3e2bcddd9 100644 --- a/src/BeamAdapter/component/controller/InterventionalRadiologyController.h +++ b/src/BeamAdapter/component/controller/InterventionalRadiologyController.h @@ -186,7 +186,6 @@ class InterventionalRadiologyController : public sofa::component::controller::Me unsigned int m_currentSensorData; type::vector m_nodeCurvAbs; type::vector< type::vector > m_idInstrumentCurvAbsTable; - unsigned int m_numControlledNodes; // Excluding the nodes that are "dropped" }; #if !defined(SOFA_PLUGIN_BEAMADAPTER_INTERVENTIONALRADIOCONTROLLER_CPP) diff --git a/src/BeamAdapter/component/controller/InterventionalRadiologyController.inl b/src/BeamAdapter/component/controller/InterventionalRadiologyController.inl index 026b06fd1..fb7f8a1e9 100644 --- a/src/BeamAdapter/component/controller/InterventionalRadiologyController.inl +++ b/src/BeamAdapter/component/controller/InterventionalRadiologyController.inl @@ -213,7 +213,6 @@ void InterventionalRadiologyController::bwdInit() WriteAccessor > x = *this->mState->write(sofa::core::vec_id::write_access::position); for(unsigned int i=0; i::bwdInit() nbrBeam += nb; } - if (nbrBeam > m_numControlledNodes) - { - msg_warning() << "Parameter missmatch: According to the list of controlled instrument controlled. The number of potential beams: " - << nbrBeam << " exceed the number of degree of freedom in the MechanicalObject: " << m_numControlledNodes << ". This could lead to unespected behavior."; - } - applyInterventionalRadiologyController(); sofa::core::objectmodel::BaseObject::d_componentState.setValue(sofa::core::objectmodel::ComponentState::Valid); @@ -775,22 +768,23 @@ void InterventionalRadiologyController::applyInterventionalRadiologyC Data* datax = this->getMechanicalState()->write(sofa::core::vec_id::write_access::position); auto x = sofa::helper::getWriteOnlyAccessor(*datax); VecCoord xbuf = x.ref(); - - sofa::Size nbrCurvAbs = newCurvAbs.size(); // number of simulated nodes - if (nbrCurvAbs > x.size()) + + const sofa::Size numberOfNodes = x.size(); + sofa::Size numberOfSimulatedNodes = newCurvAbs.size(); // number of simulated nodes + if (numberOfSimulatedNodes > numberOfNodes) { - msg_warning() << "Parameters missmatch. There are more curv abscisses '" << nbrCurvAbs << "' than the number of dof: " << x.size(); - nbrCurvAbs = x.size(); + msg_warning() << "Parameters missmatch. There are more curv abscisses '" << numberOfSimulatedNodes << "' than the number of dof: " << numberOfNodes; + numberOfSimulatedNodes = numberOfNodes; } - const sofa::Size prev_nbrCurvAbs = m_nodeCurvAbs.size(); // previous number of simulated nodes; + const sofa::Size prev_numberOfSimulatedNodes = m_nodeCurvAbs.size(); // previous number of simulated nodes; - const sofa::Size nbrUnactiveNode = (m_numControlledNodes > nbrCurvAbs) ? m_numControlledNodes - nbrCurvAbs : 0; // m_numControlledNodes == nbr Dof | nbr of CurvAbs > 0 - const sofa::Size prev_nbrUnactiveNode = (m_numControlledNodes > prev_nbrCurvAbs) ? m_numControlledNodes - prev_nbrCurvAbs : 0; + const sofa::Size numberOfUnactiveNodes = numberOfNodes - numberOfSimulatedNodes; // m_numControlledNodes == nbr Dof | nbr of CurvAbs > 0 + const sofa::Size prev_numberOfUnactiveNodes = numberOfNodes - prev_numberOfSimulatedNodes; - for (sofa::Index xId = 0; xId < nbrCurvAbs; xId++) + for (sofa::Index xId = 0; xId < numberOfSimulatedNodes; xId++) { - const sofa::Index globalNodeId = nbrUnactiveNode + xId; // position of the curvAbs in the dof buffer filled by the end + const sofa::Index globalNodeId = numberOfUnactiveNodes + xId; // position of the curvAbs in the dof buffer filled by the end const Real xCurvAbs = modifiedCurvAbs[xId]; if ((xCurvAbs - std::numeric_limits::epsilon()) > m_nodeCurvAbs.back() + threshold) @@ -811,7 +805,7 @@ void InterventionalRadiologyController::applyInterventionalRadiologyC break; } - sofa::Index prev_globalNodeId = prev_nbrUnactiveNode + prev_xId; + sofa::Index prev_globalNodeId = prev_numberOfUnactiveNodes + prev_xId; const Real prev_xCurvAbs = m_nodeCurvAbs[prev_xId]; if (fabs(prev_xCurvAbs - xCurvAbs) < threshold) @@ -856,36 +850,37 @@ void InterventionalRadiologyController::applyInterventionalRadiologyC // ## STEP 4: Assign the beams helper::AdvancedTimer::stepBegin("step4"); - sofa::Size nbrBeam = newCurvAbs.size() - 1; // number of simulated beams - const sofa::Size numEdges = m_numControlledNodes - 1; + sofa::Size numberOfBeams = newCurvAbs.size() - 1; // number of simulated beams + const sofa::Size numberOfEdges = x.size() - 1; - if (numEdges < nbrBeam) // verify that there is a sufficient number of Edge in the topology : TODO if not, modify topo ! + if (numberOfEdges < numberOfBeams) // verify that there is a sufficient number of Edge in the topology : TODO if not, modify topo ! { - msg_error() << "Not enough edges in the topology. Only: " << numEdges << " while nbrBeam = " << nbrBeam << ". Will simulate only " << numEdges << " beams."; - nbrBeam = numEdges; + msg_error() << "Not enough edges in the topology. Only: " << numberOfEdges << " while nbrBeam = " << numberOfBeams << ". Will simulate only " << numberOfEdges << " beams."; + + numberOfBeams = numberOfEdges; } const type::vector& rotInstruments = d_rotationInstrument.getValue(); - for (unsigned int b=0; b< nbrBeam; b++) + for (unsigned int b=0; b< numberOfBeams; b++) { - const Real& x0 = newCurvAbs[b]; - const Real& x1 = newCurvAbs[b+1]; + const Real x0 = newCurvAbs[b]; + const Real x1 = newCurvAbs[b+1]; for (unsigned int i=0; i(xmin- threshold) && x0<(xmax+ threshold) && x1>(xmin- threshold) && x1<(xmax+ threshold)) { - BaseMeshTopology::EdgeID eID = (BaseMeshTopology::EdgeID)(numEdges - nbrBeam + b); + const auto eID = static_cast(numberOfEdges - numberOfBeams + b); - Real length = x1 - x0; - Real x0_local = x0-xmin; - Real x1_local = x1-xmin; + const Real length = x1 - x0; + const Real x0_local = x0-xmin; + const Real x1_local = x1-xmin; - Real theta = rotInstruments[i]; + const Real theta = rotInstruments[i]; m_instrumentsList[i]->addBeam(eID, length, x0_local, x1_local,theta ); } @@ -896,7 +891,7 @@ void InterventionalRadiologyController::applyInterventionalRadiologyC // ## STEP 5: Fix the not simulated nodes helper::AdvancedTimer::stepBegin("step5"); - unsigned int firstSimulatedNode = m_numControlledNodes - nbrBeam; + unsigned int firstSimulatedNode = numberOfNodes - numberOfBeams; // => 1. Fix the nodes (beginning of the instruments) that are not "out" fixFirstNodesWithUntil(firstSimulatedNode); From efacdc85219e70646279caef3c792f6bda4c8185 Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Fri, 25 Apr 2025 16:14:29 +0900 Subject: [PATCH 05/24] [Wire Shape] Fix the confusion between the nbEdgesCollision and the number of mechanical Beams (#177) * make link for topo * compat for nbbeams --- .../component/BaseBeamInterpolation.h | 2 + src/BeamAdapter/component/BeamInterpolation.h | 1 + .../component/BeamInterpolation.inl | 18 ++- .../component/WireBeamInterpolation.h | 5 + .../InterventionalRadiologyController.h | 4 + .../InterventionalRadiologyController.inl | 38 +++++-- .../component/engine/WireRestShape.h | 12 +- .../component/engine/WireRestShape.inl | 103 +++++++++++++----- .../AdaptiveBeamForceFieldAndMass.inl | 4 +- .../mapping/MultiAdaptiveBeamMapping.inl | 6 +- .../component/model/BaseRodSectionMaterial.h | 6 +- .../model/BaseRodSectionMaterial.inl | 11 +- 12 files changed, 156 insertions(+), 54 deletions(-) diff --git a/src/BeamAdapter/component/BaseBeamInterpolation.h b/src/BeamAdapter/component/BaseBeamInterpolation.h index f49c7522e..d7d8b7369 100644 --- a/src/BeamAdapter/component/BaseBeamInterpolation.h +++ b/src/BeamAdapter/component/BaseBeamInterpolation.h @@ -114,6 +114,8 @@ class BaseBeamInterpolation : public virtual sofa::core::objectmodel::BaseObject Real getLength(const EdgeID edgeInList); void setLength(const EdgeID edgeInList, Real& length); + virtual void getMechanicalSampling(Real& dx, const Real x_localcurv_abs) = 0; + /// Collision information using @sa d_beamCollision virtual void getCollisionSampling(Real& dx, const Real x_localcurv_abs) = 0; void addCollisionOnBeam(const sofa::Index beam); diff --git a/src/BeamAdapter/component/BeamInterpolation.h b/src/BeamAdapter/component/BeamInterpolation.h index 2be0fc5b9..a73409573 100644 --- a/src/BeamAdapter/component/BeamInterpolation.h +++ b/src/BeamAdapter/component/BeamInterpolation.h @@ -199,6 +199,7 @@ class BeamInterpolation : public BaseBeamInterpolation virtual void getSamplingParameters(type::vector& xP_noticeable, type::vector& nbP_density) override; Real getRestTotalLength() override; + void getMechanicalSampling(Real& dx, const Real x_localcurv_abs) override; void getCollisionSampling(Real &dx, const Real x_localcurv_abs) override; void getNumberOfCollisionSegment(Real &dx, unsigned int &numLines) override; diff --git a/src/BeamAdapter/component/BeamInterpolation.inl b/src/BeamAdapter/component/BeamInterpolation.inl index 9af09e6c6..e064858bc 100644 --- a/src/BeamAdapter/component/BeamInterpolation.inl +++ b/src/BeamAdapter/component/BeamInterpolation.inl @@ -420,17 +420,29 @@ typename BeamInterpolation::Real BeamInterpolation::getRes return le; } + +template +void BeamInterpolation::getMechanicalSampling(Real& dx, const Real x_localcurv_abs) +{ + SOFA_UNUSED(x_localcurv_abs); + + const auto numLines = this->m_topologyEdges->size(); + dx = getRestTotalLength()/numLines; +} + template -void BeamInterpolation::getCollisionSampling(Real &dx, const Real /*x_localcurv_abs*/) +void BeamInterpolation::getCollisionSampling(Real &dx, const Real x_localcurv_abs) { - unsigned int numLines = 30; + SOFA_UNUSED(x_localcurv_abs); + + const auto numLines = this->m_topologyEdges->size(); dx = getRestTotalLength()/numLines; } template void BeamInterpolation::getNumberOfCollisionSegment(Real &dx, unsigned int &numLines) { - numLines = 30; + numLines = static_cast(this->m_topologyEdges->size()); dx = getRestTotalLength()/numLines; } diff --git a/src/BeamAdapter/component/WireBeamInterpolation.h b/src/BeamAdapter/component/WireBeamInterpolation.h index 6e7b0ba71..592d0364e 100644 --- a/src/BeamAdapter/component/WireBeamInterpolation.h +++ b/src/BeamAdapter/component/WireBeamInterpolation.h @@ -117,6 +117,11 @@ class WireBeamInterpolation : public BaseBeamInterpolation return this->m_restShape->getLength(); } + void getMechanicalSampling(Real &dx, const Real x_localcurv_abs) override + { + this->m_restShape->getMechanicalSampling(dx,x_localcurv_abs); + } + void getCollisionSampling(Real &dx, const Real x_localcurv_abs) override { this->m_restShape->getCollisionSampling(dx,x_localcurv_abs); diff --git a/src/BeamAdapter/component/controller/InterventionalRadiologyController.h b/src/BeamAdapter/component/controller/InterventionalRadiologyController.h index 3e2bcddd9..d01a8666b 100644 --- a/src/BeamAdapter/component/controller/InterventionalRadiologyController.h +++ b/src/BeamAdapter/component/controller/InterventionalRadiologyController.h @@ -180,6 +180,10 @@ class InterventionalRadiologyController : public sofa::component::controller::Me SingleLink< InterventionalRadiologyController, FixedProjectiveConstraint, BaseLink::FLAG_STOREPATH | BaseLink::FLAG_STRONGLINK> l_fixedConstraint; + SingleLink< + InterventionalRadiologyController, sofa::core::topology::BaseMeshTopology, + BaseLink::FLAG_STOREPATH | BaseLink::FLAG_STRONGLINK> l_mechanicalTopology; + DeprecatedAndRemoved m_fixedConstraint; type::vector m_sensorMotionData; diff --git a/src/BeamAdapter/component/controller/InterventionalRadiologyController.inl b/src/BeamAdapter/component/controller/InterventionalRadiologyController.inl index fb7f8a1e9..96362df09 100644 --- a/src/BeamAdapter/component/controller/InterventionalRadiologyController.inl +++ b/src/BeamAdapter/component/controller/InterventionalRadiologyController.inl @@ -70,6 +70,7 @@ InterventionalRadiologyController::InterventionalRadiologyController( , d_motionFilename(initData(&d_motionFilename, "motionFilename", "text file that includes tracked motion from optical sensor")) , d_indexFirstNode(initData(&d_indexFirstNode, (unsigned int) 0, "indexFirstNode", "first node (should be fixed with restshape)")) , l_fixedConstraint(initLink("fixedConstraint", "Path to the FixedProjectiveConstraint")) +, l_mechanicalTopology(initLink("topology", "Path to the mechanical topology")) { m_sensored =false; } @@ -80,6 +81,23 @@ void InterventionalRadiologyController::init() { BaseContext* context = getContext(); this->mState = nullptr; + + if(!l_mechanicalTopology) + { + msg_info() << "topology (path to the mechanical topology) has not been set, searching for one in the context."; + l_mechanicalTopology.set(this->getContext()->getMeshTopologyLink()); + } + + if (l_mechanicalTopology) + { + msg_info() << "Found topology named "<< l_mechanicalTopology->getName() ; + } + 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; + } //get the pointers of the WireBeamInterpolations const type::vector& instrumentPathList = d_instrumentsPath.getValue(); @@ -100,7 +118,8 @@ void InterventionalRadiologyController::init() } } - if (m_instrumentsList.empty()) { + if (m_instrumentsList.empty()) + { msg_error() << "No instrument found (no WireBeamInterpolation)! the component can not work and will be set to Invalid."; sofa::core::objectmodel::BaseObject::d_componentState.setValue(sofa::core::objectmodel::ComponentState::Invalid); return; @@ -567,7 +586,7 @@ void InterventionalRadiologyController::interventionalRadiologyCollis for (int i = static_cast(xPointList.size()) - 1; i>=0; i--) { //1. we determin if the poin ument - int instrumentId = idInstrumentList[i]; + const int instrumentId = idInstrumentList[i]; // x_max for the instrument that is controlled (not dropped part) Real xMaxControlled = m_instrumentsList[instrumentId]->getRestTotalLength(); @@ -585,9 +604,9 @@ void InterventionalRadiologyController::interventionalRadiologyCollis xPointList[i] = xAbsCurv - xBegin; // provides the "local" curv absc of the point (on the instrument reference) idInstrumentList[i] = firstInstruOnx; - // 3. we look for the collision sampling of the current instrument in order to "place" the following point + // 3. we look for the mechanical sampling of the current instrument in order to "place" the following point Real xIncr; - m_instrumentsList[firstInstruOnx]->getCollisionSampling(xIncr, xPointList[i]); + m_instrumentsList[firstInstruOnx]->getMechanicalSampling(xIncr, xPointList[i]); xAbsCurv -= xIncr; // the following point could not have x_abs_curv<0; @@ -626,7 +645,7 @@ void InterventionalRadiologyController::interventionalRadiologyCollis for (unsigned int i=0; igetRestTotalLength(); @@ -709,7 +728,7 @@ void InterventionalRadiologyController::applyInterventionalRadiologyC // Create vectors with the CurvAbs of the noticiable points and the id of the corresponding instrument type::vector newCurvAbs; - type::vector> idInstrumentTable; + type::vector> idInstrumentTable; // i.e for each node -> [instID0, instID1...] // ## STEP 1: Find the total length of the COMBINED INSTRUMENTS and the one for which xtip > 0 (so the one which are simulated) helper::AdvancedTimer::stepBegin("step1"); @@ -767,10 +786,11 @@ void InterventionalRadiologyController::applyInterventionalRadiologyC // => Get write access to current nodes/dofs Data* datax = this->getMechanicalState()->write(sofa::core::vec_id::write_access::position); auto x = sofa::helper::getWriteOnlyAccessor(*datax); - VecCoord xbuf = x.ref(); + const VecCoord xbuf = x.ref(); // make a copy of the positions, as it will changed meanwhile const sofa::Size numberOfNodes = x.size(); sofa::Size numberOfSimulatedNodes = newCurvAbs.size(); // number of simulated nodes + if (numberOfSimulatedNodes > numberOfNodes) { msg_warning() << "Parameters missmatch. There are more curv abscisses '" << numberOfSimulatedNodes << "' than the number of dof: " << numberOfNodes; @@ -1012,7 +1032,7 @@ void InterventionalRadiologyController::fillInstrumentCurvAbsTable(co xBegin -= threshold; xEnd += threshold; - // check curvAbs sorted value, if value is inside [xBegin, xBegin] of the tool add it to instrumentList. + // check curvAbs sorted value, if value is inside [xBegin, xEnd] of the tool add it to instrumentList. for (unsigned int i = 0; i < curvAbs.size(); i++) { if (curvAbs[i] < xBegin) // still not inside range @@ -1066,7 +1086,7 @@ const type::vector< type::vector >& InterventionalRadiologyController int InterventionalRadiologyController::getTotalNbEdges() const { - return getContext()->getMeshTopology()->getNbEdges(); + return l_mechanicalTopology->getNbEdges(); } diff --git a/src/BeamAdapter/component/engine/WireRestShape.h b/src/BeamAdapter/component/engine/WireRestShape.h index 55dd6dc29..95041e685 100644 --- a/src/BeamAdapter/component/engine/WireRestShape.h +++ b/src/BeamAdapter/component/engine/WireRestShape.h @@ -107,10 +107,10 @@ class WireRestShape : public core::objectmodel::BaseObject Real getLength() ; - void getCollisionSampling(Real &dx, const Real x_curv); - void getNumberOfCollisionSegment(Real &dx, sofa::Size& numLines) ; - - + + void getMechanicalSampling(Real& dx, const Real x_localcurv_abs); + void getCollisionSampling(Real &dx, const Real x_curv); + void getNumberOfCollisionSegment(Real &dx, sofa::Size& numLines); /////////////////////////// Deprecated Methods ////////////////////////////////////////// @@ -142,9 +142,7 @@ class WireRestShape : public core::objectmodel::BaseObject private: /// Link to be set to the topology container in the component graph. - SingleLink, TopologyContainer, BaseLink::FLAG_STOREPATH | BaseLink::FLAG_STRONGLINK> l_topology; - /// Pointer to the topology container, should be set using @sa l_topology, otherwise will search for one in current Node. - TopologyContainer* _topology{ nullptr }; + SingleLink, sofa::core::topology::BaseMeshTopology, BaseLink::FLAG_STOREPATH | BaseLink::FLAG_STRONGLINK> l_topology; }; diff --git a/src/BeamAdapter/component/engine/WireRestShape.inl b/src/BeamAdapter/component/engine/WireRestShape.inl index 61a3e1377..4d8f20eab 100644 --- a/src/BeamAdapter/component/engine/WireRestShape.inl +++ b/src/BeamAdapter/component/engine/WireRestShape.inl @@ -58,7 +58,8 @@ WireRestShape::WireRestShape() , l_sectionMaterials(initLink("wireMaterials", "link to Wire Section Materials (to be ordered according to the instrument, from handle to tip)")) , l_topology(initLink("topology", "link to the topology container")) { - + d_density.setReadOnly(true); // density is supposed to be filled using the section materials + d_keyPoints.setReadOnly(true); // key points are supposed to be filled using the section materials } @@ -70,16 +71,15 @@ void WireRestShape::init() ////////////////////////////////////////////// ////////// get and fill local topology /////// ////////////////////////////////////////////// - - // Get pointer to given topology using the link. If not found will search in current context. - _topology = l_topology.get(); - if (!_topology) - this->getContext()->get(_topology); + if (!l_topology) + { + l_topology.set(this->getContext()->getMeshTopologyLink()); + } - if(_topology != nullptr) + if (l_topology) { - msg_info() << "found topology named "<< _topology->getName() ; + msg_info() << "Found topology named "<< l_topology->getName() ; } else { @@ -94,6 +94,16 @@ void WireRestShape::init() this->d_componentState.setValue(sofa::core::objectmodel::ComponentState::Invalid); return; } + + // Workaround around the fact that d_density and d_keyPoints are supposed to be output only + if(d_density.isSet()) + { + msg_warning() << "The density field will be ignored (output only Data)."; + } + if(d_keyPoints.isSet()) + { + msg_warning() << "The keyPoints field will be ignored (output only Data)."; + } //////////////////////////////////////////////////////// @@ -124,7 +134,7 @@ void WireRestShape::initLengths() { auto rodSection = l_sectionMaterials.get(i); keyPointList[i+1] = keyPointList[i] + rodSection->getLength(); - densityList[i] = rodSection->getNbCollisionEdges(); + densityList[i] = rodSection->getNbBeams(); } } @@ -133,8 +143,8 @@ template bool WireRestShape::initTopology() { /// fill topology : - _topology->clear(); - _topology->cleanup(); + l_topology->clear(); + l_topology->cleanup(); const type::vector& keyPts = d_keyPoints.getValue(); if (l_sectionMaterials.size() != keyPts.size() - 1) @@ -155,12 +165,12 @@ bool WireRestShape::initTopology() // add points from the material for (int i = startPtId; i < nbrVisuEdges + 1; i++) { - _topology->addPoint(prev_length + i * dx, 0, 0); + l_topology->addPoint(prev_length + i * dx, 0, 0); } // add segments from the material for (int i = prev_edges; i < prev_edges + nbrVisuEdges; i++) { - _topology->addEdge(i, i + 1); + l_topology->addEdge(i, i + 1); } prev_length = length; @@ -181,10 +191,51 @@ void WireRestShape::getSamplingParameters(type::vector& xP_noti } +template +void WireRestShape::getMechanicalSampling(Real &dx, const Real x_curv) +{ + unsigned int numLines = 0; + Real x_used = x_curv - EPSILON; + + const Real totalLength = this->getLength(); + x_used = std::clamp(x_used, 0.0, totalLength); + + const type::vector& keyPts = d_keyPoints.getValue(); + + // verify that size of number of materials == size of keyPoints-1 + if (l_sectionMaterials.size() != keyPts.size() - 1) + { + msg_error() << "Problem size of number of materials: " << l_sectionMaterials.size() + << " != size of keyPoints-1 " << keyPts.size()-1 + << ". Returning default values."; + numLines = 20; + dx = totalLength / numLines; + return; + } + + // Check in which section x_used belongs to and get access to this section material + for (sofa::Size i = 1; i< keyPts.size(); ++i) + { + if (x_used <= keyPts[i]) + { + numLines = l_sectionMaterials.get(i-1)->getNbBeams(); + + Real length = fabs(keyPts[i] - keyPts[i-1]); + dx = length / numLines; + return; + } + } + + // If x_used is out of bounds. Warn user and returns default value. + numLines = 20; + dx = totalLength / numLines; + msg_error() << " problem in getMechanicalSampling : x_curv " << x_used << " is not between keyPoints" << d_keyPoints.getValue(); +} + template void WireRestShape::getCollisionSampling(Real &dx, const Real x_curv) { - unsigned int numLines; + unsigned int numLines = 0; Real x_used = x_curv - EPSILON; const Real totalLength = this->getLength(); @@ -227,6 +278,17 @@ void WireRestShape::getCollisionSampling(Real &dx, const Real x_curv) msg_error() << " problem in getCollisionSampling : x_curv " << x_used << " is not between keyPoints" << d_keyPoints.getValue(); } +template +void WireRestShape::getNumberOfCollisionSegment(Real &dx, sofa::Size& numLines) +{ + numLines = 0; + for (sofa::Size i = 0; i < l_sectionMaterials.size(); ++i) + { + numLines += l_sectionMaterials.get(i)->getNbCollisionEdges(); + } + dx = getLength() / numLines; +} + template void WireRestShape::getRestTransformOnX(Transform &global_H_local, const Real x) @@ -319,19 +381,6 @@ typename WireRestShape::Real WireRestShape::getLength() return d_keyPoints.getValue().back(); } - -template -void WireRestShape::getNumberOfCollisionSegment(Real &dx, sofa::Size& numLines) -{ - numLines = 0; - for (sofa::Size i = 0; i < l_sectionMaterials.size(); ++i) - { - numLines += l_sectionMaterials.get(i)->getNbCollisionEdges(); - } - dx = getLength() / numLines; -} - - template void WireRestShape::computeOrientation(const Vec3& AB, const Quat& Q, Quat &result) { diff --git a/src/BeamAdapter/component/forcefield/AdaptiveBeamForceFieldAndMass.inl b/src/BeamAdapter/component/forcefield/AdaptiveBeamForceFieldAndMass.inl index 757b96a45..b72f54b73 100644 --- a/src/BeamAdapter/component/forcefield/AdaptiveBeamForceFieldAndMass.inl +++ b/src/BeamAdapter/component/forcefield/AdaptiveBeamForceFieldAndMass.inl @@ -60,7 +60,7 @@ AdaptiveBeamForceFieldAndMass::AdaptiveBeamForceFieldAndMass() , m_defaultMassDensity(Real(1.)) , d_massDensity(initData(&d_massDensity,type::vector(1, m_defaultMassDensity),"massDensity", "Density of the mass" )) , d_useShearStressComputation(initData(&d_useShearStressComputation, true, "shearStressComputation","if false, suppress the shear stress in the computation")) - , d_reinforceLength(initData(&d_reinforceLength, false, "reinforceLength", "if true, a separate computation for the error in elongation is peformed")) + , d_reinforceLength(initData(&d_reinforceLength, false, "reinforceLength", "if true, a separate computation for the error in elongation is performed")) , l_interpolation(initLink("interpolation","Path to the Interpolation component on scene")) { @@ -507,7 +507,7 @@ void AdaptiveBeamForceFieldAndMass::addForce (const MechanicalParams* ///* Calculer les rotation et les transformations ///* Calculer la matrice "locale" ///* Calculer la force exercée par chaque beam - ///* Calculer la force exercée par la gravitée + ///* Calculer la force exercée par la gravité for (sofa::Index beamId=0; beamId ::assignSubMappingFromControllerInfo() } else { - msg_error() << "Trying to remove baseEdge which is alreay empty. This case is not supposed to happened."; + msg_error() << "Trying to remove baseEdge which is already empty. This case is not supposed to happened."; } if (edgeToRemove.size()>0) @@ -308,7 +308,7 @@ void MultiAdaptiveBeamMapping< TIn, TOut>::init() unsigned int numSeg, numLinesInstrument; numSeg=0; InReal DX=0; - // we chose the collision parameters of the most discrestized instrument + // we chose the collision parameters of the most discretized instrument for (unsigned int i=0; i::addBaryPoint(const int& edgeId,const V int nbUnControlledEdges = totalNbEdges - nbControlledEdge; assert(nbUnControlledEdges>=0); - if (edgeId < (totalNbEdges-nbControlledEdge) ) + if (edgeId < nbUnControlledEdges ) { //if the edge in question is not under control, dont need to compute for collision } diff --git a/src/BeamAdapter/component/model/BaseRodSectionMaterial.h b/src/BeamAdapter/component/model/BaseRodSectionMaterial.h index 280e48d26..03a450ad6 100644 --- a/src/BeamAdapter/component/model/BaseRodSectionMaterial.h +++ b/src/BeamAdapter/component/model/BaseRodSectionMaterial.h @@ -43,7 +43,7 @@ using sofa::core::loader::MeshLoader; * Method @sa initSection and @sa getRestTransformOnX should be overriden to provide the correct creation and interpolation. * * The rod section is described by: - * - Topology parameters: vertices and edges @sa d_nbEdgesVisu and @sa d_nbEdgesCollis + * - Topology parameters: vertices and edges @sa d_nbEdgesVisu, @sa d_nbEdgesCollis and @sa d_nbBeams * - Geometry parameters: radius @sa d_radius, @sa d_innerRadius and length @sa d_length * - Mechanical parameters: @sa d_poissonRatio and @sa d_youngModulus */ @@ -76,6 +76,9 @@ class BaseRodSectionMaterial : public core::objectmodel::BaseObject /// Returns the number of collision edges of this section. To be set or computed by child. [[nodiscard]] auto getNbCollisionEdges() const { return d_nbEdgesCollis.getValue(); } + + /// Returns the number of collision edges of this section. To be set or computed by child. + [[nodiscard]] auto getNbBeams() const { return d_nbBeams.getValue(); } /// Returns the total length of this section. To be set or computed by child. [[nodiscard]] auto getLength() const { return d_length.getValue(); } @@ -111,6 +114,7 @@ class BaseRodSectionMaterial : public core::objectmodel::BaseObject Data d_innerRadius; ///< Data defining the geometry internal radius of this section is hollow Data d_length; ///< Data defining the geometry length of this section + Data d_nbBeams; ///< Data defining the number of (mechanical) beams composing this section Data d_nbEdgesVisu; ///< Data defining the number of visual edges composing this section Data d_nbEdgesCollis; ///< Data defining the number of collision edges composing this section diff --git a/src/BeamAdapter/component/model/BaseRodSectionMaterial.inl b/src/BeamAdapter/component/model/BaseRodSectionMaterial.inl index 957363dcc..b9adaac51 100644 --- a/src/BeamAdapter/component/model/BaseRodSectionMaterial.inl +++ b/src/BeamAdapter/component/model/BaseRodSectionMaterial.inl @@ -34,8 +34,9 @@ BaseRodSectionMaterial::BaseRodSectionMaterial() , d_radius(initData(&d_radius, (Real)1.0, "radius", "Full radius of this section")) , d_innerRadius(initData(&d_innerRadius, (Real)0.0, "innerRadius", "Inner radius of this section if hollow")) , d_length(initData(&d_length, (Real)1.0, "length", "Total length of this section")) - , d_nbEdgesVisu(initData(&d_nbEdgesVisu, (Size)10, "nbEdgesVisu", "number of Edges for the visual model")) - , d_nbEdgesCollis(initData(&d_nbEdgesCollis, (Size)20, "nbEdgesCollis", "number of Edges for the collision model")) + , d_nbBeams(initData(&d_nbBeams, (Size)5, "nbBeams", "Number of Beams for the mechanical model")) + , d_nbEdgesVisu(initData(&d_nbEdgesVisu, (Size)10, "nbEdgesVisu", "Number of Edges for the visual model")) + , d_nbEdgesCollis(initData(&d_nbEdgesCollis, (Size)20, "nbEdgesCollis", "Number of Edges for the collision model")) { } @@ -46,6 +47,12 @@ void BaseRodSectionMaterial::init() { this->d_componentState.setValue(sofa::core::objectmodel::ComponentState::Loading); + if(!d_nbBeams.isSet()) + { + msg_deprecated() << "nbBeams is now required but it was not set. Its value will be copied from nbEdgesCollis as a temporary compatibility solution."; + d_nbBeams.setValue(d_nbEdgesCollis.getValue()); + } + // Prepare beam sections double r = this->d_radius.getValue(); double rInner = this->d_innerRadius.getValue(); From a3ad75d19fd98f69a13b6296072533636a37757e Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Fri, 25 Apr 2025 17:53:30 +0900 Subject: [PATCH 06/24] [IRC] Add checks/warnings at init stage (#178) * fetch total number of potential beams * add warnings --- src/BeamAdapter/component/BeamInterpolation.h | 2 +- .../component/BeamInterpolation.inl | 1 - .../component/WireBeamInterpolation.h | 6 ++++ .../component/WireBeamInterpolation.inl | 2 +- .../InterventionalRadiologyController.inl | 32 ++++++++++++------- .../component/engine/WireRestShape.h | 2 ++ .../component/engine/WireRestShape.inl | 11 +++++++ 7 files changed, 41 insertions(+), 15 deletions(-) diff --git a/src/BeamAdapter/component/BeamInterpolation.h b/src/BeamAdapter/component/BeamInterpolation.h index a73409573..5da83f227 100644 --- a/src/BeamAdapter/component/BeamInterpolation.h +++ b/src/BeamAdapter/component/BeamInterpolation.h @@ -202,7 +202,7 @@ class BeamInterpolation : public BaseBeamInterpolation void getMechanicalSampling(Real& dx, const Real x_localcurv_abs) override; void getCollisionSampling(Real &dx, const Real x_localcurv_abs) override; void getNumberOfCollisionSegment(Real &dx, unsigned int &numLines) override; - + void setTransformBetweenDofAndNode(const sofa::Index beam, const Transform &DOF_H_Node, unsigned int zeroORone ); void getSplineRestTransform(const EdgeID edgeInList, Transform &local_H_local0_rest, Transform &local_H_local1_rest) override; diff --git a/src/BeamAdapter/component/BeamInterpolation.inl b/src/BeamAdapter/component/BeamInterpolation.inl index e064858bc..066f5eae4 100644 --- a/src/BeamAdapter/component/BeamInterpolation.inl +++ b/src/BeamAdapter/component/BeamInterpolation.inl @@ -446,7 +446,6 @@ void BeamInterpolation::getNumberOfCollisionSegment(Real &dx, unsigne dx = getRestTotalLength()/numLines; } - template void BeamInterpolation::getInterpolationParameters(sofa::Index beamId, Real& _L, Real& _A, Real& _Iy, Real& _Iz, Real& _Asy, Real& _Asz, Real& _J) diff --git a/src/BeamAdapter/component/WireBeamInterpolation.h b/src/BeamAdapter/component/WireBeamInterpolation.h index 592d0364e..42240b39d 100644 --- a/src/BeamAdapter/component/WireBeamInterpolation.h +++ b/src/BeamAdapter/component/WireBeamInterpolation.h @@ -131,6 +131,12 @@ class WireBeamInterpolation : public BaseBeamInterpolation { this->m_restShape->getNumberOfCollisionSegment(dx,numLines); } + + // this is the number of beams which can be simulated according to the rest shape (and its sections) + sofa::Size getTotalNumberOfPossibleBeams() const + { + return this->m_restShape->getTotalNumberOfBeams(); + } virtual void getRestTransform(const EdgeID edgeInList, Transform &local0_H_local1_rest); diff --git a/src/BeamAdapter/component/WireBeamInterpolation.inl b/src/BeamAdapter/component/WireBeamInterpolation.inl index fdd3443e3..2a791aa6a 100644 --- a/src/BeamAdapter/component/WireBeamInterpolation.inl +++ b/src/BeamAdapter/component/WireBeamInterpolation.inl @@ -57,7 +57,7 @@ void WireBeamInterpolation::init() this->d_componentState.setValue(sofa::core::objectmodel::ComponentState::Invalid); return; } - + type::vector xP_noticeable; type::vector nbP_density; diff --git a/src/BeamAdapter/component/controller/InterventionalRadiologyController.inl b/src/BeamAdapter/component/controller/InterventionalRadiologyController.inl index 96362df09..0005a64b7 100644 --- a/src/BeamAdapter/component/controller/InterventionalRadiologyController.inl +++ b/src/BeamAdapter/component/controller/InterventionalRadiologyController.inl @@ -223,27 +223,35 @@ void InterventionalRadiologyController::bwdInit() stPos.getOrientation().normalize(); d_startingPos.setValue(stPos); - if (!this->mState) { + if (!this->mState) + { msg_error() << "No MechanicalState found. The component can not work and will be set to Invalid."; sofa::core::objectmodel::BaseObject::d_componentState.setValue(sofa::core::objectmodel::ComponentState::Invalid); return; } + + // check if the provided mechanical state and topology can manage our tools + sofa::Size requiredNumberOfEdges = 0; + for (const auto* instrument : m_instrumentsList) + { + requiredNumberOfEdges += instrument->getTotalNumberOfPossibleBeams(); + } + + if(requiredNumberOfEdges + 1 > this->mState->getSize()) + { + msg_warning() << "The associated Mechanical Object does not contain enough nodes (" << this->mState->getSize() + << ") whereas the provided tool(s) need(s) at least " << requiredNumberOfEdges + 1 << " nodes."; + } + if(requiredNumberOfEdges > this->l_mechanicalTopology->getNbEdges()) + { + msg_warning() << "The associated Topology does not contain enough edges (" << this->l_mechanicalTopology->getNbEdges() + << ") whereas the provided tool(s) need(s) at least " << requiredNumberOfEdges << " edges."; + } WriteAccessor > x = *this->mState->write(sofa::core::vec_id::write_access::position); for(unsigned int i=0; i xP_noticeable_I; - type::vector density_I; - m_instrumentsList[i]->getSamplingParameters(xP_noticeable_I, density_I); - - for (auto nb : density_I) - nbrBeam += nb; - } - applyInterventionalRadiologyController(); sofa::core::objectmodel::BaseObject::d_componentState.setValue(sofa::core::objectmodel::ComponentState::Valid); diff --git a/src/BeamAdapter/component/engine/WireRestShape.h b/src/BeamAdapter/component/engine/WireRestShape.h index 95041e685..f866a226e 100644 --- a/src/BeamAdapter/component/engine/WireRestShape.h +++ b/src/BeamAdapter/component/engine/WireRestShape.h @@ -111,6 +111,8 @@ class WireRestShape : public core::objectmodel::BaseObject void getMechanicalSampling(Real& dx, const Real x_localcurv_abs); void getCollisionSampling(Real &dx, const Real x_curv); void getNumberOfCollisionSegment(Real &dx, sofa::Size& numLines); + sofa::Size getTotalNumberOfBeams() const; + /////////////////////////// Deprecated Methods ////////////////////////////////////////// diff --git a/src/BeamAdapter/component/engine/WireRestShape.inl b/src/BeamAdapter/component/engine/WireRestShape.inl index 4d8f20eab..3fed22982 100644 --- a/src/BeamAdapter/component/engine/WireRestShape.inl +++ b/src/BeamAdapter/component/engine/WireRestShape.inl @@ -289,6 +289,17 @@ void WireRestShape::getNumberOfCollisionSegment(Real &dx, sofa::Size& dx = getLength() / numLines; } +template +sofa::Size WireRestShape::getTotalNumberOfBeams() const +{ + sofa::Size numBeams = 0; + for (sofa::Size i = 0; i < l_sectionMaterials.size(); ++i) + { + numBeams += l_sectionMaterials.get(i)->getNbBeams(); + } + + return numBeams; +} template void WireRestShape::getRestTransformOnX(Transform &global_H_local, const Real x) From ca9d181718565b619e543eddd1659dbe2da0e036 Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Mon, 28 Apr 2025 09:58:28 +0900 Subject: [PATCH 07/24] wip --- .../InterventionalRadiologyController.inl | 61 ++++++++++++++++--- 1 file changed, 51 insertions(+), 10 deletions(-) diff --git a/src/BeamAdapter/component/controller/InterventionalRadiologyController.inl b/src/BeamAdapter/component/controller/InterventionalRadiologyController.inl index 0005a64b7..918f7db39 100644 --- a/src/BeamAdapter/component/controller/InterventionalRadiologyController.inl +++ b/src/BeamAdapter/component/controller/InterventionalRadiologyController.inl @@ -128,6 +128,7 @@ void InterventionalRadiologyController::init() { msg_info() << m_instrumentsList.size() << " instrument(s) found (WireBeamInterpolation)"; } + const auto numberOfInstruments = m_instrumentsList.size(); m_activatedPointsBuf.clear(); @@ -143,15 +144,44 @@ void InterventionalRadiologyController::init() loadMotionData(d_motionFilename.getValue()); } - auto x_instr_tip = sofa::helper::getWriteOnlyAccessor(d_xTip); - x_instr_tip.resize(m_instrumentsList.size()); - - auto angle_Instrument = sofa::helper::getWriteOnlyAccessor(d_rotationInstrument); - angle_Instrument.resize(m_instrumentsList.size()); - - for(unsigned int i=0; isetControlled(true); + auto checkData = [&](auto& data) + { + if(data.isSet()) + { + const auto& values = data.getValue(); + const auto valueSize = values.size(); + + if(valueSize != numberOfInstruments) + { + msg_warning() << "Discrepancy for " << data.getName() << " value: it manages " << valueSize << " tools, but there are " << numberOfInstruments << " defined in instrumentPathList."; + if(valueSize > numberOfInstruments) + { + msg_warning() << "The superfluous values will be ignored."; + } + else + { + msg_warning() << "The missing values will be set as zero."; + } + } + } + }; + + { + checkData(d_xTip); + auto xTip = sofa::helper::getWriteOnlyAccessor(d_xTip); + xTip.resize(numberOfInstruments); + } + { + checkData(d_rotationInstrument); + auto angle_Instrument = sofa::helper::getWriteOnlyAccessor(d_rotationInstrument); + angle_Instrument.resize(numberOfInstruments); + } + for(auto* instrument : m_instrumentsList) + { + instrument->setControlled(true); + } + if (!l_fixedConstraint) { typename FixedProjectiveConstraint::SPtr fixedConstraint{}; @@ -176,10 +206,21 @@ void InterventionalRadiologyController::init() m_nodeCurvAbs.clear(); m_idInstrumentCurvAbsTable.clear(); + + // initiliaze current curvAbs + // if there are deployed at start (xtip set) then it should reflect that. + for(const auto xTip : d_xTip.getValue()) + { + if(xTip > 0.0) + { + m_nodeCurvAbs.push_back(xTip); + } + } + // it should be terminated always by zero (origin) m_nodeCurvAbs.push_back(0.0); + type::vector listInit; - - for(unsigned int i=0; i Date: Mon, 28 Apr 2025 13:50:22 +0900 Subject: [PATCH 08/24] fix different xtip --- .../InterventionalRadiologyController.h | 1 - .../InterventionalRadiologyController.inl | 90 +++++++++++-------- 2 files changed, 52 insertions(+), 39 deletions(-) diff --git a/src/BeamAdapter/component/controller/InterventionalRadiologyController.h b/src/BeamAdapter/component/controller/InterventionalRadiologyController.h index d01a8666b..5ea336dfb 100644 --- a/src/BeamAdapter/component/controller/InterventionalRadiologyController.h +++ b/src/BeamAdapter/component/controller/InterventionalRadiologyController.h @@ -86,7 +86,6 @@ class InterventionalRadiologyController : public sofa::component::controller::Me ////////////////////// Inherited from BaseObject /////////////////////////////////////////////// virtual void init() override ; - virtual void bwdInit() override ; //////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/BeamAdapter/component/controller/InterventionalRadiologyController.inl b/src/BeamAdapter/component/controller/InterventionalRadiologyController.inl index 918f7db39..8282af6ef 100644 --- a/src/BeamAdapter/component/controller/InterventionalRadiologyController.inl +++ b/src/BeamAdapter/component/controller/InterventionalRadiologyController.inl @@ -43,6 +43,7 @@ #include +#include namespace beamadapter @@ -208,16 +209,38 @@ void InterventionalRadiologyController::init() m_idInstrumentCurvAbsTable.clear(); // initiliaze current curvAbs - // if there are deployed at start (xtip set) then it should reflect that. - for(const auto xTip : d_xTip.getValue()) + // if there are to be deployed at start (xtip set) then it should reflect that. + // it should always start with zero (origin) + m_nodeCurvAbs.push_back(0.0); + const auto& xTips = d_xTip.getValue(); + for(const auto xTip : xTips | std::views::reverse) { if(xTip > 0.0) { m_nodeCurvAbs.push_back(xTip); } } - // it should be terminated always by zero (origin) - m_nodeCurvAbs.push_back(0.0); + + // initialize curvAbsNode <-> instrument ID table + // i,e identify for each "simulated" node the corresponding tool(s) + // if xtip not initialized then only the 0 (origin) node is considered, and has all the tools + // and as before, if they are deployed at start (xtip set), then we need to take it into account + for(sofa::Index i = 0 ; i < m_nodeCurvAbs.size(); i++) + { + type::vector listTool; + for(sofa::Index id = 0 ; id < numberOfInstruments ; id++) + { + const auto xTip = xTips[id]; + + if(xTip >= m_nodeCurvAbs[i]) + { + listTool.push_back(id); + } + } + + m_idInstrumentCurvAbsTable.push_back(listTool); + } + type::vector listInit; for(unsigned int i=0; i::init() m_idInstrumentCurvAbsTable.push_back(listInit); Inherit::init(); - - sofa::core::objectmodel::BaseObject::d_componentState.setValue(sofa::core::objectmodel::ComponentState::Valid); -} - -template -void InterventionalRadiologyController::loadMotionData(std::string filename) -{ - if (!helper::system::DataRepository.findFile(filename)) - { - msg_error() << "File " << filename << " not found."; - return; - } - std::ifstream file(filename.c_str()); - - std::string line; - Vec3 result; - while( std::getline(file,line) ) - { - if (line.empty()) - continue; - std::istringstream values(line); - values >> result[0] >> result[1] >> result[2]; - result[0] /= 1000; - m_sensorMotionData.push_back(result); - } - - file.close(); -} - - -template -void InterventionalRadiologyController::bwdInit() -{ - // assign the starting pos to each point of the Mechanical State + Coord stPos =d_startingPos.getValue(); stPos.getOrientation().normalize(); d_startingPos.setValue(stPos); @@ -298,6 +288,30 @@ void InterventionalRadiologyController::bwdInit() sofa::core::objectmodel::BaseObject::d_componentState.setValue(sofa::core::objectmodel::ComponentState::Valid); } +template +void InterventionalRadiologyController::loadMotionData(std::string filename) +{ + if (!helper::system::DataRepository.findFile(filename)) + { + msg_error() << "File " << filename << " not found."; + return; + } + std::ifstream file(filename.c_str()); + + std::string line; + Vec3 result; + while( std::getline(file,line) ) + { + if (line.empty()) + continue; + std::istringstream values(line); + values >> result[0] >> result[1] >> result[2]; + result[0] /= 1000; + m_sensorMotionData.push_back(result); + } + + file.close(); +} /*! * \todo fix the mouse event with better controls From 978b4bf8f31f0203b9d972da5733f9ff8b3cd6b4 Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Tue, 29 Apr 2025 15:14:31 +0900 Subject: [PATCH 09/24] fix warnings --- examples/SingleBeamDeployment.scn | 4 ++-- examples/SingleBeamDeploymentCollision.scn | 4 ++-- examples/Tool_from_loader.scn | 2 +- examples/python3/SingleBeamDeployment.py | 4 ++-- examples/python3/SingleBeamDeploymentCollision.py | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/examples/SingleBeamDeployment.scn b/examples/SingleBeamDeployment.scn index d84ea4732..06ec1623a 100644 --- a/examples/SingleBeamDeployment.scn +++ b/examples/SingleBeamDeployment.scn @@ -36,8 +36,8 @@ diff --git a/examples/SingleBeamDeploymentCollision.scn b/examples/SingleBeamDeploymentCollision.scn index f8afde1da..5fb2c7f8f 100644 --- a/examples/SingleBeamDeploymentCollision.scn +++ b/examples/SingleBeamDeploymentCollision.scn @@ -54,8 +54,8 @@ diff --git a/examples/Tool_from_loader.scn b/examples/Tool_from_loader.scn index 1b09e0984..1b8a6db69 100644 --- a/examples/Tool_from_loader.scn +++ b/examples/Tool_from_loader.scn @@ -51,7 +51,7 @@ diff --git a/examples/python3/SingleBeamDeployment.py b/examples/python3/SingleBeamDeployment.py index daf321da4..a0a566449 100644 --- a/examples/python3/SingleBeamDeployment.py +++ b/examples/python3/SingleBeamDeployment.py @@ -41,8 +41,8 @@ def createScene(rootNode): BeamMechanics.addObject('WireBeamInterpolation', name='BeamInterpolation', WireRestShape='@../EdgeTopology/BeamRestShape', printLog=False) BeamMechanics.addObject('AdaptiveBeamForceFieldAndMass', name='BeamForceField', massDensity=0.00000155, interpolation='@BeamInterpolation') BeamMechanics.addObject('InterventionalRadiologyController', name='DeployController', template='Rigid3d', instruments='BeamInterpolation', - startingPos=[0, 0, 0, 0, 0, 0, 1], xtip=[0, 0, 0], printLog=True, - rotationInstrument=[0, 0, 0], step=0.5, speed=0.5, + startingPos=[0, 0, 0, 0, 0, 0, 1], xtip=[0], printLog=True, + rotationInstrument=[0], step=0.5, speed=0.5, listening=True, controlledInstrument=0) BeamMechanics.addObject('FixedProjectiveConstraint', indices=0, name='FixedConstraint') BeamMechanics.addObject('RestShapeSpringsForceField', name="RestSPForceField", points='@DeployController.indexFirstNode', angularStiffness=1e8, stiffness=1e8) diff --git a/examples/python3/SingleBeamDeploymentCollision.py b/examples/python3/SingleBeamDeploymentCollision.py index 79c532aea..1d5ef1297 100644 --- a/examples/python3/SingleBeamDeploymentCollision.py +++ b/examples/python3/SingleBeamDeploymentCollision.py @@ -49,8 +49,8 @@ def createScene(rootNode): BeamMechanics.addObject('WireBeamInterpolation', name='BeamInterpolation', WireRestShape='@../EdgeTopology/BeamRestShape', printLog=False) BeamMechanics.addObject('AdaptiveBeamForceFieldAndMass', name='BeamForceField', massDensity=0.00000155, interpolation='@BeamInterpolation') BeamMechanics.addObject('InterventionalRadiologyController', name='DeployController', template='Rigid3d', instruments='BeamInterpolation', - startingPos=[0, 0, 0, 0, 0, 0, 1], xtip=[0, 0, 0], printLog=True, - rotationInstrument=[0, 0, 0], step=5., speed=5., + startingPos=[0, 0, 0, 0, 0, 0, 1], xtip=[0], printLog=True, + rotationInstrument=[0], step=5., speed=5., listening=True, controlledInstrument=0) BeamMechanics.addObject('LinearSolverConstraintCorrection', wire_optimization='true', printLog=False) BeamMechanics.addObject('FixedProjectiveConstraint', indices=0, name='FixedConstraint') From 029a09b43aacab3fcd37301231b49de98d2404df Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Fri, 2 May 2025 16:35:31 +0900 Subject: [PATCH 10/24] [Scenes] Fix runtime warnings, and consistent behavior between python and xml (#179) * fix warnings, behavior in scenes * remove nbBeams deprecation msg and nb nodes/edges warnings * fix regression tests * remove info msg about topology --- examples/3instruments.scn | 16 ++++---- examples/3instruments_collis.scn | 37 +++++++++++-------- examples/SingleBeamDeployment.scn | 6 +-- examples/SingleBeamDeploymentCollision.scn | 6 +-- examples/Tool_from_loader.scn | 6 +-- examples/python3/SingleBeamDeployment.py | 12 +++--- .../python3/SingleBeamDeploymentCollision.py | 12 +++--- 7 files changed, 51 insertions(+), 44 deletions(-) diff --git a/examples/3instruments.scn b/examples/3instruments.scn index 755c085ec..e1030a6e9 100644 --- a/examples/3instruments.scn +++ b/examples/3instruments.scn @@ -19,11 +19,11 @@ - + - - + + @@ -33,8 +33,8 @@ - - + + @@ -44,8 +44,8 @@ - - + + @@ -60,7 +60,7 @@ - + - - + + @@ -49,8 +49,8 @@ - - + + @@ -60,8 +60,8 @@ - - + + @@ -142,14 +142,21 @@ - - - - - - - - + + + + + + + + + + + + + + + diff --git a/examples/SingleBeamDeployment.scn b/examples/SingleBeamDeployment.scn index d84ea4732..3957063b1 100644 --- a/examples/SingleBeamDeployment.scn +++ b/examples/SingleBeamDeployment.scn @@ -15,8 +15,8 @@ - - + + @@ -36,7 +36,7 @@ diff --git a/examples/SingleBeamDeploymentCollision.scn b/examples/SingleBeamDeploymentCollision.scn index f8afde1da..b169a8b8a 100644 --- a/examples/SingleBeamDeploymentCollision.scn +++ b/examples/SingleBeamDeploymentCollision.scn @@ -32,8 +32,8 @@ - - + + @@ -54,7 +54,7 @@ diff --git a/examples/Tool_from_loader.scn b/examples/Tool_from_loader.scn index 1b09e0984..f8a9a2005 100644 --- a/examples/Tool_from_loader.scn +++ b/examples/Tool_from_loader.scn @@ -25,8 +25,8 @@ - - + + @@ -39,7 +39,7 @@ - diff --git a/examples/python3/SingleBeamDeployment.py b/examples/python3/SingleBeamDeployment.py index daf321da4..246ae7e7d 100644 --- a/examples/python3/SingleBeamDeployment.py +++ b/examples/python3/SingleBeamDeployment.py @@ -12,14 +12,14 @@ def createScene(rootNode): topoLines = rootNode.addChild('EdgeTopology') topoLines.addObject('RodStraightSection', name='StraightSection', length=980.0, radius=0.9, - nbEdgesCollis=30, nbEdgesVisu=196, - youngModulus=20000) + nbBeams=30, nbEdgesCollis=30, nbEdgesVisu=196, + youngModulus=20000, massDensity=0.00000155) topoLines.addObject('RodSpireSection', name='SpireSection', length=20.0, radius=0.9, - nbEdgesCollis=5, nbEdgesVisu=4, + nbBeams=5, nbEdgesCollis=5, nbEdgesVisu=4, spireDiameter=25, spireHeight=0, - youngModulus=20000) + youngModulus=20000, massDensity=0.00000155) topoLines.addObject('WireRestShape', name='BeamRestShape', template="Rigid3d", wireMaterials="@StraightSection @SpireSection") @@ -34,14 +34,14 @@ def createScene(rootNode): BeamMechanics.addObject('EulerImplicitSolver', rayleighStiffness=0.2, printLog=False, rayleighMass=0.1) BeamMechanics.addObject('BTDLinearSolver', verification=False, subpartSolve=False, verbose=False) BeamMechanics.addObject('RegularGridTopology', name='MeshLines', drawEdges=True, - nx=60, ny=1, nz=1, + nx=61, ny=1, nz=1, xmax=0.0, xmin=0.0, ymin=0, ymax=0, zmax=0, zmin=0, p0=[0,0,0]) BeamMechanics.addObject('MechanicalObject', showIndices=False, name='DOFs Container', template='Rigid3d', ry=-90) BeamMechanics.addObject('WireBeamInterpolation', name='BeamInterpolation', WireRestShape='@../EdgeTopology/BeamRestShape', printLog=False) BeamMechanics.addObject('AdaptiveBeamForceFieldAndMass', name='BeamForceField', massDensity=0.00000155, interpolation='@BeamInterpolation') BeamMechanics.addObject('InterventionalRadiologyController', name='DeployController', template='Rigid3d', instruments='BeamInterpolation', - startingPos=[0, 0, 0, 0, 0, 0, 1], xtip=[0, 0, 0], printLog=True, + topology="@MeshLines", startingPos=[0, 0, 0, 0, 0, 0, 1], xtip=[0, 0, 0], printLog=True, rotationInstrument=[0, 0, 0], step=0.5, speed=0.5, listening=True, controlledInstrument=0) BeamMechanics.addObject('FixedProjectiveConstraint', indices=0, name='FixedConstraint') diff --git a/examples/python3/SingleBeamDeploymentCollision.py b/examples/python3/SingleBeamDeploymentCollision.py index 79c532aea..97ea7c2fe 100644 --- a/examples/python3/SingleBeamDeploymentCollision.py +++ b/examples/python3/SingleBeamDeploymentCollision.py @@ -20,14 +20,14 @@ def createScene(rootNode): topoLines = rootNode.addChild('EdgeTopology') topoLines.addObject('RodStraightSection', name='StraightSection', length=980.0, radius=0.9, - nbEdgesCollis=50, nbEdgesVisu=200, - youngModulus=20000, massDensity=0.1, poissonRatio=0.3) + nbBeams=50, nbEdgesCollis=50, nbEdgesVisu=200, + youngModulus=20000, massDensity=0.00000155, poissonRatio=0.3) topoLines.addObject('RodSpireSection', name='SpireSection', length=20.0, radius=0.9, - nbEdgesCollis=10, nbEdgesVisu=200, + nbBeams=10, nbEdgesCollis=10, nbEdgesVisu=200, spireDiameter=25, spireHeight=0, - youngModulus=20000, massDensity=0.1, poissonRatio=0.3) + youngModulus=20000, massDensity=0.00000155, poissonRatio=0.3) topoLines.addObject('WireRestShape', name='BeamRestShape', template="Rigid3d", wireMaterials="@StraightSection @SpireSection") @@ -42,14 +42,14 @@ def createScene(rootNode): BeamMechanics.addObject('EulerImplicitSolver', rayleighStiffness=0.2, rayleighMass=0.1) BeamMechanics.addObject('BTDLinearSolver', verification=False, subpartSolve=False, verbose=False) BeamMechanics.addObject('RegularGridTopology', name='MeshLines', - nx=60, ny=1, nz=1, + nx=61, ny=1, nz=1, xmax=0.0, xmin=0.0, ymin=0, ymax=0, zmax=0, zmin=0, p0=[0,0,0]) BeamMechanics.addObject('MechanicalObject', showIndices=False, name='DOFs', template='Rigid3d', ry=-90) BeamMechanics.addObject('WireBeamInterpolation', name='BeamInterpolation', WireRestShape='@../EdgeTopology/BeamRestShape', printLog=False) BeamMechanics.addObject('AdaptiveBeamForceFieldAndMass', name='BeamForceField', massDensity=0.00000155, interpolation='@BeamInterpolation') BeamMechanics.addObject('InterventionalRadiologyController', name='DeployController', template='Rigid3d', instruments='BeamInterpolation', - startingPos=[0, 0, 0, 0, 0, 0, 1], xtip=[0, 0, 0], printLog=True, + topology="@MeshLines", startingPos=[0, 0, 0, 0, 0, 0, 1], xtip=[0, 0, 0], printLog=True, rotationInstrument=[0, 0, 0], step=5., speed=5., listening=True, controlledInstrument=0) BeamMechanics.addObject('LinearSolverConstraintCorrection', wire_optimization='true', printLog=False) From c1e1464ea4dc316eb21d373098bbd337fa9c4e51 Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Fri, 2 May 2025 16:54:33 +0900 Subject: [PATCH 11/24] Add more regression tests (#180) * fix warnings, behavior in scenes * remove nbBeams deprecation msg and nb nodes/edges warnings * fix regression tests * remove info msg about topology * add more regression tests, and update some other * add pythonpath for running python scenes * fix nb nodes warnings and regenerate refs --------- Co-authored-by: erik pernod --- .github/workflows/ci.yml | 1 + examples/3instruments_collis.scn | 2 +- .../RegressionStateScenes.regression-tests | 8 +++++--- examples/SingleBeamDeployment.scn | 2 +- examples/SingleBeamDeploymentCollision.scn | 2 +- .../RegressionStateScenes.regression-tests | 11 +++++++++++ .../references/3instruments.scn.reference | 0 ...ruments.scn.reference_0_DOFs_mstate.txt.gz | Bin 0 -> 353 bytes ...uments.scn.reference_1_Quads_mstate.txt.gz | Bin 0 -> 3261 bytes ...uments.scn.reference_2_Quads_mstate.txt.gz | Bin 0 -> 1892 bytes ...uments.scn.reference_3_Quads_mstate.txt.gz | Bin 0 -> 140 bytes ..._collis.scn.reference_0_DOFs_mstate.txt.gz | Bin 1954 -> 2203 bytes ...cn.reference_1_CollisionDOFs_mstate.txt.gz | Bin 875 -> 780 bytes ...collis.scn.reference_2_Quads_mstate.txt.gz | Bin 29812 -> 0 bytes ...collis.scn.reference_3_Quads_mstate.txt.gz | Bin 25107 -> 0 bytes ...collis.scn.reference_4_Quads_mstate.txt.gz | Bin 6238 -> 0 bytes regression/references/SingleBeam.py.reference | 0 ...ngleBeam.py.reference_0_DOFs_mstate.txt.gz | Bin 0 -> 4098 bytes .../SingleBeamDeployment.py.reference | 0 ...y.reference_0_DOFs Container_mstate.txt.gz | Bin 0 -> 308 bytes ...n.reference_0_DOFs Container_mstate.txt.gz | Bin 307 -> 304 bytes ...SingleBeamDeploymentCollision.py.reference | 0 ...ollision.py.reference_0_DOFs_mstate.txt.gz | Bin 0 -> 864 bytes ...py.reference_1_CollisionDOFs_mstate.txt.gz | Bin 0 -> 410 bytes ...n.reference_0_DOFs Container_mstate.txt.gz | Bin 799 -> 858 bytes ...cn.reference_1_CollisionDOFs_mstate.txt.gz | Bin 421 -> 409 bytes .../references/Tool_from_loader.scn.reference | 0 ....reference_0_Instrument_DOFs_mstate.txt.gz | Bin 0 -> 7543 bytes ...cn.reference_1_dof_visual_GC_mstate.txt.gz | Bin 0 -> 27222 bytes 29 files changed, 20 insertions(+), 6 deletions(-) create mode 100644 examples/python3/RegressionStateScenes.regression-tests create mode 100644 regression/references/3instruments.scn.reference create mode 100644 regression/references/3instruments.scn.reference_0_DOFs_mstate.txt.gz create mode 100644 regression/references/3instruments.scn.reference_1_Quads_mstate.txt.gz create mode 100644 regression/references/3instruments.scn.reference_2_Quads_mstate.txt.gz create mode 100644 regression/references/3instruments.scn.reference_3_Quads_mstate.txt.gz delete mode 100644 regression/references/3instruments_collis.scn.reference_2_Quads_mstate.txt.gz delete mode 100644 regression/references/3instruments_collis.scn.reference_3_Quads_mstate.txt.gz delete mode 100644 regression/references/3instruments_collis.scn.reference_4_Quads_mstate.txt.gz create mode 100644 regression/references/SingleBeam.py.reference create mode 100644 regression/references/SingleBeam.py.reference_0_DOFs_mstate.txt.gz create mode 100644 regression/references/SingleBeamDeployment.py.reference create mode 100644 regression/references/SingleBeamDeployment.py.reference_0_DOFs Container_mstate.txt.gz create mode 100644 regression/references/SingleBeamDeploymentCollision.py.reference create mode 100644 regression/references/SingleBeamDeploymentCollision.py.reference_0_DOFs_mstate.txt.gz create mode 100644 regression/references/SingleBeamDeploymentCollision.py.reference_1_CollisionDOFs_mstate.txt.gz create mode 100644 regression/references/Tool_from_loader.scn.reference create mode 100644 regression/references/Tool_from_loader.scn.reference_0_Instrument_DOFs_mstate.txt.gz create mode 100644 regression/references/Tool_from_loader.scn.reference_1_dof_visual_GC_mstate.txt.gz diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 78f330233..810f2f9bd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -139,6 +139,7 @@ jobs: # Setup mandatory env vars export REGRESSION_SCENES_DIR="${WORKSPACE_SRC_PATH}/examples" export REGRESSION_REFERENCES_DIR="${WORKSPACE_SRC_PATH}/regression/references" + export PYTHONPATH=$SOFA_ROOT/plugins/SofaPython3/lib/python3/site-packages # Run regression test bench ${SOFA_ROOT}/bin/Regression_test${{ steps.sofa.outputs.exe }} else diff --git a/examples/3instruments_collis.scn b/examples/3instruments_collis.scn index 75cd2461e..98cf3c4b0 100644 --- a/examples/3instruments_collis.scn +++ b/examples/3instruments_collis.scn @@ -76,7 +76,7 @@ - diff --git a/examples/SingleBeamDeploymentCollision.scn b/examples/SingleBeamDeploymentCollision.scn index b169a8b8a..c587e7150 100644 --- a/examples/SingleBeamDeploymentCollision.scn +++ b/examples/SingleBeamDeploymentCollision.scn @@ -46,7 +46,7 @@ - diff --git a/examples/python3/RegressionStateScenes.regression-tests b/examples/python3/RegressionStateScenes.regression-tests new file mode 100644 index 000000000..25ae3ab84 --- /dev/null +++ b/examples/python3/RegressionStateScenes.regression-tests @@ -0,0 +1,11 @@ +# WARNING: +# REGRESSION_TEST DOES NOT SUPPORT DASHES ("-") IN SCENE NAMES. +# USE UNDERSCORES ("_") INSTEAD. + +### References relative path ### +../../regression/references + +### Demo scenes ### +SingleBeam.py 1000 1e-4 1 1 +SingleBeamDeployment.py 10000 1e-4 1 1 +SingleBeamDeploymentCollision.py 7000 1e-4 1 1 \ No newline at end of file diff --git a/regression/references/3instruments.scn.reference b/regression/references/3instruments.scn.reference new file mode 100644 index 000000000..e69de29bb diff --git a/regression/references/3instruments.scn.reference_0_DOFs_mstate.txt.gz b/regression/references/3instruments.scn.reference_0_DOFs_mstate.txt.gz new file mode 100644 index 0000000000000000000000000000000000000000..433d55d98bb1f7f8b737e38a03821bf05beab9d1 GIT binary patch literal 353 zcmb2|=3oE=;WsB2@*Xe{V0M_NS=SgM|7AtANXdWCNgk{RkE%1@-u1jMNAUeBecSb? zq#}RI3OjKqwg^Cp|4FlfM*RQ(X2!-+7DEn~;_!{U5&j1n6bi)O?H5y)-StMxV7Bi6 z{jzSSzI}h*DY5Xc^`^wbZM@QEIX5=I84v$A-Y>c5^l4*%5Z5lISu?!6mQe`6?IP^3)E!7IpPd>eW?Cyp0kh zxK)b8Bz6j&GAUl~yKu=q@AFe1=id6DDw>}#`PP-5X|{GEMM}k=pM-KYMXdKfHs?gy z#?z~11e-hWEeeXc=p*+`v1FF;@ej9UWEiKMm^ZOW@ZoMnNA}yjX;U8vm7c5E;3mg< tjy1J?weR&!pLFJ{oU0a$nziz}(&Wl&7UO;Yz(K*xaP(jG+ijW*3;_0`pSb`4 literal 0 HcmV?d00001 diff --git a/regression/references/3instruments.scn.reference_1_Quads_mstate.txt.gz b/regression/references/3instruments.scn.reference_1_Quads_mstate.txt.gz new file mode 100644 index 0000000000000000000000000000000000000000..7e98af92bcda5a47a68c6c1625ce6e8e28f4e6e9 GIT binary patch literal 3261 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 T0Q~EEW(D-68>D=F3Z}Fv` zo6^|*%i}Rx8TvJy3ose|kX5EurPd7Y=5--(46zGdR_`gBx#6fi*j%=H>*Zd^y(){= zy#UKPsoAh?_na}e11d9{nlKT>jpySTJBj;Clzj0#(Q=sreXZ{*c=XjjAJD z{rZXX$IN4ClbZhrp*n*GQ^%XmT8NLUXksbWvdJgfu0Ob@%IHz8-ARG>_+4~sJ{l#m z8R1?bvnGey%QS^f5}_c+e&xKF%qvd<4{GK28j?mVIgm9$YwA-}~F64V}r5na|*D zfHPElz|ETW54n#_LzjZ)OO?~&R^Ysz>v&`uj+H!gW~d^T-jvMzOF~qC?2pYdL%7i> zu_;%^K2|!I3?UZw=Awo9z9{}FlZk*g5Q%3-&VmMKB2v^R4MNsp$qsQRFg|G7#OU$h zpK2`fE}&G1BHOuVcUmD&VYyBHzDp$HPA)NIx-%1%Eec#Po{sdvtTSrj$#FCr%VVB> z686QVtyGXkVmmM@o&s*nVd#IQBWi#Le$n#yzt!U}Wsdk8tdDmHje|ot*=n`rTO(gt zyTA49Zb4(FI%|b)7ad>+6;i?JRwcdhw+_V1|3|!z`>v&wci4FuKp?htT6)~&gF!tu zj=OG!ynDD)on}xK3k*gdKyRrTN~|5ygK_wEYoN4~ z>HX)(<5Q!I_TjG={q(!r{VFDXikbc)-Y^p7`Xg#bLN47Yu$5%H~P9LWaB`L6l&8 zEHLqfwM#MEoLDFTF)7B(YodnNMZ2-vtPawSbXC8V*`Nm-bSLlXAaCR+mEBm2Me3-s z6-=E`^U9eAO50h}---xRtEZ% ztvn?HQJo5?(bSe|wzp9WvKy5J;i9Q%?A8F*VGV0{2{^mIPaSDv7xE>(T_u(t*EWlw zJu^4oELKm3Yzo#5kcU;2v7B(a!D$Fm?<(nfn|+;crcMlh0DHi#OBQ*rCc8$G`t8ON7 zLO&OH<$+cx)(7Z%8K3WkmDllm;rsaQCk}Fa*xd0jPCH7U>A3R*x)XPG0C~mZhpQXI zyblzosIv3%SzZX5`@@P&hrtMh;U$i?097>B(~{hn0@;fWr>~q2;Jg_1MBJJpm3lWz zm}U$26a6b$-AM11lcT#M@Rj?#Xe^9@l&Xg@uRA8|^*4jx3Y|`|0 z&X>V5a=sBq(#gfB%fWr4dFf{~Kt0RuGZW_E8gmOz zN*)f+VfUHCRpE>g-{QN6s&wB!II-R=S^5WdJ0(~fHnVk@Xx{v1ALmdPpKXE_b3W!- jBQO)^{r&mb+SFotzu1nY-u>f0Y62XH-w(r-0KmThfe^is literal 0 HcmV?d00001 diff --git a/regression/references/3instruments.scn.reference_3_Quads_mstate.txt.gz b/regression/references/3instruments.scn.reference_3_Quads_mstate.txt.gz new file mode 100644 index 0000000000000000000000000000000000000000..19a2760ea9cfa0a57f090e99828bc303b9adab54 GIT binary patch literal 140 zcmb2|=3oE=;WtMO1sM!DST@Lt>Mvw_wTSuOR1y9)J9#S2mrt4pQG-F=SjEr4@c;jt zJBmQ9JS+xwqQM4c9~S7niud*vdG<&-*1YnkS_OtSut0-qI*|VVpP8ZPL&GwGJq!#0 DF3us6 literal 0 HcmV?d00001 diff --git a/regression/references/3instruments_collis.scn.reference_0_DOFs_mstate.txt.gz b/regression/references/3instruments_collis.scn.reference_0_DOFs_mstate.txt.gz index 25287bcb631af56566bd624a7d56c4aee8dd38ad..9916ddd1fb4505d1534733410bd051b258d9b16f 100644 GIT binary patch literal 2203 zcmV;M2xRvkiwFP!000006YR;M4ZtuE13;cBet}dU^r%}DbpJ_`NCm;gCj;BM=ZOaylZLJ6CUI__H5P};99v> zxO|KF()w(jR7vYK_F>~%!z3$$dvwWpRNlDhy@tv1`x{5N*AjZvZ}tk|TY81d&qv=d zJCE(xb3zDpf*$z3rvvp!wSn7BEx4FxJi*eNg+pGw*Ls0tdS%Z7Y=3KkZJ83ifU-j` zP#@jblmk3F-a2FB)8fV*ylL*jUoL;O(9X=;a9x`~L|@Bg&t0#@96*RnKRAI)!Si}f z!AQ8w_IVuqVIlrce`D{CUkJY!+PTvzdm^!9b`RY-r_abo^*weV#ky~-jvgN5k%8-} z*u_NjG+OuRy%)99NAH`P+=~`{qR~`rsz~O^L&1IDT|Y>3TCYszeLqghgf|lQ=IVSy z65IW2pR79ZXj6}l{oXre)iCGCe%>3a@aOga;+K5e7Y_>`8C`j{U#C`#bPiG#+jji{ z^Q!us{hX?89~C-u>W+P>bJI#k7@OvE_N%twpUHbZ=4~(&$PqW`!WX+^_X1E~Cim5I z$6ty{77q*b385w|o64)H#jx?(J=O<^RTZ|ED1RwO{%DtXyERLxrk~#hrX^{n?Oi?! zqO7@jv^ffCyI!s1`JQ39QO-TBBiH0>2fcac1grS;bEtpt8Fj05Xs9>3Hx`tZlmG4h zGbeB}@o8O_uK>1RGEUEthDuJQ0YF`Qu-2(RCS-`X;tNCMe+Ef)TWbe-|>gA>fR-Gg&wIl3Lr}wJ)LE#Yqkt1 zH0tM8BrUjwjb)uI*`nX|P{%;^dT+NleOi3!oLTw7eD)U^rE7xY=f;|^aj#yCv^`~)L^s1-|| zPW!YZ4TK?0vLV&roAAB(4G1U2Tv#dP@?CPLA}xh{IYwtoi=>6VIAouU+Ea3em0p?x zikbmmr|h(aI40M{@R9b-Xwg#SC=t#HP3ScOBy;DI0!+U6+oKCkr9)D)`L!|1mTh;O zC?0gB^!xd7C&VC{i)f4yE9%=PhYUDf!)2cyGMrp`85`YKf*!Cv_=X(4C{(jznz7mj zYZ%Q@%&L!$BhuiU_>e&a6QXT;&XH^;nuc=-891(!1NU+t;3Zk7ti8PX=f>NrjQEk3 zU-Z=vZC~uPV~4&Fm&DabQ&~aMpPPb^5gN5gX^8YM8c`WEl(vS`EcQSUsLU%_MJ+Z* zZD#&DzrrfPP-DYA_Wo*Kiq`BNG|eJ|R*fF_WKZbW!u6RH(e*Fy8}5R4;y53i9|f1@ zYc4dawH8^*R;I-aGV=*T0E5A!yF@`J_G+nuQ={6wOt@{Gy0oZbO=}cHs&A^rM3I)C z%!>R@-sa?#ut!!!j-oaU|JbS+7yVV>P6;Hf0mhJi+T409jUPWufB7I=nU|d1gJjDdI5(6o z_?-!oq5gdL0m`N`&~tB!JKf{Tg=-gOmS-=)G?msN=(N?nyZotJ%^4_0DzD8mQgeC+ zFRAJCN7p^dVj9krb_WF6Gv?gPg69*>x*LIy$S?f`tm2&s5q}?Hqlz(e?PiAkZW*ht zQWZaGx}Q))H5*r=nIS!B^;rO=AE80irOjz+$5*ts9We{^G{`u*M;n<~^4Gw`$Dwb@ zSIor#Q!D192s!vey%`|!{vM}v%Xb2YHai9%Yjw|}hShckKPsb@!MY$6-kUDU1UeJN4)KBsTuUb>dFrl{c=yOrw)Gs>Y!5 zJAxsY(Ish`>G083vNSwY+BXNzKx)Nau9ZzaMB^6x zFqv28Zs|hbC{QV`yF*@lpk>c%%#b|h+ht$Z~9bDJ$)?30N9PAuL z$T|7VIr-^s<=!zfBU7@-xN%)u>j?sEJw-9FeO(Kkx@6K|=FHt<&86+Tc5yu)6=2|k z40YEim+peWJ1*F7%|tcsL?6#6R*%yq_&R^UNT&R{FWw5R$19R>8)O|> zk-4AaZmPMfm+I?!tq?*8A%qY@2qAAg&k8IAhcI1qYop{l-`uld>*c3 zytY}3LH<}&0#Pj6eYWcsSSaw(hOS!Mi7zs{#ri~6h4KyGRG|bMNu%1Q(^7BONx*Wa zJC*f4={`Y+rd1|DOW@aTGu>qbvof5!4faAVo^Rzb9BS>8fxISg?7rM5@(5zf zHY79PiPJBv-#XccG&nmpWlk(sl;?LlzAI2}sT8_=!>w451=~9_mZ5}*4nAyR$&%b? zWFyuLx+2Lh3sex|D5^-LhQoEp$$=xcCUX-7+rCTegl<(UNTti<#uAPXg^r^tas@f@ z^Z`3>-CECffLG>t=V_M>tkX*wvSOMG5q8N)sN6ptCp!EHpO=5^HiP&}10{n(Q5VFI zPuCGWvGm-wa&+LzsWvQ11-)IWieS8(T($(~9V6C8@=yB0=y)nBIx|WH-j)Pp1MiX` z4n1u-wjUjku4)4;5%P%Ou#pf5AVg>&k04 zCZMR)ZPVr8X0H5Cq-cBZE3BU*btcR~LtK(wvU&iEb(IAX;0YH<#ro9kisz1RU5+0OxEi#~G>#{s9_& z4A~%XyKQSYRtj!myWNFeoH=SxNcSxydzNg$HYALct~TgSC`2r+W2Q?u`w1Va`U12u z=*I8Ar+9zw@BO{M_xJz#Vbt6P?)LkK*2NfDi}2E#Aq^{v;n9l3nSt^4NQSw2@21L_ zPAOStp(jhW#d0P*r(9<=#PEW_z3C&U8_4k4U+A7|@E+V$e5P4^!!b+OYaxqlB1Wmv zGz%Pn4n@W@#+W^V!3&&cM8^D1!{~&-7`b`q+T5=|JBWs{fq7ukY<9iG)?sADN1`W| zg|R(X8CUooBuf8SN98TbO?&{~=0i*{;#(QjV|meK8F)5P1N%))JDeA+3|w(ubJ;Re zaIT@@U}q>Q|)XXVllV4jhJv4mHI{wP&ovV-62yZj0fThczw6L~J7zZI2+Gbxg zk;&pImdcn-gYO9)>?6~wr<^;osxh}Cz@FBDXwRJip&8{UzZJ%@TS$e=|-#K6uL1`Vr#+_!+ba-#0^>h=MMk?0RR630GV>Yg@r@_01cnG)&Kwi diff --git a/regression/references/3instruments_collis.scn.reference_1_CollisionDOFs_mstate.txt.gz b/regression/references/3instruments_collis.scn.reference_1_CollisionDOFs_mstate.txt.gz index 84512cf853b7e25fd8f94ab1654f9b30c94f2719..a648774dc1377896e23803cd949535222f161df1 100644 GIT binary patch literal 780 zcmV+n1M~bJiwFP!000006AH0aFyK;9h_F@AH5f&s2tVQi00030|1?y~l>|u&yN?y& zK=Fb<9L9_B-)aym>TR z;?}v68*6ncrF`P(x|jt4$HP5cVt(V8tMoQW)K&F#$OOq6fY154bmvu;Ld86;S1SDc zO7s>uz&Qa~4$U3NXz0y~lP<)4R#KA*6$23oE=3@0B|OeAB}`}72KuQJwdgF!ou-#B zp}s(M2aP_!lOlrZQBY;{X+=|`40|ZvE7X?~xcKc8bmme5jDb-KwL>h% z$^@0{%{p+$R_(>ZjexiHU^IB=`2h2`2Vv9L2#Cx^n^fhBu}d2~!*4+(y9lX)!J-kE z&=3DZEP#ull^-+(W7#HVIpB(II&?58ZK{iaK?x@Z!&;QaE*hOoIF$wftCayQfeLu| z!y-+=p1_1euN|X47h`^oz!;XR}kg%}23|k!>s~Bt@ugAg1kQ`iUEh2_A<;N)+ee*&5fDgR3HPVczAhZA7LhW}c;g}& zH&Fgjqf5hr)qOz7jTcwkr6r_jeR1oriAkO~p6vs0sp~Jp&@DbRHu^E-Ya+RdjA!nj zBnGIp@e-BB@$j2a(%2EN=eSqq2fUldN1||%Fscj&KcEez5}7RJqThLd{8BA|xNq92 z=AYW}d8GzJeAaT8Z%T5{z`g7n?Q1`Z!XDWJF18Ea2mk literal 875 zcmV-x1C;z9iwFP!000003ktDSFyK;9h_F@AH5f&s2tNX*lrUg|8HMzNTmS$70RR8& zR>_rIHw?Tx3MMdo*q4K|PzugJ1s90cNg=4;%z+<;9c&hU{d>A^`{!56_`gM6^4|~D zp<7Za`~|+i-vrR@j-s3sc?4^nxS2@{rmDxBT-EvrU+MSV5#}{+IlSd)TuFX$NcaI( z!FC)bQ4e76H6dj~9GJudw3`%JMRu2+8mH`oq?aDMY?h}TpW|AtC~Kd-(bRgFaGDTu z6S3=XOy+!8ASvXlNGf4!bcI+ByI*yCv9%B)__m21Sl_DYkcf%INr!8*gY&iRSUCCx zdBfres<5eBuaZ@tNMx&dln_Go{5b5;_~Ui6S&y2bU;xuGaz3uN)nQR~39$i8`LA_U zZN~%}mE+1i&_XM84s)?kWGm;#ZYYf=4z9;ZZ(`1nyQSpFHBg>Ypl8p(3e`bQOvsJu zj^mL-(8o}Y^`wB&8xMf;5*C8w6CoH=Xdi$(&=AAW5<_rCPH6*BW*uz|MMv%!TTP?d zrKj9W0S}I8smlN*miexd_k=(z>sdqxE`TBljD6&ZVj%Zq^`pqi(;v{#17MEolu)&= zynIO)%}p2l4496f?-B1f*Pj5`-1e`^SLLhnRryn8&LK{sd#2!~VJdv!akF{^L3n*y z4I~1h^TJy<3taMMGFK}H6~<7o>f zf{lnX?SWD__EM|J>W9H%PKIh|dDjx8J|O7DUVsFR__GvuexOK`zl@qpmzUWdui}9f zmVP-Bp7mvSmY0qe6+NX=t7+d-#B-`lx{VWY{@nxH!n+fH=#VBjHWt$uT1EySs~AkV z^n*D$Bn+ZK{WJ@QyrzShV-^e9xX(4lut1r}}F7-UI<<&5)#>9)nd-w#p~T9*(k=8gqwFz?zG z3v{@5MFT6tWSOPJZyuNoZMd2I6kllhj<#ENdIuo;~003SW Bo}vH% diff --git a/regression/references/3instruments_collis.scn.reference_2_Quads_mstate.txt.gz b/regression/references/3instruments_collis.scn.reference_2_Quads_mstate.txt.gz deleted file mode 100644 index acd07db10175a4bbd62269c14d9c1981b1f18b55..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 29812 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 D*IoPk diff --git a/regression/references/3instruments_collis.scn.reference_3_Quads_mstate.txt.gz b/regression/references/3instruments_collis.scn.reference_3_Quads_mstate.txt.gz deleted file mode 100644 index 10e5de4051c81edf50b6734fa4b80ec47484f0de..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25107 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 R;C{cuUt}rK75uQ{zW{J2)J6aR diff --git a/regression/references/SingleBeam.py.reference b/regression/references/SingleBeam.py.reference new file mode 100644 index 000000000..e69de29bb diff --git a/regression/references/SingleBeam.py.reference_0_DOFs_mstate.txt.gz b/regression/references/SingleBeam.py.reference_0_DOFs_mstate.txt.gz new file mode 100644 index 0000000000000000000000000000000000000000..a28afb8b043ee3e3c4de796c3d2a1a7177397aca GIT binary patch literal 4098 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 A3IG5A literal 0 HcmV?d00001 diff --git a/regression/references/SingleBeamDeployment.py.reference b/regression/references/SingleBeamDeployment.py.reference new file mode 100644 index 000000000..e69de29bb diff --git a/regression/references/SingleBeamDeployment.py.reference_0_DOFs Container_mstate.txt.gz b/regression/references/SingleBeamDeployment.py.reference_0_DOFs Container_mstate.txt.gz new file mode 100644 index 0000000000000000000000000000000000000000..619cd91d1f97f768fded873b34bf5490e6524a31 GIT binary patch literal 308 zcmV-40n7d$iwFP!000006AH0aFyK;9hyYSZ&~TJD${XcT&f@|A00960?32xLLoo=1 z@2Nr!JcfUkL%K){?!Sc~`^ks25Z}Zu^Jzh&pYPR8-Jd`9ss3jrqzw4uQd^f$S;Z8x z4e#u2=$rw><6^9fsknq|T}oM1_LSdhP!#j-t=-Ek0_|MdgpMI)dh6Kn2L-K{{8pLL zJOVez4da^8bjng`-d1MGfw-fpH$}Sjgh5-n z&0J=3Q(C#OoJq_#rO^J4V$503T|!j-rgT_oEl^8xzBtOAliH`VSeFsIiyw|m9OUNM zZ1l|3bCqL>5;AU#vdjy(lA&ubs)BRxtSwn44k%{v7&$kl2V`Hr00030{{sMeABaw0 G2mk;qM3bQa literal 0 HcmV?d00001 diff --git a/regression/references/SingleBeamDeployment.scn.reference_0_DOFs Container_mstate.txt.gz b/regression/references/SingleBeamDeployment.scn.reference_0_DOFs Container_mstate.txt.gz index f714fac74584748b8ade8b59b39a33ebc1f4d7cf..547f03bf1114763c202f4098406509933f004458 100644 GIT binary patch literal 304 zcmV-00nh#)iwFP!000006AH0aFyK;9hyYSZ&~TJDQg~bd00030|Ll^zbptUBgZoUO z1&)vZ1dD9p6}J>8)eOFBG)i@<(M#^9bA= zH;ijWla!^zs5qQJxNgjJmb@_-h)vMT4kO!UG%K>`-d1MGfw-fpH$}SjltEj%&0J=3 zQ(C#OoJq_#rO^JVV$4~uT|!j-rgT_oEl^8xzB$UB)7mFltjmbq#ScfO4svsBHhO02 zxyrFb2^lv=S>^>?$k{g5E(l#$L6qH&kcCX#22*hf*Xb_}J>}mj6PtRtZ9h31J~zt9Hks8l z)ruiJJ1bkvb@y&}UcH5VCN|^QfheV%EZhJSoT|8IDFyQD96?e*sh?JFkM~p)eBtxZ zJ?nohkjc@#nafSbm}f%RGE%O}V+=`U-*q&FRstdM!=`A)aW;~Xbz?S@Y%e%`IvX4z zP~}x)n*sRe0ySm_R3;fNKmQ0}={_JLAXRei1bc(dejje%ne*`j009600{{yX^DVax F002nChcN&E diff --git a/regression/references/SingleBeamDeploymentCollision.py.reference b/regression/references/SingleBeamDeploymentCollision.py.reference new file mode 100644 index 000000000..e69de29bb diff --git a/regression/references/SingleBeamDeploymentCollision.py.reference_0_DOFs_mstate.txt.gz b/regression/references/SingleBeamDeploymentCollision.py.reference_0_DOFs_mstate.txt.gz new file mode 100644 index 0000000000000000000000000000000000000000..298ad728fc59b540012f6e2e9d6940f3156c29a8 GIT binary patch literal 864 zcmV-m1E2gKiwFP!000006AH0aFyK;9hyYSZ&~TJD${XcT&f@|A00960>{ZE;95D?0 zJBm(VRJ@OevvB^g7^HeO2$x^0Z3#R8kn`vF6Mee>&)7fz-ytLN?&pIeb9M2`IGM@qK_33kd2;3~ACNK&W< z?;&U>k5?OFD+&$ur}Y?0UXlbMFJ8>~7GNekpa^(vbZkfySOlWU`lc7f` z(56V9HJT1w+QSzf5dOj;>8e+;)Otcky#ByB-iGW$f6fcmGTx5OxUP5*KWek5g~jZv z;q&>J;=ugCRFGrggW8-X4Ph?4(=NOU64e+Zk==)S0vsgRzF>Gj`NCniTf9s72wO!( zaEcsNDDiRhZa4_KU|%%cb(up*ii=jwCB7JCK1{H#0v&O<5e|a}QS^nk{lMjggSra` zNwArtX3(2@S|PH89M{`=#?85K{=&0fcm-^m$41g(OjkBs6i1}K@Sozp;Qr<=gMUhK2ayDx@!cq##JQ!T9=(mrE)t+I z@VH~;)9H(b1HpPWT3Oz*D;6YJHMWz-j8!)q2WG^a0j}e7!0jlRFT8#B1sC?l>vFZa zqpc z!{^f}Y~jpzTTDyKUBc)Bc+Z|%xRBiPYh5WAg-ar=Wp~*)7Rm18PFQwU1B2g0L+?|C z8i-)(ufudP{nxT|(#0Z&1SUj1()s(9THXDAne#tAdg7eiWlTN5>_CuOyGb$2tK(|O zZEx{ZTdY6_Ubsp%B11BCB3(jH^}0OJ?uwBPwegkKoDGx5>0X|)u2IW%rUj!{DUW9a zJi(-3)e2mXqit}36Qn|{)ho$N(ut-;v|5fhE%I6!0nI~DT#}F|oeWahaH(^m=SGlh zZRh)8vfSXr#p=2je&{7nI6nZf)8s4}M$YC$UaCWZ%OW6a>b{&v?g)MuJ;4}xY$Gdwy?agQ2KmPy#0RR6309fZ6{;>rB E0C~m32><{9 literal 0 HcmV?d00001 diff --git a/regression/references/SingleBeamDeploymentCollision.scn.reference_0_DOFs Container_mstate.txt.gz b/regression/references/SingleBeamDeploymentCollision.scn.reference_0_DOFs Container_mstate.txt.gz index 397f4ad15925436719b7fe42edc0974dcbf00622..1c27efad91200c7c2e1ced9ac1dc0bf75f585231 100644 GIT binary patch literal 858 zcmV-g1Eu^QiwFP!000006AH0aFyK;9hyYSZ&~TJD${XcT&f@|A00960>{ZFJTsaK8 zI|?PxD(=g{Svdb#3{w41&R}Y8>L-B<0J8u5KGC=P{~7zw|GH#E&V4>OGB;Ts_8Z+k zmqgMXI=4w;_1+PSn>HRKbF|^$p`kUm?48L+{YrCh5GxX&yJ*hT7sI1sLx@l!=`B{b zeUvER8Swr2d-r?l-jm!Jlq`f z(S;UYq?Ew-l?}kd3CCdtnK9&28Kd0|;54;OfPx}5c=5)i<>Nc%`V@1@sA&5cR`>{(UUMoc@%za^+=@g_}3KXQm$uoD3yOfi>mB zS)=H{r9Bp8LEqB|`mzVa>f;F+Co*i~!xazv83R{zza5(~xLUX=a05>ZtJzkd1#nXw zKt3!5H5NXoW!$9I^T8XYS$I|DsqqDpeS93pSCN79Bt6(l5FMmd$I7N5vSdf`#IW^bflKMR$JDiR6$Pq5&=F8*E`n zuFIAV4WCl#r262-!vWn!p@7i1$@IL3Y4t$Z8C z8+fLT|NGiATz17yi(t*zvpXBM*1$3Bj^b5ebMRhvb(Y@1+qd^Bdl|vjRzcm8Ylp@{ z5G*SwoHJ`cCpbSS?#j*sw=XHPAg~lO->hmA%{5BKLPdZv{50=aMA%vrfL9xAg`(YB zDn6C%vdHfJ*tt{6+Owq=FS7=w{PI|Q_{zla)|+}P_*mC;q6i3a=fz^pA6C|KkeYVm zaAAeM)FV3OGB>k=(Yrdd)R^3#Y#I*@3g#tbjpy9yC3g;Ch#J4!G;GTp#?jX_vco8| kC`KYxjCp+jX~0R5hz%>V!Z literal 799 zcmV+)1K|80iwFP!000003ktDSFyK;9hyYSZ&~TJD${Xd8!vkifFx1R63P<4(hg<*v z00960?3T-seNJQrzX<{ktp_dZuE^@k?ut?*-8z7Z)!Z_kGY699mYq>-%`s88PHzNL z(uNCAF9L6>)e1<4HjaE|P$SF&U7h?$`aFn|uFypJ2!Zx9`x=hQROrtTY7N3#=cG^q zj8Khch|lU?hUCh+DJfHGHNK$s94vrvGt_c3pm$G(DD-M!G3$IsgM7{zI}?0DzW=s- zzkm1r6f9Ose}AaPhacDl&wC^6+`lAjvm|hhDGuzMwSzuz6UR9BQ_STwmDt+`Zh)D$ zVGrdzvy8nH*R2&kO2;f|5>peHCC+Y}0>h3C(B9T8x=as zI|1Sg$aw(_gL@P3l6E^5WA6&)-pDK7jyuYP8{Ba0$M^lA6>Vnln{4V_8&h|D#C<;; z=n6nC=`R4DcmPh-0+_hy1{6}b0E@T)5vmJFu9gNy>u~_EN}K}=fDA9->WlLuE#4B= z`OrtpTwQEH;Mmy`)N;cE$RB{slot?j&V(5_#TtNV+8Hp~886`0Ed?XC7f`_mP;q?I zQ2`G91t@<3rFH>0MBXOi=2-Sx-SN7byW-LASAvQgPCqkPYobm##=FDOa2%GlPwKaa dRaby4ulfNf*?)fk009600{~OKBl40F000b`c+UU; diff --git a/regression/references/SingleBeamDeploymentCollision.scn.reference_1_CollisionDOFs_mstate.txt.gz b/regression/references/SingleBeamDeploymentCollision.scn.reference_1_CollisionDOFs_mstate.txt.gz index 2886309b2a3106ebbe78cf29b8d0fff813e9fd53..581b69d597a59f761c7543718d2a5df04249ef7e 100644 GIT binary patch literal 409 zcmV;K0cQRmiwFP!000006AH0aFyK;9h_F@AH5f&s2tVQi00030|J;(xk>oH8L+=v> z6EI8QpNGj}@{eU9JBzKpxC|M1fco!OYIXPf%bfq|rzg%yENkik_CyO(Yd0xodHGxo zx!n*?wdHB(z%#B=jdYU?ok*9^Q$3Ie+Fb?dP#a%q%~>mXobJVw>l(EtFfEuorA#~Y z<_k;;R_zsVv<)S2a#V=5dL^+4I?*nOR^tn&MH;h>eCD($W+Ws^Cxf^)%ybBPZUf2I zb|j%vbAwiws_WkOsiY9j2U6K-VwMaWCl5SHDr}1}ECRBo?p*CjX2kFddeRz)llbjd z%6&~Nm`xZEzl|TX>$=maOw-wu1p_V5fa?zXmYQ)G(vz0&jR}mj=BO6$Fjh{%6>l`_ zC0XFm?RC*`CRrmui2j7gPq15@oBGTvAoIiZy2AC2H+-Gz--71-^A7+3|NjF3&cN58umu1B DZSTXf literal 421 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 literal 0 HcmV?d00001 diff --git a/regression/references/Tool_from_loader.scn.reference_1_dof_visual_GC_mstate.txt.gz b/regression/references/Tool_from_loader.scn.reference_1_dof_visual_GC_mstate.txt.gz new file mode 100644 index 0000000000000000000000000000000000000000..91dace6e98a4d95c6169f0a0d9963518866dae9b GIT binary patch literal 27222 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 Date: Wed, 7 May 2025 08:25:19 +0900 Subject: [PATCH 12/24] fix compilation for clang (#183) --- .../controller/InterventionalRadiologyController.inl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/BeamAdapter/component/controller/InterventionalRadiologyController.inl b/src/BeamAdapter/component/controller/InterventionalRadiologyController.inl index 8282af6ef..1165384a8 100644 --- a/src/BeamAdapter/component/controller/InterventionalRadiologyController.inl +++ b/src/BeamAdapter/component/controller/InterventionalRadiologyController.inl @@ -213,8 +213,11 @@ void InterventionalRadiologyController::init() // it should always start with zero (origin) m_nodeCurvAbs.push_back(0.0); const auto& xTips = d_xTip.getValue(); - for(const auto xTip : xTips | std::views::reverse) + + //for(const auto xTip : xTips | std::views::reverse) + for(std::size_t i = xTips.size() ; i > 0 ; i--) { + const auto xTip = xTips[i-1]; if(xTip > 0.0) { m_nodeCurvAbs.push_back(xTip); From 1b8dcaf6a655f53e3e49a937b53f1cfec4f7dc62 Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Wed, 7 May 2025 17:19:14 +0900 Subject: [PATCH 13/24] [Interpolations] Fix init stage (#184) * fix init stage (removing bwdInit shenanigans and add forgotten super init) * call super init for baseobj --- src/BeamAdapter/component/BaseBeamInterpolation.h | 4 +++- .../component/BaseBeamInterpolation.inl | 4 +++- src/BeamAdapter/component/BeamInterpolation.h | 1 - src/BeamAdapter/component/BeamInterpolation.inl | 14 ++++---------- .../component/WireBeamInterpolation.inl | 2 ++ 5 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/BeamAdapter/component/BaseBeamInterpolation.h b/src/BeamAdapter/component/BaseBeamInterpolation.h index d7d8b7369..8157d99a7 100644 --- a/src/BeamAdapter/component/BaseBeamInterpolation.h +++ b/src/BeamAdapter/component/BaseBeamInterpolation.h @@ -61,6 +61,8 @@ class BaseBeamInterpolation : public virtual sofa::core::objectmodel::BaseObject SOFA_CLASS(SOFA_TEMPLATE(BaseBeamInterpolation, DataTypes) , sofa::core::objectmodel::BaseObject); + using Inherit = sofa::core::objectmodel::BaseObject; + using Coord = typename DataTypes::Coord; using VecCoord = typename DataTypes::VecCoord; using Real = typename Coord::value_type; @@ -86,7 +88,7 @@ class BaseBeamInterpolation : public virtual sofa::core::objectmodel::BaseObject virtual ~BaseBeamInterpolation() = default; - void bwdInit() override; + void init() override; static void getControlPointsFromFrame( const Transform& global_H_local0, const Transform& global_H_local1, diff --git a/src/BeamAdapter/component/BaseBeamInterpolation.inl b/src/BeamAdapter/component/BaseBeamInterpolation.inl index b76b3f0e4..7cfb6d27b 100644 --- a/src/BeamAdapter/component/BaseBeamInterpolation.inl +++ b/src/BeamAdapter/component/BaseBeamInterpolation.inl @@ -117,8 +117,10 @@ BaseBeamInterpolation::BaseBeamInterpolation(/*sofa::component::engin template -void BaseBeamInterpolation::bwdInit() +void BaseBeamInterpolation::init() { + Inherit::init(); + BaseContext* context = getContext(); m_mstate = dynamic_cast *> (context->getMechanicalState()); diff --git a/src/BeamAdapter/component/BeamInterpolation.h b/src/BeamAdapter/component/BeamInterpolation.h index 5da83f227..d97db4ace 100644 --- a/src/BeamAdapter/component/BeamInterpolation.h +++ b/src/BeamAdapter/component/BeamInterpolation.h @@ -117,7 +117,6 @@ class BeamInterpolation : public BaseBeamInterpolation //////////////////////////////////// Inherited from Base /////////////////////////////////////// void init() override ; - void bwdInit() override ; void reinit() override ; void reset() override ; diff --git a/src/BeamAdapter/component/BeamInterpolation.inl b/src/BeamAdapter/component/BeamInterpolation.inl index 066f5eae4..e10c7265d 100644 --- a/src/BeamAdapter/component/BeamInterpolation.inl +++ b/src/BeamAdapter/component/BeamInterpolation.inl @@ -163,11 +163,6 @@ void BeamInterpolation::computeCrossSectionInertiaMatrix() ////////////////////////////////// ADAPTIVE INTERPOLATION ////////////////////////////////////////// -template -void BeamInterpolation::init() -{ -} - template void BeamInterpolation::checkDataSize(Real& defaultValue, Data>& dataList, const size_t& nbEdges) { @@ -194,10 +189,10 @@ void BeamInterpolation::checkDataSize(Real& defaultValue, Data -void BeamInterpolation::bwdInit() +void BeamInterpolation::init() { this->d_componentState.setValue(ComponentState::Loading); - BaseBeamInterpolation::bwdInit(); + BaseBeamInterpolation::init(); if (this->d_componentState.getValue() == ComponentState::Invalid) return; @@ -293,8 +288,7 @@ void BeamInterpolation::bwdInit() template void BeamInterpolation::reinit() { - init(); - bwdInit(); + init(); } template @@ -312,7 +306,7 @@ void BeamInterpolation::reset() if(d_componentState.getValue()==ComponentState::Invalid) return ; - bwdInit(); + init(); } template diff --git a/src/BeamAdapter/component/WireBeamInterpolation.inl b/src/BeamAdapter/component/WireBeamInterpolation.inl index 2a791aa6a..81493dbdf 100644 --- a/src/BeamAdapter/component/WireBeamInterpolation.inl +++ b/src/BeamAdapter/component/WireBeamInterpolation.inl @@ -51,6 +51,8 @@ WireBeamInterpolation::WireBeamInterpolation(WireRestShape template void WireBeamInterpolation::init() { + Inherited::init(); + if( m_restShape.get() == nullptr ) { msg_error() << "Missing WireRestShape. The component is thus de-activated" ; From 2897605cfd5ebf18c8c1998b200691aafc0e4202 Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Wed, 14 May 2025 17:33:23 +0900 Subject: [PATCH 14/24] [Interpolations] Remove raw ptr for mstate and topology (#185) * use link instead of raw pointers * use SingleStateAccessor (+ some minor cleanings) --- .../component/BaseBeamInterpolation.h | 29 ++++--- .../component/BaseBeamInterpolation.inl | 77 +++++++++---------- src/BeamAdapter/component/BeamInterpolation.h | 2 + .../component/BeamInterpolation.inl | 38 ++++----- .../component/WireBeamInterpolation.h | 30 ++++---- .../component/WireBeamInterpolation.inl | 7 +- 6 files changed, 91 insertions(+), 92 deletions(-) diff --git a/src/BeamAdapter/component/BaseBeamInterpolation.h b/src/BeamAdapter/component/BaseBeamInterpolation.h index 8157d99a7..7bd71e861 100644 --- a/src/BeamAdapter/component/BaseBeamInterpolation.h +++ b/src/BeamAdapter/component/BaseBeamInterpolation.h @@ -28,14 +28,16 @@ #include #include #include -#include #include +#include #include #include #include +#include +#include + -#include #include @@ -55,13 +57,13 @@ using sofa::component::statecontainer::MechanicalObject; * */ template -class BaseBeamInterpolation : public virtual sofa::core::objectmodel::BaseObject +class BaseBeamInterpolation : public sofa::core::behavior::SingleStateAccessor { public: SOFA_CLASS(SOFA_TEMPLATE(BaseBeamInterpolation, DataTypes) , - sofa::core::objectmodel::BaseObject); + SOFA_TEMPLATE(sofa::core::behavior::SingleStateAccessor, DataTypes)); - using Inherit = sofa::core::objectmodel::BaseObject; + using Inherit = sofa::core::behavior::SingleStateAccessor; using Coord = typename DataTypes::Coord; using VecCoord = typename DataTypes::VecCoord; @@ -70,8 +72,8 @@ class BaseBeamInterpolation : public virtual sofa::core::objectmodel::BaseObject using Deriv = typename DataTypes::Deriv; using VecDeriv = typename DataTypes::VecDeriv; - using Transform = typename sofa::defaulttype::SolidTypes::Transform; - using SpatialVector = typename sofa::defaulttype::SolidTypes::SpatialVector; + using Transform = sofa::type::Transform; + using SpatialVector = sofa::type::SpatialVector; using Vec2 = sofa::type::Vec<2, Real>; using Vec3 = sofa::type::Vec<3, Real>; @@ -84,7 +86,7 @@ class BaseBeamInterpolation : public virtual sofa::core::objectmodel::BaseObject using VecEdgeID = type::vector; using VecEdges = type::vector; - BaseBeamInterpolation(/*sofa::component::engine::WireRestShape *_restShape = nullptr*/); + BaseBeamInterpolation(); virtual ~BaseBeamInterpolation() = default; @@ -212,7 +214,6 @@ class BaseBeamInterpolation : public virtual sofa::core::objectmodel::BaseObject typename MechanicalObject::SPtr m_StateNodes; Data< VecEdgeID > d_edgeList; - const VecEdges* m_topologyEdges{ nullptr }; ///2. Vector of length of each beam. Same size as @sa d_edgeList Data< type::vector< Real > > d_lengthList; @@ -230,13 +231,9 @@ class BaseBeamInterpolation : public virtual sofa::core::objectmodel::BaseObject Data< sofa::type::vector > d_beamCollision; Data d_dofsAndBeamsAligned; - - - /// pointer to the topology - BaseMeshTopology* m_topology{ nullptr }; - - /// pointer on mechanical state - MechanicalState* m_mstate{ nullptr }; + + /// link to the (edge) topology + SingleLink, BaseMeshTopology, BaseLink::FLAG_STOREPATH|BaseLink::FLAG_STRONGLINK> l_topology; }; diff --git a/src/BeamAdapter/component/BaseBeamInterpolation.inl b/src/BeamAdapter/component/BaseBeamInterpolation.inl index 7cfb6d27b..d87ae05d8 100644 --- a/src/BeamAdapter/component/BaseBeamInterpolation.inl +++ b/src/BeamAdapter/component/BaseBeamInterpolation.inl @@ -96,8 +96,9 @@ void BaseBeamInterpolation::RotateFrameForAlignNormalizedX(const Quat template -BaseBeamInterpolation::BaseBeamInterpolation(/*sofa::component::engine::WireRestShape *_restShape*/) - : m_StateNodes(sofa::core::objectmodel::New< sofa::component::statecontainer::MechanicalObject >()) +BaseBeamInterpolation::BaseBeamInterpolation() + : Inherit() + , m_StateNodes(sofa::core::objectmodel::New< sofa::component::statecontainer::MechanicalObject >()) , d_edgeList(initData(&d_edgeList, "edgeList", "list of the edge in the topology that are concerned by the Interpolation")) , d_lengthList(initData(&d_lengthList, "lengthList", "list of the length of each beam")) , d_DOF0TransformNode0(initData(&d_DOF0TransformNode0, "DOF0TransformNode0", "Optional rigid transformation between the degree of Freedom and the first node of the beam")) @@ -106,13 +107,11 @@ BaseBeamInterpolation::BaseBeamInterpolation(/*sofa::component::engin , d_beamCollision(initData(&d_beamCollision, "beamCollision", "list of beam (in edgeList) that needs to be considered for collision")) , d_dofsAndBeamsAligned(initData(&d_dofsAndBeamsAligned, true, "dofsAndBeamsAligned", "if false, a transformation for each beam is computed between the DOF and the beam nodes")) - , m_topology(nullptr) - , m_mstate(nullptr) + , l_topology(initLink("topology", "link to the topology (must contain edges)")) { - m_StateNodes->setName("bezierNodes"); - addSlave(m_StateNodes); + this->addSlave(m_StateNodes); } @@ -120,35 +119,31 @@ template void BaseBeamInterpolation::init() { Inherit::init(); - - BaseContext* context = getContext(); - - m_mstate = dynamic_cast *> (context->getMechanicalState()); - if (m_mstate == nullptr) + + if (!l_topology) { - msg_error() << "No MechanicalState found. Component is de-activated."; - d_componentState.setValue(ComponentState::Invalid); - return; + l_topology.set(this->getMState()->getContext()->getMeshTopologyLink()); } - /// Get the topology from context and check if it is valid. - m_topology = context->getMeshTopology(); - if (!m_topology) + if (l_topology) + { + msg_info() << "Found topology named "<< l_topology->getName() ; + } + else { - msg_error() << "No Topology found. Component is de-activated."; - d_componentState.setValue(ComponentState::Invalid); + msg_error() << "Cannot find a topology container. Please specify the link to the topology or insert one in the same node."; + this->d_componentState.setValue(ComponentState::Invalid); return; } - - /// Check the topology properties - if (m_topology->getNbEdges() == 0) + + if(l_topology->getNbEdges() == 0) { - msg_error() << "Found a topology but it is empty. Component is de-activated"; - d_componentState.setValue(ComponentState::Invalid); + msg_error() << "Found a topology but it is empty (no edges). Component is de-activated"; + this->d_componentState.setValue(ComponentState::Invalid); return; } - - m_topologyEdges = &m_topology->getEdges(); + + this->d_componentState.setValue(sofa::core::objectmodel::ComponentState::Valid); } @@ -266,7 +261,7 @@ void BaseBeamInterpolation::getBeamAtCurvAbs(const Real x_input, unsi /// LTotal = length sum of the beams that are "out" Real LTotal = 0.0; - const unsigned int edgeListSize = this->d_edgeList.getValue().size(); + const sofa::Size edgeListSize = static_cast(this->d_edgeList.getValue().size()); /// we find the length of the beam that is "out" for (unsigned int e = start; e < edgeListSize; e++) @@ -360,7 +355,7 @@ void BaseBeamInterpolation::getDOFtoLocalTransformInGlobalFrame(const template void BaseBeamInterpolation::setTransformBetweenDofAndNode(const sofa::Index beam, const Transform& DOF_H_Node, unsigned int zeroORone) { - if (beam > int(d_DOF0TransformNode0.getValue().size() - 1) || beam > int(d_DOF1TransformNode1.getValue().size() - 1)) + if (beam > (d_DOF0TransformNode0.getValue().size() - 1) || beam > (d_DOF1TransformNode1.getValue().size() - 1)) { msg_error() << "WARNING setTransformBetweenDofAndNode on non existing beam"; return; @@ -402,15 +397,15 @@ int BaseBeamInterpolation::getNodeIndices(const EdgeID edgeInList, unsigned int& node0Idx, unsigned int& node1Idx) { - if (m_topologyEdges == nullptr) + if(!this->isComponentStateValid()) { - msg_error() << "This object does not have edge topology defined (computation halted). "; + msg_error() << "(getNodeIndices) This component is invalid, check the other error messages. "; return -1; } /// 1. Get the indices of element and nodes const EdgeID& e = d_edgeList.getValue()[edgeInList]; - const BaseMeshTopology::Edge& edge = (*m_topologyEdges)[e]; + const BaseMeshTopology::Edge& edge = l_topology->getEdge(e); node0Idx = edge[0]; node1Idx = edge[1]; @@ -437,14 +432,14 @@ void BaseBeamInterpolation::getSplinePoints(const EdgeID edgeInList, template unsigned int BaseBeamInterpolation::getStateSize() const { - if (m_mstate == nullptr) + if(!this->isComponentStateValid()) { - msg_error() << "No _mstate found (Aborting)"; + msg_error() << "(getStateSize) This component is invalid, check the other error messages. "; return 0; } else { - return m_mstate->getSize(); + return this->getMState()->getSize(); } } @@ -582,13 +577,15 @@ void BaseBeamInterpolation::computeStrechAndTwist(const EdgeID edgeIn ///vId_Out provides the id of the multiVecId which stores the position of the Bezier Points template -void BaseBeamInterpolation::updateBezierPoints(const VecCoord& x, sofa::core::VecCoordId& vId_Out) { +void BaseBeamInterpolation::updateBezierPoints(const VecCoord& x, sofa::core::VecCoordId& vId_Out) +{ + const sofa::Size edgeListSize = static_cast(d_edgeList.getValue().size()); ///Mechanical Object to stock Bezier points. - m_StateNodes->resize(d_edgeList.getValue().size() * 4); + m_StateNodes->resize(edgeListSize * 4); auto bezierPosVec = sofa::helper::getWriteOnlyAccessor(*m_StateNodes->write(vId_Out)); - bezierPosVec.resize(d_edgeList.getValue().size() * 4); + bezierPosVec.resize(edgeListSize * 4); - for (unsigned int i = 0; i < d_edgeList.getValue().size(); i++) { + for (unsigned int i = 0; i < edgeListSize; i++) { updateBezierPoints(x, i, bezierPosVec.wref()); } @@ -596,8 +593,8 @@ void BaseBeamInterpolation::updateBezierPoints(const VecCoord& x, so template void BaseBeamInterpolation::updateBezierPoints(const VecCoord& x, unsigned int index, - VectorVec3& bezierPosVec) { - /// <<" interpolatePointUsingSpline : "<< edgeInList<<" xbary="< { public: SOFA_CLASS( SOFA_TEMPLATE(BeamInterpolation, DataTypes) , SOFA_TEMPLATE(BaseBeamInterpolation, DataTypes)); + + using Inherit = BaseBeamInterpolation; using Coord = typename DataTypes::Coord; using VecCoord = typename DataTypes::VecCoord; diff --git a/src/BeamAdapter/component/BeamInterpolation.inl b/src/BeamAdapter/component/BeamInterpolation.inl index e10c7265d..d17c1ce6d 100644 --- a/src/BeamAdapter/component/BeamInterpolation.inl +++ b/src/BeamAdapter/component/BeamInterpolation.inl @@ -50,8 +50,9 @@ using sofa::helper::ReadAccessor ; //////////////////////////////////// BREAMINTERPOLATION //////////////////////////////////////////// template -BeamInterpolation::BeamInterpolation() : - crossSectionShape(initData(&crossSectionShape, +BeamInterpolation::BeamInterpolation() + : Inherit() + , crossSectionShape(initData(&crossSectionShape, {"circular","elliptic (not available)","rectangular"}, "crossSectionShape", "shape of the cross-section. Can be: circular, elliptic, square, rectangular. Default is circular" )) @@ -139,7 +140,7 @@ void BeamInterpolation::computeCrossSectionInertiaMatrix() } else { - Size nbEdges = this->m_topology->getNbEdges(); + Size nbEdges = this->l_topology->getNbEdges(); m_section.resize(nbEdges); if ( crossSectionShape.getValue().getSelectedItem() == "elliptic") { @@ -192,12 +193,12 @@ template void BeamInterpolation::init() { this->d_componentState.setValue(ComponentState::Loading); - BaseBeamInterpolation::init(); + Inherit::init(); if (this->d_componentState.getValue() == ComponentState::Invalid) return; - Size nbEdges = this->m_topology->getNbEdges(); + Size nbEdges = this->l_topology->getNbEdges(); checkDataSize(m_defaultRadius, d_radius, nbEdges); checkDataSize(m_defaultInnerRadius, d_innerRadius, nbEdges); checkDataSize(m_defaultLengthY, d_lengthY, nbEdges); @@ -211,7 +212,7 @@ void BeamInterpolation::init() auto edgeList = sofa::helper::getWriteOnlyAccessor(this->d_edgeList); edgeList.clear(); - for (unsigned int i=0; im_topology->getNbEdges(); i++) + for (unsigned int i=0; il_topology->getNbEdges(); i++) { edgeList.push_back(i); } @@ -225,7 +226,7 @@ void BeamInterpolation::init() DOF1TransformNode1.resize(edgeList.size()); } - ReadAccessor > statePos = this->m_mstate->read(sofa::core::vec_id::read_access::position) ; + ReadAccessor > statePos = this->getMState()->read(sofa::core::vec_id::read_access::position) ; auto lengthList = sofa::helper::getWriteOnlyAccessor(this->d_lengthList); lengthList.clear(); @@ -335,17 +336,18 @@ bool BeamInterpolation::interpolationIsAlreadyInitialized() template bool BeamInterpolation::verifyTopology() { + const auto nbEdges = this->l_topology->getNbEdges(); + //TODO(dmarchal) This contains "code" specific slang that cannot be understood by user. - dmsg_info() << "The vector _topologyEdges is now set with " << this->m_topologyEdges->size() << " edges" ; - + dmsg_info() << "The vector _topologyEdges is now set with " << nbEdges << " edges" ; const VecElementID &edgeList = this->d_edgeList.getValue(); for (unsigned int j = 0; j < edgeList.size(); j++) { - if(edgeList[j] > this->m_topologyEdges->size()) + if(edgeList[j] > nbEdges) { msg_error() << "The provided edge index '" << edgeList[j] << "'is larger than '" - << this->m_topologyEdges->size() << "' the amount of edges in the topology. " ; + << nbEdges << "' the amount of edges in the topology. " ; return false; } } @@ -420,7 +422,7 @@ void BeamInterpolation::getMechanicalSampling(Real& dx, const Real x_ { SOFA_UNUSED(x_localcurv_abs); - const auto numLines = this->m_topologyEdges->size(); + const auto numLines = this->l_topology->getNbEdges(); dx = getRestTotalLength()/numLines; } @@ -429,14 +431,14 @@ void BeamInterpolation::getCollisionSampling(Real &dx, const Real x_l { SOFA_UNUSED(x_localcurv_abs); - const auto numLines = this->m_topologyEdges->size(); + const auto numLines = this->l_topology->getNbEdges(); dx = getRestTotalLength()/numLines; } template void BeamInterpolation::getNumberOfCollisionSegment(Real &dx, unsigned int &numLines) { - numLines = static_cast(this->m_topologyEdges->size()); + numLines = static_cast(this->l_topology->getNbEdges()); dx = getRestTotalLength()/numLines; } @@ -599,16 +601,16 @@ void BeamInterpolation::updateInterpolation(){ if(d_vecID.getValue().getSelectedItem() == "current") { dmsg_info() <<" position " << msgendl - << " ="<< this->m_mstate->read( sofa::core::vec_id::read_access::position )->getValue( ) ; - x=this->m_mstate->read( sofa::core::vec_id::read_access::position ); + << " ="<< this->getMState()->read( sofa::core::vec_id::read_access::position )->getValue( ) ; + x=this->getMState()->read( sofa::core::vec_id::read_access::position ); } else if(d_vecID.getValue().getSelectedItem() == "free") { - x=this->m_mstate->read( sofa::core::vec_id::read_access::freePosition ) ; + x=this->getMState()->read( sofa::core::vec_id::read_access::freePosition ) ; } else /// rest position { - x=this->m_mstate->read( sofa::core::vec_id::read_access::restPosition ) ; + x=this->getMState()->read( sofa::core::vec_id::read_access::restPosition ) ; computeVel = false; } diff --git a/src/BeamAdapter/component/WireBeamInterpolation.h b/src/BeamAdapter/component/WireBeamInterpolation.h index 42240b39d..32f69ee1b 100644 --- a/src/BeamAdapter/component/WireBeamInterpolation.h +++ b/src/BeamAdapter/component/WireBeamInterpolation.h @@ -75,22 +75,22 @@ class WireBeamInterpolation : public BaseBeamInterpolation public: SOFA_CLASS(SOFA_TEMPLATE(WireBeamInterpolation, DataTypes) , SOFA_TEMPLATE(BaseBeamInterpolation, DataTypes) ); + + using Inherit = BaseBeamInterpolation; - typedef BaseBeamInterpolation Inherited; - - typedef typename Inherited::VecCoord VecCoord; - typedef typename Inherited::VecDeriv VecDeriv; - typedef typename Inherited::Coord Coord; - typedef typename Inherited::Deriv Deriv; + typedef typename Inherit::VecCoord VecCoord; + typedef typename Inherit::VecDeriv VecDeriv; + typedef typename Inherit::Coord Coord; + typedef typename Inherit::Deriv Deriv; - typedef typename Inherited::Real Real; + typedef typename Inherit::Real Real; - typedef typename Inherited::Transform Transform; - typedef typename Inherited::SpatialVector SpatialVector; + typedef typename Inherit::Transform Transform; + typedef typename Inherit::SpatialVector SpatialVector; - typedef typename Inherited::Vec2 Vec2; - typedef typename Inherited::Vec3 Vec3; - typedef typename Inherited::Quat Quat; + typedef typename Inherit::Vec2 Vec2; + typedef typename Inherit::Vec3 Vec3; + typedef typename Inherit::Quat Quat; using EdgeID = BaseMeshTopology::EdgeID; @@ -176,9 +176,9 @@ class WireBeamInterpolation : public BaseBeamInterpolation BaseLink::FLAG_STOREPATH|BaseLink::FLAG_STRONGLINK> m_restShape; /*! link on an external rest-shape*/ - ////////////////////////// Inherited attributes //////////////////////////// + ////////////////////////// Inherit attributes //////////////////////////// /// https://gcc.gnu.org/onlinedocs/gcc/Name-lookup.html - /// Bring inherited attributes and function in the current lookup context. + /// Bring Inherit attributes and function in the current lookup context. /// otherwise any access to the base::attribute would require /// the "this->" approach. using BaseBeamInterpolation::d_componentState ; @@ -189,7 +189,7 @@ class WireBeamInterpolation : public BaseBeamInterpolation template static bool canCreate(T* obj, sofa::core::objectmodel::BaseContext* context, sofa::core::objectmodel::BaseObjectDescription* arg) { - return Inherited::canCreate(obj,context,arg); + return Inherit::canCreate(obj,context,arg); } template diff --git a/src/BeamAdapter/component/WireBeamInterpolation.inl b/src/BeamAdapter/component/WireBeamInterpolation.inl index 81493dbdf..74d471931 100644 --- a/src/BeamAdapter/component/WireBeamInterpolation.inl +++ b/src/BeamAdapter/component/WireBeamInterpolation.inl @@ -41,7 +41,8 @@ namespace beamadapter template WireBeamInterpolation::WireBeamInterpolation(WireRestShape *_restShape) - : m_restShape(initLink("WireRestShape", "link to the component on the scene"), _restShape) + : Inherit() + , m_restShape(initLink("WireRestShape", "link to the component on the scene"), _restShape) { @@ -51,7 +52,7 @@ WireBeamInterpolation::WireBeamInterpolation(WireRestShape template void WireBeamInterpolation::init() { - Inherited::init(); + Inherit::init(); if( m_restShape.get() == nullptr ) { @@ -72,7 +73,7 @@ void WireBeamInterpolation::init() template void WireBeamInterpolation::bwdInit() { - Inherited::bwdInit(); + Inherit::bwdInit(); if (this->isControlled()){ msg_info() << "external controller for this ForceField is detected" ; From b23acc4c307e153741efd1df58fc65c377320d84 Mon Sep 17 00:00:00 2001 From: Paul Baksic Date: Fri, 16 May 2025 15:20:20 +0200 Subject: [PATCH 15/24] Add 'notify dashboard' step to CI --- .github/workflows/ci.yml | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 810f2f9bd..ba40eef4e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,4 @@ -name: CI + name: CI on: workflow_dispatch: @@ -29,6 +29,7 @@ jobs: path: ${{ env.WORKSPACE_SRC_PATH }} - name: Build and install + id: build-install shell: bash run: | if [[ "$RUNNER_OS" == "Windows" ]]; then @@ -70,12 +71,14 @@ jobs: echo "artifact_name=$artifact_name" >> $env:GITHUB_OUTPUT - name: Create artifact + id: create-artifact uses: actions/upload-artifact@v4.4.0 with: name: ${{ steps.sanitize.outputs.artifact_name }} path: ${{ env.WORKSPACE_INSTALL_PATH }} - name: Install artifact + id: install-artifact uses: actions/download-artifact@v4.1.7 with: name: ${{ steps.sanitize.outputs.artifact_name }} @@ -116,7 +119,9 @@ jobs: # echo '----------------------' # echo "SOFA_ROOT = $SOFA_ROOT" + - name: Run BeamAdapter_test + id: unit-test if: always() shell: bash run: | @@ -125,6 +130,7 @@ jobs: ./bin/BeamAdapter_test${{ steps.sofa.outputs.exe }} - name: Fetch, install and run Regression_test + id: regression-test if: always() shell: bash run: | @@ -146,6 +152,36 @@ jobs: echo "Regression tests are not supported on the CI for macOS yet (TODO)" fi + - name: notify dashboard + if: always() + env: + DASH_AUTH: ${{ secrets.PLUGIN_DASH }} + shell: bash + run: | + + test_status=$([ '${{ steps.unit-test.outcome }}' == 'success' ] && \ + [ '${{ steps.regression-test.outcome }}' == 'success' ] && \ + echo 'true' || echo 'false') + + build_status=$([ '${{ steps.build-install.outcome }}' == 'success' ] && \ + echo 'true' || echo 'false') + + binary_status=$([ '${{ steps.sanitize.outcome }}' == 'success' ] && \ + [ '${{ steps.create-artifact.outcome }}' == 'success' ] && \ + [ '${{ steps.install-artifact.outcome }}' == 'success' ] && \ + echo 'true' || echo 'false') + + + curl -X POST -H "X-API-KEY: $DASH_AUTH" -H "Content-Type: application/json" -d \ + "{\"id\":\"$(echo "${{ github.repository }}" | awk -F/ '{ print $2 }')\",\ + \"github_ref\":\"${{ github.sha }}\",\ + \"url\":\"https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}\",\ + \"build\":\"\$build_status\",\ + \"tests\":\"$test_status\",\ + \"binary\":\"$binary_status\"}"\ + https://sofa-framework.org:5000/api/v1/plugins + + deploy: name: Deploy artifacts if: always() && startsWith(github.repository, 'sofa-framework') && (startsWith(github.ref, 'refs/heads/') || startsWith(github.ref, 'refs/tags/')) # we are not on a fork and on a branch or a tag (not a PR) needs: [build-and-test] From 563532e5d0efe47e0e6f7f4ff25d7f22ea186516 Mon Sep 17 00:00:00 2001 From: Paul Baksic <30337881+bakpaul@users.noreply.github.com> Date: Fri, 16 May 2025 15:24:20 +0200 Subject: [PATCH 16/24] Update ci.yml --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ba40eef4e..1d9c78505 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -152,8 +152,8 @@ jobs: echo "Regression tests are not supported on the CI for macOS yet (TODO)" fi - - name: notify dashboard - if: always() + - name: Notify dashboard + if: always() && startsWith(github.repository, 'sofa-framework') && startsWith(github.ref, 'refs/heads/master') # we are not on a fork and on master env: DASH_AUTH: ${{ secrets.PLUGIN_DASH }} shell: bash From 3c5b334b41f8bb471304b3d71d2e2b5d266ca1b5 Mon Sep 17 00:00:00 2001 From: Paul Baksic Date: Fri, 16 May 2025 15:30:07 +0200 Subject: [PATCH 17/24] Fix typo --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1d9c78505..4bb51b865 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -176,7 +176,7 @@ jobs: "{\"id\":\"$(echo "${{ github.repository }}" | awk -F/ '{ print $2 }')\",\ \"github_ref\":\"${{ github.sha }}\",\ \"url\":\"https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}\",\ - \"build\":\"\$build_status\",\ + \"build\":\"$build_status\",\ \"tests\":\"$test_status\",\ \"binary\":\"$binary_status\"}"\ https://sofa-framework.org:5000/api/v1/plugins From 3dfbe3a07169bcea18a1c1107ee286576f0a876b Mon Sep 17 00:00:00 2001 From: Paul Baksic <30337881+bakpaul@users.noreply.github.com> Date: Fri, 16 May 2025 16:51:08 +0200 Subject: [PATCH 18/24] Fix introduced typo in ci file --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4bb51b865..75d6c6d2a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,4 @@ - name: CI +name: CI on: workflow_dispatch: From 491e85892a00df9e58df67bc9b21e3ab0f4e87b0 Mon Sep 17 00:00:00 2001 From: Paul Baksic Date: Fri, 16 May 2025 17:14:53 +0200 Subject: [PATCH 19/24] Remove quote to send real boolean --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 75d6c6d2a..466164369 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -176,9 +176,9 @@ jobs: "{\"id\":\"$(echo "${{ github.repository }}" | awk -F/ '{ print $2 }')\",\ \"github_ref\":\"${{ github.sha }}\",\ \"url\":\"https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}\",\ - \"build\":\"$build_status\",\ - \"tests\":\"$test_status\",\ - \"binary\":\"$binary_status\"}"\ + \"build\":$build_status,\ + \"tests\":$test_status,\ + \"binary\":$binary_status}"\ https://sofa-framework.org:5000/api/v1/plugins From f38462b5ccf2932a55298c157eddc34dcf84fe0b Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Tue, 20 May 2025 07:14:48 +0900 Subject: [PATCH 20/24] Cleaning of AdaptiveBeamForceFieldAndMass (#187) * use link instead of raw pointers * use SingleStateAccessor (+ some minor cleanings) * clean code --- .../AdaptiveBeamForceFieldAndMass.h | 54 ++- .../AdaptiveBeamForceFieldAndMass.inl | 318 +++++++++--------- 2 files changed, 185 insertions(+), 187 deletions(-) diff --git a/src/BeamAdapter/component/forcefield/AdaptiveBeamForceFieldAndMass.h b/src/BeamAdapter/component/forcefield/AdaptiveBeamForceFieldAndMass.h index a46193c4e..2bea4791e 100644 --- a/src/BeamAdapter/component/forcefield/AdaptiveBeamForceFieldAndMass.h +++ b/src/BeamAdapter/component/forcefield/AdaptiveBeamForceFieldAndMass.h @@ -32,10 +32,12 @@ // #pragma once -//////////////////////// Inclusion of headers...from wider to narrower/closer ////////////////////// +#include + +#include +#include #include #include -#include #include #include @@ -43,10 +45,6 @@ namespace beamadapter { -using sofa::core::behavior::MultiMatrixAccessor; -using sofa::core::visual::VisualParams; -using sofa::core::MechanicalParams; - /*! * \class AdaptiveBeamForceFieldAndMass * @brief AdaptiveBeamForceFieldAndMass Class @@ -77,8 +75,8 @@ class AdaptiveBeamForceFieldAndMass : public core::behavior::Mass using Vec6NoInit = sofa::type::VecNoInit<6, Real>; using Matrix6x6 = sofa::type::Mat<6, 6, Real>; using Matrix6x6NoInit = sofa::type::MatNoInit<6, 6, Real>; - using Transform = typename sofa::defaulttype::SolidTypes::Transform; - using SpatialVector = typename sofa::defaulttype::SolidTypes::SpatialVector; + using Transform = sofa::type::Transform; + using SpatialVector = sofa::type::SpatialVector; using BInterpolation = BaseBeamInterpolation; using core::behavior::Mass::mstate; @@ -89,8 +87,8 @@ class AdaptiveBeamForceFieldAndMass : public core::behavior::Mass * \class BeamLocalMatrices * @brief BeamLocalMatrices Class */ - class BeamLocalMatrices{ - + class BeamLocalMatrices + { public: BeamLocalMatrices() = default; @@ -114,29 +112,29 @@ class AdaptiveBeamForceFieldAndMass : public core::behavior::Mass ///////////////////////////////////// void init() override ; void reinit() override ; - void draw(const VisualParams* vparams) override ; + void draw(const sofa::core::visual::VisualParams* vparams) override ; ///////////////////////////////////// /// Mass Interface ///////////////////////////////////// - void addMDx(const MechanicalParams* mparams, DataVecDeriv& f, const DataVecDeriv& dx, SReal factor) override; - void addMToMatrix(const MechanicalParams *mparams, const MultiMatrixAccessor* matrix) override; - void addMBKToMatrix(const MechanicalParams* mparams, const MultiMatrixAccessor* matrix) override; + void addMDx(const sofa::core::MechanicalParams* mparams, DataVecDeriv& f, const DataVecDeriv& dx, SReal factor) override; + void addMToMatrix(const sofa::core::MechanicalParams *mparams, const sofa::core::behavior::MultiMatrixAccessor* matrix) override; + void addMBKToMatrix(const sofa::core::MechanicalParams* mparams, const sofa::core::behavior::MultiMatrixAccessor* matrix) override; void buildMassMatrix(sofa::core::behavior::MassMatrixAccumulator* matrices) override; void buildStiffnessMatrix(core::behavior::StiffnessMatrix* matrix) override; void buildDampingMatrix(core::behavior::DampingMatrix* matrices) override; //TODO(dmarchal 2017-05-17) So what do we do ? For who is this message intended for ? How can we make this code "more" manageable. - void accFromF(const MechanicalParams* mparams, DataVecDeriv& , const DataVecDeriv& ) override + void accFromF(const sofa::core::MechanicalParams* mparams, DataVecDeriv& , const DataVecDeriv& ) override { SOFA_UNUSED(mparams); msg_error()<<"accFromF can not be implemented easily: It necessitates a solver because M^-1 is not available"; } //TODO(dmarchal 2017-05-17) So what do we do ? For who is this message intended for ? How can we make this code "more" manageable. - SReal getKineticEnergy(const MechanicalParams* mparams, const DataVecDeriv& ) const override ///< vMv/2 using dof->getV() + SReal getKineticEnergy(const sofa::core::MechanicalParams* mparams, const DataVecDeriv& ) const override ///< vMv/2 using dof->getV() { SOFA_UNUSED(mparams); msg_error() << "getKineticEnergy not yet implemented"; @@ -144,7 +142,7 @@ class AdaptiveBeamForceFieldAndMass : public core::behavior::Mass } //TODO(dmarchal 2017-05-17) So what do we do ? For who is this message intended for ? How can we make this code "more" manageable. - void addGravityToV(const MechanicalParams* mparams, DataVecDeriv& ) override + void addGravityToV(const sofa::core::MechanicalParams* mparams, DataVecDeriv& ) override { SOFA_UNUSED(mparams); msg_error() << "addGravityToV not implemented yet"; @@ -155,12 +153,12 @@ class AdaptiveBeamForceFieldAndMass : public core::behavior::Mass ///////////////////////////////////// /// ForceField Interface ///////////////////////////////////// - void addForce(const MechanicalParams* mparams, DataVecDeriv& f, const DataVecCoord& x, const DataVecDeriv& v) override; + void addForce(const sofa::core::MechanicalParams* mparams, DataVecDeriv& f, const DataVecCoord& x, const DataVecDeriv& v) override; - void addDForce(const MechanicalParams* mparams, DataVecDeriv& datadF , const DataVecDeriv& datadX ) override; + void addDForce(const sofa::core::MechanicalParams* mparams, DataVecDeriv& datadF , const DataVecDeriv& datadX ) override; //TODO(dmarchal 2017-05-17) So what do we do ? For who is this message intended for ? How can we make this code "more" manageable. - SReal getPotentialEnergy(const MechanicalParams* mparams, const DataVecCoord& )const override + SReal getPotentialEnergy(const sofa::core::MechanicalParams* mparams, const DataVecCoord& )const override { SOFA_UNUSED(mparams); msg_error()<<"getPotentialEnergy not yet implemented"; @@ -168,11 +166,11 @@ class AdaptiveBeamForceFieldAndMass : public core::behavior::Mass } using sofa::core::behavior::ForceField::addKToMatrix; - void addKToMatrix(const MechanicalParams* mparams, - const MultiMatrixAccessor* matrix) override; + void addKToMatrix(const sofa::core::MechanicalParams* mparams, + const sofa::core::behavior::MultiMatrixAccessor* matrix) override; - void computeStiffness(int beam, BeamLocalMatrices& beamLocalMatrices); - void computeMass(int beam, BeamLocalMatrices& beamMatrices); + void computeStiffness(const sofa::Index beamID, BeamLocalMatrices& beamLocalMatrices); + void computeMass(const sofa::Index beamID, BeamLocalMatrices& beamMatrices); Data d_computeMass; ///< if false, only compute the stiff elastic model Real m_defaultMassDensity; @@ -183,16 +181,16 @@ class AdaptiveBeamForceFieldAndMass : public core::behavior::Mass protected : SingleLink, BInterpolation, BaseLink::FLAG_STOREPATH|BaseLink::FLAG_STRONGLINK> l_interpolation; - void applyMassLarge( VecDeriv& df, int bIndex, Index nd0Id, Index nd1Id, SReal factor); - void applyStiffnessLarge( VecDeriv& df, const VecDeriv& dx, int beam, Index nd0Id, Index nd1Id, SReal factor ); + void applyMassLarge( VecDeriv& df, const sofa::Index beamID, const sofa::Index nd0Id, const sofa::Index nd1Id, const SReal factor); + void applyStiffnessLarge( VecDeriv& df, const VecDeriv& dx, const sofa::Index beamID, const sofa::Index nd0Id, const sofa::Index nd1Id, const SReal factor ); void computeGravityVector(); private: type::vector m_localBeamMatrices; Vec6 m_gravity; - void drawElement(const VisualParams* vparams, int beam, - Transform &global_H0_local, Transform &global_H1_local) ; + void drawElement(const sofa::core::visual::VisualParams* vparams, const sofa::Index beamID, + const Transform &global_H0_local, const Transform &global_H1_local) ; }; /// Instantiate the templates so that they are not instiated in each translation unit (see ) diff --git a/src/BeamAdapter/component/forcefield/AdaptiveBeamForceFieldAndMass.inl b/src/BeamAdapter/component/forcefield/AdaptiveBeamForceFieldAndMass.inl index b72f54b73..509600e71 100644 --- a/src/BeamAdapter/component/forcefield/AdaptiveBeamForceFieldAndMass.inl +++ b/src/BeamAdapter/component/forcefield/AdaptiveBeamForceFieldAndMass.inl @@ -74,16 +74,16 @@ void AdaptiveBeamForceFieldAndMass::init() if(!l_interpolation) l_interpolation.set(dynamic_cast(this->getContext())->get(BaseContext::Local)); - if (!l_interpolation) { + if (!l_interpolation) + { msg_error() << "No Beam Interpolation found, the component can not work."; this->d_componentState.setValue(sofa::core::objectmodel::ComponentState::Invalid); } else { - BaseContext* context = this->getContext(); - core::topology::BaseMeshTopology* topology = context->getMeshTopology(); + core::topology::BaseMeshTopology* topology = l_interpolation->l_topology.get(); auto massDensity = sofa::helper::getWriteOnlyAccessor(d_massDensity); - if (topology) + if(topology) { const auto& nbEdges = topology->getNbEdges(); if (massDensity.size() != nbEdges) @@ -123,19 +123,19 @@ void AdaptiveBeamForceFieldAndMass::computeGravityVector() template -void AdaptiveBeamForceFieldAndMass::computeStiffness(int beamId, BeamLocalMatrices& beamLocalMatrices) +void AdaptiveBeamForceFieldAndMass::computeStiffness(const sofa::Index beamId, BeamLocalMatrices& beamLocalMatrices) { SOFA_UNUSED(beamId); /// material parameters - Real _G = beamLocalMatrices._youngM / (2.0 * (1.0 + beamLocalMatrices._cPoisson)); + const Real _G = beamLocalMatrices._youngM / (2.0 * (1.0 + beamLocalMatrices._cPoisson)); - Real phiy, phiz; - Real L2 = (Real) (beamLocalMatrices._L * beamLocalMatrices._L); - Real L3 = (Real) (L2 * beamLocalMatrices._L); - Real EIy = (Real)(beamLocalMatrices._youngM * beamLocalMatrices._Iy); - Real EIz = (Real)(beamLocalMatrices._youngM * beamLocalMatrices._Iz); + const Real L2 = (Real) (beamLocalMatrices._L * beamLocalMatrices._L); + const Real L3 = (Real) (L2 * beamLocalMatrices._L); + const Real EIy = (Real)(beamLocalMatrices._youngM * beamLocalMatrices._Iy); + const Real EIz = (Real)(beamLocalMatrices._youngM * beamLocalMatrices._Iz); + Real phiy{0.0}, phiz{0.0}; /// Find shear-deformation parameters if (beamLocalMatrices._Asy == 0) phiy = 0.0; @@ -158,8 +158,8 @@ void AdaptiveBeamForceFieldAndMass::computeStiffness(int beamId, Beam beamLocalMatrices.m_K00[5][5] = beamLocalMatrices.m_K11[5][5] = (beamLocalMatrices._L == 0.0)? 0.0 : (Real)((4.0+phiy)*EIz/(beamLocalMatrices._L*(1.0+phiy))); /// diagonal blocks - beamLocalMatrices.m_K00[4][2] =(L2 == 0.0)? 0.0 : (Real)(-6.0*EIy/(L2*(1.0+phiz))); - beamLocalMatrices.m_K00[5][1] =(L2 == 0.0)? 0.0 : (Real)( 6.0*EIz/(L2*(1.0+phiy))); + beamLocalMatrices.m_K00[4][2] = (L2 == 0.0)? 0.0 : (Real)(-6.0*EIy/(L2*(1.0+phiz))); + beamLocalMatrices.m_K00[5][1] = (L2 == 0.0)? 0.0 : (Real)( 6.0*EIz/(L2*(1.0+phiy))); beamLocalMatrices.m_K11[5][1] = -beamLocalMatrices.m_K00[5][1]; beamLocalMatrices.m_K11[4][2] = -beamLocalMatrices.m_K00[4][2]; @@ -170,10 +170,10 @@ void AdaptiveBeamForceFieldAndMass::computeStiffness(int beamId, Beam beamLocalMatrices.m_K10[2][2] = -beamLocalMatrices.m_K00[2][2]; beamLocalMatrices.m_K10[2][4] = -beamLocalMatrices.m_K00[4][2]; beamLocalMatrices.m_K10[3][3] = -beamLocalMatrices.m_K00[3][3]; - beamLocalMatrices.m_K10[4][2] = beamLocalMatrices.m_K00[4][2]; - beamLocalMatrices.m_K10[4][4] =(beamLocalMatrices._L == 0.0)? 0.0 : (Real)((2.0-phiz)*EIy/(beamLocalMatrices._L*(1.0+phiz))); - beamLocalMatrices.m_K10[5][1] = beamLocalMatrices.m_K00[5][1]; - beamLocalMatrices.m_K10[5][5] =(beamLocalMatrices._L == 0.0)? 0.0 : (Real)((2.0-phiy)*EIz/(beamLocalMatrices._L*(1.0+phiy))); + beamLocalMatrices.m_K10[4][2] = beamLocalMatrices.m_K00[4][2]; + beamLocalMatrices.m_K10[4][4] = (beamLocalMatrices._L == 0.0)? 0.0 : (Real)((2.0-phiz)*EIy/(beamLocalMatrices._L*(1.0+phiz))); + beamLocalMatrices.m_K10[5][1] = beamLocalMatrices.m_K00[5][1]; + beamLocalMatrices.m_K10[5][5] = (beamLocalMatrices._L == 0.0)? 0.0 : (Real)((2.0-phiy)*EIz/(beamLocalMatrices._L*(1.0+phiy))); /// Make a symetric matrix with diagonal blocks for (int i=0; i<=5; i++) @@ -191,15 +191,15 @@ void AdaptiveBeamForceFieldAndMass::computeStiffness(int beamId, Beam template -void AdaptiveBeamForceFieldAndMass::computeMass(int beamId, BeamLocalMatrices& beamLocalMatrix) +void AdaptiveBeamForceFieldAndMass::computeMass(const sofa::Index beamID, BeamLocalMatrices& beamLocalMatrix) { - SOFA_UNUSED(beamId); - Real L2 = (Real) (beamLocalMatrix._L * beamLocalMatrix._L); + SOFA_UNUSED(beamID); + const Real L2 = beamLocalMatrix._L * beamLocalMatrix._L; beamLocalMatrix.m_M00.clear(); beamLocalMatrix.m_M01.clear(); beamLocalMatrix.m_M10.clear(); beamLocalMatrix.m_M11.clear(); - Real AL = beamLocalMatrix._A * beamLocalMatrix._L; - Real Iz_A = (beamLocalMatrix._A == 0.0) ? 0.0 : beamLocalMatrix._Iz / beamLocalMatrix._A; - Real Iy_A = (beamLocalMatrix._A == 0.0) ? 0.0 : beamLocalMatrix._Iy / beamLocalMatrix._A; + const Real AL = beamLocalMatrix._A * beamLocalMatrix._L; + const Real Iz_A = (beamLocalMatrix._A == 0.0) ? 0.0 : beamLocalMatrix._Iz / beamLocalMatrix._A; + const Real Iy_A = (beamLocalMatrix._A == 0.0) ? 0.0 : beamLocalMatrix._Iy / beamLocalMatrix._A; /// diagonal values beamLocalMatrix.m_M00[0][0] = beamLocalMatrix.m_M11[0][0] = (Real)(1.0 / 3.0); @@ -234,9 +234,9 @@ void AdaptiveBeamForceFieldAndMass::computeMass(int beamId, BeamLocal beamLocalMatrix.m_M10 *= beamLocalMatrix._rho * AL; /// Make a symetric matrix with diagonal blocks - for (int i=0; i<=5; i++) + for (sofa::Index i=0; i<=5; i++) { - for (int j=i+1; j<6; j++) + for (sofa::Index j=i+1; j<6; j++) { beamLocalMatrix.m_M00[i][j] = beamLocalMatrix.m_M00[j][i]; beamLocalMatrix.m_M11[i][j] = beamLocalMatrix.m_M11[j][i]; @@ -250,14 +250,14 @@ void AdaptiveBeamForceFieldAndMass::computeMass(int beamId, BeamLocal template void AdaptiveBeamForceFieldAndMass::applyStiffnessLarge( VecDeriv& df, const VecDeriv& dx, - int bIndex, Index nd0Id, Index nd1Id, - SReal factor ) + const sofa::Index beamID, const sofa::Index nd0Id, const sofa::Index nd1Id, + const SReal factor ) { - if(nd0Id==nd1Id) /// Return in case of rigidification + if(nd0Id == nd1Id) /// Return in case of rigidification return; Vec6NoInit U0, U1, u0, u1, f0, f1, F0, F1; - BeamLocalMatrices &beamLocalMatrix = m_localBeamMatrices[bIndex]; + const BeamLocalMatrices &beamLocalMatrix = m_localBeamMatrices[beamID]; for (unsigned int i=0; i<6; i++) { @@ -287,21 +287,21 @@ void AdaptiveBeamForceFieldAndMass::applyStiffnessLarge( VecDeriv& df template -void AdaptiveBeamForceFieldAndMass::applyMassLarge( VecDeriv& df, int bIndex, Index nd0Id, Index nd1Id, SReal factor) +void AdaptiveBeamForceFieldAndMass::applyMassLarge(VecDeriv& df, const sofa::Index beamID, const sofa::Index nd0Id, const sofa::Index nd1Id, const SReal factor) { - const BeamLocalMatrices &beamLocalMatrix = m_localBeamMatrices[bIndex]; + const BeamLocalMatrices &beamLocalMatrix = m_localBeamMatrices[beamID]; /// displacement in local frame (only gravity as external force) - Vec6 a0 = beamLocalMatrix.m_A0Ref * m_gravity; - Vec6 a1 = beamLocalMatrix.m_A1Ref * m_gravity; + const Vec6 a0 = beamLocalMatrix.m_A0Ref * m_gravity; + const Vec6 a1 = beamLocalMatrix.m_A1Ref * m_gravity; /// internal force in local frame - Vec6 f0 = beamLocalMatrix.m_M00*a0 + beamLocalMatrix.m_M01*a1; - Vec6 f1 = beamLocalMatrix.m_M10*a0 + beamLocalMatrix.m_M11*a1; + const Vec6 f0 = beamLocalMatrix.m_M00*a0 + beamLocalMatrix.m_M01*a1; + const Vec6 f1 = beamLocalMatrix.m_M10*a0 + beamLocalMatrix.m_M11*a1; /// force in global frame - Vec6 F0 = beamLocalMatrix.m_A0Ref.multTranspose(f0); - Vec6 F1 = beamLocalMatrix.m_A1Ref.multTranspose(f1); + const Vec6 F0 = beamLocalMatrix.m_A0Ref.multTranspose(f0); + const Vec6 F1 = beamLocalMatrix.m_A1Ref.multTranspose(f1); /// put the result in df for (unsigned int i=0; i<6; i++) @@ -318,21 +318,21 @@ void AdaptiveBeamForceFieldAndMass::applyMassLarge( VecDeriv& df, int ///////////////////////////////////// template -void AdaptiveBeamForceFieldAndMass::addMDx(const MechanicalParams* mparams , DataVecDeriv& dataf, const DataVecDeriv& datadx, SReal factor) +void AdaptiveBeamForceFieldAndMass::addMDx(const sofa::core::MechanicalParams* mparams , DataVecDeriv& dataf, const DataVecDeriv& datadx, const SReal factor) { SOFA_UNUSED(mparams); SOFA_UNUSED(datadx); auto f = sofa::helper::getWriteOnlyAccessor(dataf); - auto size = l_interpolation->getStateSize(); + const auto size = l_interpolation->getStateSize(); if (f.size() != size) f.resize(size); - unsigned int numBeams = l_interpolation->getNumBeams(); - for (unsigned int b=0; bgetNumBeams(); + for (sofa::Index b=0; bgetNodeIndices( b, node0Idx, node1Idx ); applyMassLarge( f.wref(), b, node0Idx, node1Idx, factor); @@ -341,35 +341,35 @@ void AdaptiveBeamForceFieldAndMass::addMDx(const MechanicalParams* mp template -void AdaptiveBeamForceFieldAndMass::addMToMatrix(const MechanicalParams *mparams, - const MultiMatrixAccessor* matrix) +void AdaptiveBeamForceFieldAndMass::addMToMatrix(const sofa::core::MechanicalParams *mparams, + const sofa::core::behavior::MultiMatrixAccessor* matrix) { - MultiMatrixAccessor::MatrixRef r = matrix->getMatrix(mstate); - Real mFact = (Real)mparams->mFactor(); + sofa::core::behavior::MultiMatrixAccessor::MatrixRef r = matrix->getMatrix(mstate); + const Real mFact = (Real)mparams->mFactor(); - unsigned int numBeams = l_interpolation->getNumBeams(); + const auto numBeams = l_interpolation->getNumBeams(); - for (unsigned int b=0; bgetNodeIndices( b, node0Idx, node1Idx ); /// matrices in global frame - Matrix6x6 M00 = bLM.m_A0Ref.multTranspose((bLM.m_M00 * bLM.m_A0Ref)); - Matrix6x6 M01 = bLM.m_A0Ref.multTranspose((bLM.m_M01 * bLM.m_A1Ref)); - Matrix6x6 M10 = bLM.m_A1Ref.multTranspose((bLM.m_M10 * bLM.m_A0Ref)); - Matrix6x6 M11 = bLM.m_A1Ref.multTranspose((bLM.m_M11 * bLM.m_A1Ref)); + const Matrix6x6 M00 = bLM.m_A0Ref.multTranspose((bLM.m_M00 * bLM.m_A0Ref)); + const Matrix6x6 M01 = bLM.m_A0Ref.multTranspose((bLM.m_M01 * bLM.m_A1Ref)); + const Matrix6x6 M10 = bLM.m_A1Ref.multTranspose((bLM.m_M10 * bLM.m_A0Ref)); + const Matrix6x6 M11 = bLM.m_A1Ref.multTranspose((bLM.m_M11 * bLM.m_A1Ref)); - int index0[6], index1[6]; - for (int i=0;i<6;i++) + sofa::Index index0[6], index1[6]; + for (sofa::Index i=0;i<6;i++) index0[i] = r.offset+node0Idx*6+i; - for (int i=0;i<6;i++) + for (sofa::Index i=0;i<6;i++) index1[i] = r.offset+node1Idx*6+i; - for (int i=0;i<6;i++) + for (sofa::Index i=0;i<6;i++) { - for (int j=0;j<6;j++) + for (sofa::Index j=0;j<6;j++) { r.matrix->add(index0[i], index0[j], M00(i,j)*mFact); r.matrix->add(index0[i], index1[j], M01(i,j)*mFact); @@ -384,13 +384,12 @@ void AdaptiveBeamForceFieldAndMass::addMToMatrix(const MechanicalPara template void AdaptiveBeamForceFieldAndMass::buildMassMatrix(sofa::core::behavior::MassMatrixAccumulator* matrices) { - const unsigned int numBeams = l_interpolation->getNumBeams(); - + const auto numBeams = l_interpolation->getNumBeams(); - for (unsigned int b=0; bgetNodeIndices( b, node0Idx, node1Idx ); /// matrices in global frame @@ -409,39 +408,39 @@ void AdaptiveBeamForceFieldAndMass::buildMassMatrix(sofa::core::behav template -void AdaptiveBeamForceFieldAndMass::addMBKToMatrix(const MechanicalParams* mparams, - const MultiMatrixAccessor* matrix) +void AdaptiveBeamForceFieldAndMass::addMBKToMatrix(const sofa::core::MechanicalParams* mparams, + const sofa::core::behavior::MultiMatrixAccessor* matrix) { - MultiMatrixAccessor::MatrixRef r = matrix->getMatrix(mstate); - Real kFact = (Real)mparams->kFactor(); - Real mFact = (Real)mparams->mFactor(); + sofa::core::behavior::MultiMatrixAccessor::MatrixRef r = matrix->getMatrix(mstate); + const Real kFact = (Real)mparams->kFactor(); + const Real mFact = (Real)mparams->mFactor(); Real totalMass = 0; - unsigned int numBeams = l_interpolation->getNumBeams(); + const auto numBeams = l_interpolation->getNumBeams(); - for (unsigned int b=0; bgetNodeIndices( b, node0Idx, node1Idx ); - int index0[6], index1[6]; - for (int i=0;i<6;i++) + sofa::Index index0[6], index1[6]; + for (sofa::Index i=0;i<6;i++) index0[i] = r.offset+node0Idx*6+i; - for (int i=0;i<6;i++) + for (sofa::Index i=0;i<6;i++) index1[i] = r.offset+node1Idx*6+i; - if(node0Idx!=node1Idx) // no rigidification + if(node0Idx != node1Idx) // no rigidification { // matrices in global frame - Matrix6x6 K00 = bLM.m_A0Ref.multTranspose((bLM.m_K00 * bLM.m_A0Ref)); - Matrix6x6 K01 = bLM.m_A0Ref.multTranspose((bLM.m_K01 * bLM.m_A1Ref)); - Matrix6x6 K10 = bLM.m_A1Ref.multTranspose((bLM.m_K10 * bLM.m_A0Ref)); - Matrix6x6 K11 = bLM.m_A1Ref.multTranspose((bLM.m_K11 * bLM.m_A1Ref)); + const Matrix6x6 K00 = bLM.m_A0Ref.multTranspose((bLM.m_K00 * bLM.m_A0Ref)); + const Matrix6x6 K01 = bLM.m_A0Ref.multTranspose((bLM.m_K01 * bLM.m_A1Ref)); + const Matrix6x6 K10 = bLM.m_A1Ref.multTranspose((bLM.m_K10 * bLM.m_A0Ref)); + const Matrix6x6 K11 = bLM.m_A1Ref.multTranspose((bLM.m_K11 * bLM.m_A1Ref)); - for (int i=0;i<6;i++) + for (sofa::Index i=0;i<6;i++) { - for (int j=0;j<6;j++) + for (sofa::Index j=0;j<6;j++) { r.matrix->add(index0[i], index0[j], - K00(i,j)*kFact); r.matrix->add(index0[i], index1[j], - K01(i,j)*kFact); @@ -452,14 +451,14 @@ void AdaptiveBeamForceFieldAndMass::addMBKToMatrix(const MechanicalPa } // matrices in global frame - Matrix6x6 M00 = bLM.m_A0Ref.multTranspose((bLM.m_M00 * bLM.m_A0Ref)); - Matrix6x6 M01 = bLM.m_A0Ref.multTranspose((bLM.m_M01 * bLM.m_A1Ref)); - Matrix6x6 M10 = bLM.m_A1Ref.multTranspose((bLM.m_M10 * bLM.m_A0Ref)); - Matrix6x6 M11 = bLM.m_A1Ref.multTranspose((bLM.m_M11 * bLM.m_A1Ref)); + const Matrix6x6 M00 = bLM.m_A0Ref.multTranspose((bLM.m_M00 * bLM.m_A0Ref)); + const Matrix6x6 M01 = bLM.m_A0Ref.multTranspose((bLM.m_M01 * bLM.m_A1Ref)); + const Matrix6x6 M10 = bLM.m_A1Ref.multTranspose((bLM.m_M10 * bLM.m_A0Ref)); + const Matrix6x6 M11 = bLM.m_A1Ref.multTranspose((bLM.m_M11 * bLM.m_A1Ref)); - for (int i=0;i<6;i++) + for (sofa::Index i=0;i<6;i++) { - for (int j=0;j<6;j++) + for (sofa::Index j=0;j<6;j++) { totalMass += M00(i,j)*mFact + M11(i,j)*mFact; r.matrix->add(index0[i], index0[j], M00(i,j)*mFact); @@ -483,7 +482,7 @@ void AdaptiveBeamForceFieldAndMass::buildDampingMatrix(core::behavior ///////////////////////////////////// template -void AdaptiveBeamForceFieldAndMass::addForce (const MechanicalParams* mparams , +void AdaptiveBeamForceFieldAndMass::addForce (const sofa::core::MechanicalParams* mparams , DataVecDeriv& dataf, const DataVecCoord& datax, const DataVecDeriv& v) @@ -496,7 +495,7 @@ void AdaptiveBeamForceFieldAndMass::addForce (const MechanicalParams* f.resize(x.size()); // current content of the vector will remain the same (http://www.cplusplus.com/reference/vector/vector/resize/) - unsigned int numBeams = l_interpolation->getNumBeams(); + const auto numBeams = l_interpolation->getNumBeams(); m_localBeamMatrices.resize(numBeams); if(d_computeMass.getValue()) @@ -525,8 +524,8 @@ void AdaptiveBeamForceFieldAndMass::addForce (const MechanicalParams* /// 2. Computes the frame of the beam based on the spline interpolation: Transform global_H_local; - Real baryX = 0.5; - Real L = l_interpolation->getLength(beamId); + constexpr Real baryX = 0.5; + const Real L = l_interpolation->getLength(beamId); l_interpolation->InterpolateTransformUsingSpline(global_H_local, baryX, global_H_local0, global_H_local1, L); @@ -584,7 +583,7 @@ void AdaptiveBeamForceFieldAndMass::addForce (const MechanicalParams* } /// IF RIGIDIFICATION: no stiffness forces: - if(node0Idx==node1Idx) + if(node0Idx == node1Idx) continue; /// compute the local stiffness matrices @@ -597,8 +596,8 @@ void AdaptiveBeamForceFieldAndMass::addForce (const MechanicalParams* l_interpolation->getSplineRestTransform(beamId, local_H_local0_rest, local_H_local1_rest); ///2. computes the local displacement of 0 and 1 in frame local: - SpatialVector u0 = local_H_local0.CreateSpatialVector() - local_H_local0_rest.CreateSpatialVector(); - SpatialVector u1 = local_H_local1.CreateSpatialVector() - local_H_local1_rest.CreateSpatialVector(); + const SpatialVector u0 = local_H_local0.CreateSpatialVector() - local_H_local0_rest.CreateSpatialVector(); + const SpatialVector u1 = local_H_local1.CreateSpatialVector() - local_H_local1_rest.CreateSpatialVector(); /// 3. put the result in a Vec6 Vec6NoInit U0local, U1local; @@ -615,7 +614,7 @@ void AdaptiveBeamForceFieldAndMass::addForce (const MechanicalParams* { Vec3 P0,P1,P2,P3; Real length; - Real rest_length = l_interpolation->getLength(beamId); + const Real rest_length = l_interpolation->getLength(beamId); l_interpolation->getSplinePoints(beamId, x, P0, P1, P2, P3); l_interpolation->computeActualLength(length, P0, P1, P2, P3); @@ -630,8 +629,8 @@ void AdaptiveBeamForceFieldAndMass::addForce (const MechanicalParams* Vec3 ResultNode0, ResultNode1; l_interpolation->computeStrechAndTwist(beamId, x, ResultNode0, ResultNode1); - Real ux0 =-ResultNode0[0] + l_interpolation->getLength(beamId)/2; - Real ux1 = ResultNode1[0] - l_interpolation->getLength(beamId)/2; + const Real ux0 =-ResultNode0[0] + l_interpolation->getLength(beamId)/2; + const Real ux1 = ResultNode1[0] - l_interpolation->getLength(beamId)/2; U0local[0] = ux0; U1local[0] = ux1; @@ -643,12 +642,12 @@ void AdaptiveBeamForceFieldAndMass::addForce (const MechanicalParams* } /// compute the force in the local frame: - Vec6 f0 = beamMatrices.m_K00 * U0local + beamMatrices.m_K01 * U1local; - Vec6 f1 = beamMatrices.m_K10 * U0local + beamMatrices.m_K11 * U1local; + const Vec6 f0 = beamMatrices.m_K00 * U0local + beamMatrices.m_K01 * U1local; + const Vec6 f1 = beamMatrices.m_K10 * U0local + beamMatrices.m_K11 * U1local; /// compute the force in the global frame - Vec6 F0_ref = beamMatrices.m_A0Ref.multTranspose(f0); - Vec6 F1_ref = beamMatrices.m_A1Ref.multTranspose(f1); + const Vec6 F0_ref = beamMatrices.m_A0Ref.multTranspose(f0); + const Vec6 F1_ref = beamMatrices.m_A1Ref.multTranspose(f1); /// Add this force to vector f for (unsigned int i=0; i<6; i++) @@ -668,7 +667,7 @@ void AdaptiveBeamForceFieldAndMass::addForce (const MechanicalParams* template -void AdaptiveBeamForceFieldAndMass::addDForce(const MechanicalParams* mparams, +void AdaptiveBeamForceFieldAndMass::addDForce(const sofa::core::MechanicalParams* mparams, DataVecDeriv& datadF, const DataVecDeriv& datadX ) { auto df = sofa::helper::getWriteOnlyAccessor(datadF); @@ -677,11 +676,11 @@ void AdaptiveBeamForceFieldAndMass::addDForce(const MechanicalParams* df.resize(dx.size()); // current content of the vector will remain the same (http://www.cplusplus.com/reference/vector/vector/resize/) - unsigned int numBeams = l_interpolation->getNumBeams(); + const auto numBeams = l_interpolation->getNumBeams(); - for (unsigned int b=0; bgetNodeIndices( b, node0Idx, node1Idx ); applyStiffnessLarge( df.wref(), dx, b, node0Idx, node1Idx, kFactor); @@ -691,38 +690,38 @@ void AdaptiveBeamForceFieldAndMass::addDForce(const MechanicalParams* template -void AdaptiveBeamForceFieldAndMass::addKToMatrix(const MechanicalParams* mparams, - const MultiMatrixAccessor* matrix) +void AdaptiveBeamForceFieldAndMass::addKToMatrix(const sofa::core::MechanicalParams* mparams, + const sofa::core::behavior::MultiMatrixAccessor* matrix) { - MultiMatrixAccessor::MatrixRef matrixRef = matrix->getMatrix(mstate); - Real k = (Real)mparams->kFactor(); + sofa::core::behavior::MultiMatrixAccessor::MatrixRef matrixRef = matrix->getMatrix(mstate); + const Real k = (Real)mparams->kFactor(); - unsigned int numBeams = l_interpolation->getNumBeams(); + const auto numBeams = l_interpolation->getNumBeams(); - for (unsigned int b=0; bgetNodeIndices( b, node0Idx, node1Idx ); - if(node0Idx==node1Idx) + if(node0Idx == node1Idx) continue; // matrices in global frame - Matrix6x6 K00 = beamLocalMatrix.m_A0Ref.multTranspose((beamLocalMatrix.m_K00 * beamLocalMatrix.m_A0Ref)); - Matrix6x6 K01 = beamLocalMatrix.m_A0Ref.multTranspose((beamLocalMatrix.m_K01 * beamLocalMatrix.m_A1Ref)); - Matrix6x6 K10 = beamLocalMatrix.m_A1Ref.multTranspose((beamLocalMatrix.m_K10 * beamLocalMatrix.m_A0Ref)); - Matrix6x6 K11 = beamLocalMatrix.m_A1Ref.multTranspose((beamLocalMatrix.m_K11 * beamLocalMatrix.m_A1Ref)); + const Matrix6x6 K00 = beamLocalMatrix.m_A0Ref.multTranspose((beamLocalMatrix.m_K00 * beamLocalMatrix.m_A0Ref)); + const Matrix6x6 K01 = beamLocalMatrix.m_A0Ref.multTranspose((beamLocalMatrix.m_K01 * beamLocalMatrix.m_A1Ref)); + const Matrix6x6 K10 = beamLocalMatrix.m_A1Ref.multTranspose((beamLocalMatrix.m_K10 * beamLocalMatrix.m_A0Ref)); + const Matrix6x6 K11 = beamLocalMatrix.m_A1Ref.multTranspose((beamLocalMatrix.m_K11 * beamLocalMatrix.m_A1Ref)); - int index0[6], index1[6]; - for (int i=0;i<6;i++) + sofa::Index index0[6], index1[6]; + for (sofa::Index i=0;i<6;i++) index0[i] = matrixRef.offset+node0Idx*6+i; - for (int i=0;i<6;i++) + for (sofa::Index i=0;i<6;i++) index1[i] = matrixRef.offset+node1Idx*6+i; - for (int i=0;i<6;i++) + for (sofa::Index i=0;i<6;i++) { - for (int j=0;j<6;j++) + for (sofa::Index j=0;j<6;j++) { matrixRef.matrix->add(index0[i], index0[j], - K00(i,j)*k); matrixRef.matrix->add(index0[i], index1[j], - K01(i,j)*k); @@ -736,27 +735,26 @@ void AdaptiveBeamForceFieldAndMass::addKToMatrix(const MechanicalPara template void AdaptiveBeamForceFieldAndMass::buildStiffnessMatrix(core::behavior::StiffnessMatrix* matrix) { - const unsigned int numBeams = l_interpolation->getNumBeams(); + const auto numBeams = l_interpolation->getNumBeams(); auto dfdx = matrix->getForceDerivativeIn(this->mstate) .withRespectToPositionsIn(this->mstate); - for (unsigned int b=0; bgetNodeIndices( b, node0Idx, node1Idx ); - if(node0Idx==node1Idx) + if(node0Idx == node1Idx) continue; // matrices in global frame - Matrix6x6 K00 = beamLocalMatrix.m_A0Ref.multTranspose(beamLocalMatrix.m_K00 * beamLocalMatrix.m_A0Ref); - Matrix6x6 K01 = beamLocalMatrix.m_A0Ref.multTranspose(beamLocalMatrix.m_K01 * beamLocalMatrix.m_A1Ref); - Matrix6x6 K10 = beamLocalMatrix.m_A1Ref.multTranspose(beamLocalMatrix.m_K10 * beamLocalMatrix.m_A0Ref); - Matrix6x6 K11 = beamLocalMatrix.m_A1Ref.multTranspose(beamLocalMatrix.m_K11 * beamLocalMatrix.m_A1Ref); - + const Matrix6x6 K00 = beamLocalMatrix.m_A0Ref.multTranspose(beamLocalMatrix.m_K00 * beamLocalMatrix.m_A0Ref); + const Matrix6x6 K01 = beamLocalMatrix.m_A0Ref.multTranspose(beamLocalMatrix.m_K01 * beamLocalMatrix.m_A1Ref); + const Matrix6x6 K10 = beamLocalMatrix.m_A1Ref.multTranspose(beamLocalMatrix.m_K10 * beamLocalMatrix.m_A0Ref); + const Matrix6x6 K11 = beamLocalMatrix.m_A1Ref.multTranspose(beamLocalMatrix.m_K11 * beamLocalMatrix.m_A1Ref); dfdx(node0Idx*6, node0Idx*6) += - K00; dfdx(node0Idx*6, node1Idx*6) += - K01; @@ -771,7 +769,7 @@ void AdaptiveBeamForceFieldAndMass::buildStiffnessMatrix(core::behavi ///////////////////////////////////// template -void AdaptiveBeamForceFieldAndMass::draw(const VisualParams *vparams) +void AdaptiveBeamForceFieldAndMass::draw(const sofa::core::visual::VisualParams *vparams) { if (!vparams->displayFlags().getShowForceFields() && !vparams->displayFlags().getShowBehaviorModels()) return; if (!mstate) return; @@ -780,9 +778,10 @@ void AdaptiveBeamForceFieldAndMass::draw(const VisualParams *vparams) ReadAccessor > x = mstate->read(sofa::core::vec_id::read_access::position) ; - unsigned int numBeams = l_interpolation->getNumBeams(); + const auto numBeams = l_interpolation->getNumBeams(); + constexpr Vec3 localPos(0.0,0.0,0.0); - for (unsigned int b=0; b::draw(const VisualParams *vparams) l_interpolation->getNodeIndices(b, node0Idx, node1Idx); l_interpolation->computeTransform(b, node0Idx, node1Idx, globalH0Local, globalH1Local, x.ref()); - if (vparams->displayFlags().getShowBehaviorModels() && node0Idx!=node1Idx) + if (vparams->displayFlags().getShowBehaviorModels() && node0Idx != node1Idx) drawElement(vparams, b, globalH0Local, globalH1Local); if(vparams->displayFlags().getShowForceFields()) { - // / test /// + constexpr double nbDiscretization = 50.0; + constexpr double step = 1.0/nbDiscretization; + type::vector points; Vec3 pos = globalH0Local.getOrigin(); - for (double i=0.0; i<1.00001; i+=0.02) + + for (double i=0.0; i<1.00001; i += step) { points.push_back(pos); - Vec3 localPos(0.0,0.0,0.0); this->l_interpolation->interpolatePointUsingSpline(b, i, localPos, x.ref(), pos); points.push_back(pos); } - if(node0Idx==node1Idx) /// rigidification case + if(node0Idx == node1Idx) /// rigidification case { - vparams->drawTool()->drawLines(points,2, sofa::type::RGBAColor(0,0,1,1)); + vparams->drawTool()->drawLines(points,2, sofa::type::RGBAColor::blue()); continue; } else - vparams->drawTool()->drawLines(points,2, sofa::type::RGBAColor(1,0,0,1)); - - double length = (double) l_interpolation->getLength(b); + { + vparams->drawTool()->drawLines(points,2, sofa::type::RGBAColor::red()); + } + const float length = static_cast(l_interpolation->getLength(b)); - Vec3 localPos(0.0,0.0,0.0); - Real baryX = 0.5; + constexpr Real baryX = 0.5; Transform globalHLocalInterpol; l_interpolation->InterpolateTransformUsingSpline(b, baryX, localPos, x.ref(), globalHLocalInterpol); Quat q = globalHLocalInterpol.getOrientation(); q.normalize(); - Vec3 P1, x,y,z; - P1 = globalHLocalInterpol.getOrigin(); - x= q.rotate(Vec3(length/6.0,0,0)); - y= q.rotate(Vec3(0,length/8.0,0)); - z= q.rotate(Vec3(0,0,length/8.0)); - float radius_arrow = (float)length/60.0f; + const Vec3 P1 = globalHLocalInterpol.getOrigin(); + const Vec3 x = q.rotate(Vec3(length/6.0,0,0)); + const Vec3 y = q.rotate(Vec3(0,length/8.0,0)); + const Vec3 z = q.rotate(Vec3(0,0,length/8.0)); + const float radius_arrow = static_cast(length/60.0f); - vparams->drawTool()->drawArrow(P1,P1 + x, radius_arrow, sofa::type::RGBAColor(1,0,0,1)); - vparams->drawTool()->drawArrow(P1,P1 + y, radius_arrow, sofa::type::RGBAColor(1,0,0,1)); - vparams->drawTool()->drawArrow(P1,P1 + z, radius_arrow, sofa::type::RGBAColor(1,0,0,1)); + vparams->drawTool()->drawArrow(P1,P1 + x, radius_arrow, sofa::type::RGBAColor::red()); + vparams->drawTool()->drawArrow(P1,P1 + y, radius_arrow, sofa::type::RGBAColor::red()); + vparams->drawTool()->drawArrow(P1,P1 + z, radius_arrow, sofa::type::RGBAColor::red()); } } @@ -843,13 +843,13 @@ void AdaptiveBeamForceFieldAndMass::draw(const VisualParams *vparams) template -void AdaptiveBeamForceFieldAndMass::drawElement(const VisualParams *vparams, int beam, - Transform &global_H0_local, Transform &global_H1_local) +void AdaptiveBeamForceFieldAndMass::drawElement(const sofa::core::visual::VisualParams *vparams, const sofa::Index beamID, + const Transform &global_H0_local, const Transform &global_H1_local) { - double length = (double) l_interpolation->getLength(beam); + const float length = static_cast(l_interpolation->getLength(beamID)); /// ARROWS - Vec3 sizeArrows (length/4., length/8., length/8.); + const type::Vec3f sizeArrows (length/4.0f, length/8.0f, length/8.0f); vparams->drawTool()->drawFrame(global_H0_local.getOrigin(), global_H0_local.getOrientation(), sizeArrows); vparams->drawTool()->drawFrame(global_H1_local.getOrigin(), global_H1_local.getOrientation(), sizeArrows); From 1376054af5b135ef68345add506ec41a90350677 Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Fri, 16 May 2025 11:34:49 +0900 Subject: [PATCH 21/24] fix for refactored SofaCUDA, add registrations, consistency --- CMakeLists.txt | 2 +- .../BeamAdapter/CUDA/CudaInstantiations.cpp | 149 +++++++----------- extensions/CUDA/src/BeamAdapter/CUDA/init.cpp | 18 ++- .../component/engine/WireRestShape.inl | 2 +- 4 files changed, 71 insertions(+), 100 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 85302c55c..6a6e11764 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.12) -project(BeamAdapter VERSION 1.0) +project(BeamAdapter VERSION 1.0 LANGUAGES CXX CUDA) include(cmake/environment.cmake) diff --git a/extensions/CUDA/src/BeamAdapter/CUDA/CudaInstantiations.cpp b/extensions/CUDA/src/BeamAdapter/CUDA/CudaInstantiations.cpp index 3c56f451b..b2ca6ea62 100644 --- a/extensions/CUDA/src/BeamAdapter/CUDA/CudaInstantiations.cpp +++ b/extensions/CUDA/src/BeamAdapter/CUDA/CudaInstantiations.cpp @@ -20,7 +20,7 @@ * Contact information: contact@sofa-framework.org * ******************************************************************************/ #include -#include +#include #include #include @@ -36,149 +36,114 @@ #include #include #include -#include // for InterventionalRadiologyController +#include // for InterventionalRadiologyController #include using namespace sofa::gpu::cuda; -namespace sofa::component::fem::_beaminterpolation_ +namespace beamadapter::cuda { // template class SOFA_BEAMADAPTER_CUDA_API BeamInterpolation; -#ifdef SOFA_GPU_CUDA_DOUBLE - template class SOFA_BEAMADAPTER_CUDA_API BeamInterpolation; -#endif -} // namespace sofa::component::fem::_beaminterpolation_ - -namespace sofa::component::fem::_wirebeaminterpolation_ -{ // template class SOFA_BEAMADAPTER_CUDA_API WireBeamInterpolation; -#ifdef SOFA_GPU_CUDA_DOUBLE - template class SOFA_BEAMADAPTER_CUDA_API WireBeamInterpolation; -#endif -} // namespace sofa::component::fem::_beaminterpolation_ - -namespace sofa::component::engine::_wirerestshape_ -{ template class SOFA_BEAMADAPTER_CUDA_API WireRestShape; -#ifdef SOFA_GPU_CUDA_DOUBLE - template class SOFA_BEAMADAPTER_CUDA_API WireRestShape; -#endif -} // namespace sofa::component::engine::_wirerestshape_ - -namespace sofa::component::controller::_interventionalradiologycontroller_ -{ template class SOFA_BEAMADAPTER_CUDA_API InterventionalRadiologyController; -#ifdef SOFA_GPU_CUDA_DOUBLE - template class SOFA_BEAMADAPTER_CUDA_API InterventionalRadiologyController; -#endif -} // namespace sofa::component::controller::_interventionalradiologycontroller_ - -namespace sofa::component::mapping::_adaptivebeammapping_ -{ template class SOFA_BEAMADAPTER_CUDA_API AdaptiveBeamMapping; -#ifdef SOFA_GPU_CUDA_DOUBLE - template class SOFA_BEAMADAPTER_CUDA_API AdaptiveBeamMapping; -#endif -} // namespace sofa::component::mapping::_adaptivebeammapping_ - -namespace sofa::component::mapping -{ template class SOFA_BEAMADAPTER_CUDA_API MultiAdaptiveBeamMapping; -#ifdef SOFA_GPU_CUDA_DOUBLE - template class SOFA_BEAMADAPTER_CUDA_API MultiAdaptiveBeamMapping; -#endif -} // namespace sofa::component::mapping - -namespace sofa::beamadapter -{ template class SOFA_BEAMADAPTER_CUDA_API RodMeshSection; template class SOFA_BEAMADAPTER_CUDA_API RodSpireSection; template class SOFA_BEAMADAPTER_CUDA_API RodStraightSection; #ifdef SOFA_GPU_CUDA_DOUBLE + template class SOFA_BEAMADAPTER_CUDA_API BeamInterpolation; + template class SOFA_BEAMADAPTER_CUDA_API WireBeamInterpolation; + template class SOFA_BEAMADAPTER_CUDA_API WireRestShape; + template class SOFA_BEAMADAPTER_CUDA_API InterventionalRadiologyController; + template class SOFA_BEAMADAPTER_CUDA_API AdaptiveBeamMapping; + template class SOFA_BEAMADAPTER_CUDA_API MultiAdaptiveBeamMapping; template class SOFA_BEAMADAPTER_CUDA_API RodMeshSection; template class SOFA_BEAMADAPTER_CUDA_API RodSpireSection; template class SOFA_BEAMADAPTER_CUDA_API RodStraightSection; #endif -} // namespace sofa::beamadapter +using namespace sofa::gpu::cuda; -namespace sofa::gpu::cuda +void registerBeamAdapterCUDAComponents(sofa::core::ObjectFactory* factory) { - #ifdef SOFA_GPU_CUDA_DOUBLE -int CudaBeamInterpolationClass = core::RegisterObject("Adaptive Beam Interpolation - Supports GPU-side computations using CUDA") - // .add< sofa::component::fem::BeamInterpolation >() - .add< sofa::component::fem::BeamInterpolation >() - ; + factory->registerObjects(sofa::core::ObjectRegistrationData("Adaptive Beam Interpolation - Supports GPU-side computations using CUDA") + // .add< BeamInterpolation >() + .add< BeamInterpolation >()); #endif #ifdef SOFA_GPU_CUDA_DOUBLE -int CudaWireBeamInterpolationClass = core::RegisterObject("Adaptive Wire Beam Interpolation - Supports GPU-side computations using CUDA") - // .add< sofa::component::fem::WireBeamInterpolation >() - .add< sofa::component::fem::WireBeamInterpolation >() - ; + factory->registerObjects(sofa::core::ObjectRegistrationData("Adaptive Wire Beam Interpolation - Supports GPU-side computations using CUDA") + // .add< WireBeamInterpolation >() + .add< WireBeamInterpolation >()); #endif -int CudaWireRestShapenClass = core::RegisterObject("Wire Shape - Supports GPU-side computations using CUDA") - .add< sofa::component::engine::WireRestShape >() + factory->registerObjects(sofa::core::ObjectRegistrationData("Wire Shape - Supports GPU-side computations using CUDA") + .add< WireRestShape >() #ifdef SOFA_GPU_CUDA_DOUBLE - .add< sofa::component::engine::WireRestShape >() + .add< WireRestShape >() #endif - ; + ); -int CudaAdaptiveBeamForceFieldAndMassClass = core::RegisterObject("Adaptive Beam finite elements - Supports GPU-side computations using CUDA") - .add< sofa::component::forcefield::AdaptiveBeamForceFieldAndMass >() + factory->registerObjects(sofa::core::ObjectRegistrationData("Adaptive Beam finite elements - Supports GPU-side computations using CUDA") + .add< AdaptiveBeamForceFieldAndMass >() #ifdef SOFA_GPU_CUDA_DOUBLE - .add< sofa::component::forcefield::AdaptiveBeamForceFieldAndMass >() + .add< AdaptiveBeamForceFieldAndMass >() #endif - ; + ); -int CudaInterventionalRadiologyControllerClass = core::RegisterObject("Provides a Mouse & Keyboard user control on an EdgeSet Topology - Supports GPU-side computations using CUDA") - .add< sofa::component::controller::InterventionalRadiologyController >() + factory->registerObjects(sofa::core::ObjectRegistrationData("Provides a Mouse & Keyboard user control on an EdgeSet Topology - Supports GPU-side computations using CUDA") + .add< InterventionalRadiologyController >() #ifdef SOFA_GPU_CUDA_DOUBLE - .add< sofa::component::controller::InterventionalRadiologyController >() + .add< InterventionalRadiologyController >() #endif - ; + ); -int CudaAdaptiveBeamMappingClass = core::RegisterObject("Set the positions and velocities of points attached to a beam using linear interpolation between DOFs - Supports GPU-side computations using CUDA") - .add< sofa::component::mapping::AdaptiveBeamMapping >() + factory->registerObjects(sofa::core::ObjectRegistrationData("Provides a Mouse & Keyboard user control on an EdgeSet Topology - Supports GPU-side computations using CUDA") + .add< InterventionalRadiologyController >() #ifdef SOFA_GPU_CUDA_DOUBLE - .add< sofa::component::mapping::AdaptiveBeamMapping >() + .add< InterventionalRadiologyController >() #endif - ; + ); -int CudaMultiAdaptiveBeamMappingClass = core::RegisterObject("Set the positions and velocities of points attached to a beam using linear interpolation between DOFs - Supports GPU-side computations using CUDA") - .add< sofa::component::mapping::MultiAdaptiveBeamMapping >() + factory->registerObjects(sofa::core::ObjectRegistrationData("Set the positions and velocities of points attached to a beam using linear interpolation between DOFs - Supports GPU-side computations using CUDA") + .add< AdaptiveBeamMapping >() #ifdef SOFA_GPU_CUDA_DOUBLE - .add< sofa::component::mapping::MultiAdaptiveBeamMapping >() + .add< AdaptiveBeamMapping >() #endif - ; + ); -const int CudaRodMeshSectionClass = core::RegisterObject("Class defining a Rod Section using a MeshLoader and material parameters using CUDA.") - .add< sofa::beamadapter::RodMeshSection >() + factory->registerObjects(sofa::core::ObjectRegistrationData("Set the positions and velocities of points attached to a beam using linear interpolation between DOFs - Supports GPU-side computations using CUDA") + .add< MultiAdaptiveBeamMapping >() #ifdef SOFA_GPU_CUDA_DOUBLE - .add< sofa::beamadapter::RodMeshSection >() + .add< MultiAdaptiveBeamMapping >() #endif - ; + ); -const int CudaRodSpireSectionClass = core::RegisterObject("Class defining a rod spire section, defining material and geometry parameters using CUDA.") - .add< sofa::beamadapter::RodSpireSection >() + factory->registerObjects(sofa::core::ObjectRegistrationData("Class defining a Rod Section using a MeshLoader and material parameters using CUDA.") + .add< RodMeshSection >() #ifdef SOFA_GPU_CUDA_DOUBLE - .add< sofa::beamadapter::RodSpireSection >() + .add< RodMeshSection >() #endif -; + ); -const int CudaRodStraightSectionClass = core::RegisterObject("Class defining a rod straight section Material, defining material and geometry parameters using CUDA.") - .add< sofa::beamadapter::RodStraightSection >() + factory->registerObjects(sofa::core::ObjectRegistrationData("Class defining a rod spire section, defining material and geometry parameters using CUDA.") + .add< RodSpireSection >() #ifdef SOFA_GPU_CUDA_DOUBLE - .add< sofa::beamadapter::RodStraightSection >() + .add< RodSpireSection >() #endif -; + ); + factory->registerObjects(sofa::core::ObjectRegistrationData("Class defining a rod straight section Material, defining material and geometry parameters using CUDA.") + .add< RodStraightSection >() +#ifdef SOFA_GPU_CUDA_DOUBLE + .add< RodStraightSection >() +#endif + ); +} - - -} // namespace sofa::gpu::cuda - +} // namespace beamadapter diff --git a/extensions/CUDA/src/BeamAdapter/CUDA/init.cpp b/extensions/CUDA/src/BeamAdapter/CUDA/init.cpp index 585cdc745..b29d0a635 100644 --- a/extensions/CUDA/src/BeamAdapter/CUDA/init.cpp +++ b/extensions/CUDA/src/BeamAdapter/CUDA/init.cpp @@ -22,15 +22,20 @@ #include #include #include + #include +#include + namespace beamadapter::cuda { +extern void registerBeamAdapterCUDAComponents(sofa::core::ObjectFactory* factory); + extern "C" { SOFA_EXPORT_DYNAMIC_LIBRARY void initExternalModule(); SOFA_EXPORT_DYNAMIC_LIBRARY const char* getModuleName(); SOFA_EXPORT_DYNAMIC_LIBRARY const char* getModuleVersion(); - SOFA_EXPORT_DYNAMIC_LIBRARY const char* getModuleComponentList(); + SOFA_BEAMADAPTER_API void registerObjects(sofa::core::ObjectFactory* factory); } void initExternalModule() @@ -53,17 +58,18 @@ void init() static bool first = true; if (first) { - sofa::component::initBeamAdapter(); + // make sure that this plugin is registered into the PluginManager + sofa::helper::system::PluginManager::getInstance().registerPlugin(MODULE_NAME); + + beamadapter::initBeamAdapter(); sofa::gpu::cuda::init(); first = false; } } -const char* getModuleComponentList() +void registerObjects(sofa::core::ObjectFactory* factory) { - /// string containing the names of the classes provided by the plugin - static std::string classes = sofa::core::ObjectFactory::getInstance()->listClassesFromTarget(MODULE_NAME); - return classes.c_str(); + registerBeamAdapterCUDAComponents(factory); } } // namespace beamadapter::cuda diff --git a/src/BeamAdapter/component/engine/WireRestShape.inl b/src/BeamAdapter/component/engine/WireRestShape.inl index 3fed22982..dcd08e25d 100644 --- a/src/BeamAdapter/component/engine/WireRestShape.inl +++ b/src/BeamAdapter/component/engine/WireRestShape.inl @@ -198,7 +198,7 @@ void WireRestShape::getMechanicalSampling(Real &dx, const Real x_curv Real x_used = x_curv - EPSILON; const Real totalLength = this->getLength(); - x_used = std::clamp(x_used, 0.0, totalLength); + x_used = std::clamp(x_used, static_cast(0.0), totalLength); const type::vector& keyPts = d_keyPoints.getValue(); From 8dd52e1944327813c149e71725bea3bb1429fb5d Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Mon, 19 May 2025 12:29:06 +0900 Subject: [PATCH 22/24] set cuda setting for the actual cuda project --- CMakeLists.txt | 2 +- extensions/CUDA/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6a6e11764..d136f5563 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.12) -project(BeamAdapter VERSION 1.0 LANGUAGES CXX CUDA) +project(BeamAdapter VERSION 1.0 LANGUAGES CXX) include(cmake/environment.cmake) diff --git a/extensions/CUDA/CMakeLists.txt b/extensions/CUDA/CMakeLists.txt index 51ffef0dc..edcfdfcde 100644 --- a/extensions/CUDA/CMakeLists.txt +++ b/extensions/CUDA/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.12) -project(BeamAdapter.CUDA) +project(BeamAdapter.CUDA LANGUAGES CXX CUDA) set(HEADER_FILES src/BeamAdapter/CUDA/init.h From 298c3a544286c2a63754ff4b8196b94c85f36c9d Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Mon, 23 Jun 2025 11:05:02 +0900 Subject: [PATCH 23/24] Fix compilation with gcc --- .../CUDA/src/BeamAdapter/CUDA/CudaInstantiations.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/extensions/CUDA/src/BeamAdapter/CUDA/CudaInstantiations.cpp b/extensions/CUDA/src/BeamAdapter/CUDA/CudaInstantiations.cpp index b2ca6ea62..bc5f3437f 100644 --- a/extensions/CUDA/src/BeamAdapter/CUDA/CudaInstantiations.cpp +++ b/extensions/CUDA/src/BeamAdapter/CUDA/CudaInstantiations.cpp @@ -42,7 +42,7 @@ using namespace sofa::gpu::cuda; -namespace beamadapter::cuda +namespace beamadapter { // template class SOFA_BEAMADAPTER_CUDA_API BeamInterpolation; // template class SOFA_BEAMADAPTER_CUDA_API WireBeamInterpolation; @@ -67,7 +67,8 @@ namespace beamadapter::cuda #endif using namespace sofa::gpu::cuda; - +namespace cuda +{ void registerBeamAdapterCUDAComponents(sofa::core::ObjectFactory* factory) { #ifdef SOFA_GPU_CUDA_DOUBLE @@ -146,4 +147,6 @@ void registerBeamAdapterCUDAComponents(sofa::core::ObjectFactory* factory) ); } +} // namespace cuda + } // namespace beamadapter From 659d5f0321a7f0837a29e639c453e6579a2aed74 Mon Sep 17 00:00:00 2001 From: Paul Baksic <30337881+bakpaul@users.noreply.github.com> Date: Thu, 3 Jul 2025 02:38:56 -0400 Subject: [PATCH 24/24] Change data name (#195) --- examples/3instruments_collis.scn | 4 ++-- examples/SingleBeamDeploymentCollision.scn | 4 ++-- examples/python3/SingleBeamDeploymentCollision.py | 4 ++-- examples/python3/beamadapter/parts/instrument.py | 8 ++++---- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/examples/3instruments_collis.scn b/examples/3instruments_collis.scn index 98cf3c4b0..ea5c8b58c 100644 --- a/examples/3instruments_collis.scn +++ b/examples/3instruments_collis.scn @@ -109,8 +109,8 @@ - - + + diff --git a/examples/SingleBeamDeploymentCollision.scn b/examples/SingleBeamDeploymentCollision.scn index 8a2c494fc..6c6cd5f53 100644 --- a/examples/SingleBeamDeploymentCollision.scn +++ b/examples/SingleBeamDeploymentCollision.scn @@ -66,8 +66,8 @@ - - + + diff --git a/examples/python3/SingleBeamDeploymentCollision.py b/examples/python3/SingleBeamDeploymentCollision.py index 09f80a18f..f56869dde 100644 --- a/examples/python3/SingleBeamDeploymentCollision.py +++ b/examples/python3/SingleBeamDeploymentCollision.py @@ -63,8 +63,8 @@ def createScene(rootNode): BeamCollis.addObject('EdgeSetTopologyModifier', name='colliseEdgeModifier') BeamCollis.addObject('MechanicalObject', name='CollisionDOFs') BeamCollis.addObject('MultiAdaptiveBeamMapping', controller='../DeployController', useCurvAbs=True, printLog=False, name='collisMap') - BeamCollis.addObject('LineCollisionModel', proximity=0.0) - BeamCollis.addObject('PointCollisionModel', proximity=0.0) + BeamCollis.addObject('LineCollisionModel', contactDistance=0.0) + BeamCollis.addObject('PointCollisionModel', contactDistance=0.0) Carotids = rootNode.addChild('Carotids') Carotids.addObject('MeshSTLLoader', filename='../mesh/carotids.stl', flipNormals=False, triangulate=True, name='meshLoader', rotation=[10.0, 0.0, -90.0]) diff --git a/examples/python3/beamadapter/parts/instrument.py b/examples/python3/beamadapter/parts/instrument.py index 2fab3fed0..aa2249a6b 100644 --- a/examples/python3/beamadapter/parts/instrument.py +++ b/examples/python3/beamadapter/parts/instrument.py @@ -101,8 +101,8 @@ def createInstrumentsCombined(node, xtip=[1, 0, 0], instruments=['guide'], Collis.addObject('MechanicalObject', name='CollisionDOFs') Collis.addObject('MultiAdaptiveBeamMapping', controller='../m_ircontroller', useCurvAbs=True, printLog=False, name='collisMap') - Collis.addObject('LineCollisionModel', proximity=0.0, group=1) - Collis.addObject('PointCollisionModel', proximity=0.0, group=1) + Collis.addObject('LineCollisionModel', contactDistance=0.0, group=1) + Collis.addObject('PointCollisionModel', contactDistance=0.0, group=1) # Visualization Guide VisuGuide = InstrumentCombined.addChild('VisuGuide') @@ -142,8 +142,8 @@ def createInstrumentsCombinedXRay(node, xtip=[1, 0, 0], instruments=['guide'], Collis.addObject('MechanicalObject', name='CollisionDOFs') Collis.addObject('MultiAdaptiveBeamMapping', controller='../m_ircontroller', useCurvAbs=True, printLog=False, name='collisMap') - Collis.addObject('LineCollisionModel', proximity=0.0, group=1) - Collis.addObject('PointCollisionModel', proximity=0.0, group=1) + Collis.addObject('LineCollisionModel', contactDistance=0.0, group=1) + Collis.addObject('PointCollisionModel', contactDistance=0.0, group=1) # Visualization Guide VisuGuide = InstrumentCombined.addChild('VisuGuide')