diff --git a/CMakeLists.txt b/CMakeLists.txt
index b36adb4ae..43ac28215 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -34,6 +34,8 @@ set(HEADER_FILES
${BEAMADAPTER_SRC}/component/controller/AdaptiveBeamController.h
${BEAMADAPTER_SRC}/component/controller/AdaptiveBeamController.inl
+ ${BEAMADAPTER_SRC}/component/controller/BeamAdapterActionController.h
+ ${BEAMADAPTER_SRC}/component/controller/BeamAdapterActionController.inl
${BEAMADAPTER_SRC}/component/controller/InterventionalRadiologyController.h
${BEAMADAPTER_SRC}/component/controller/InterventionalRadiologyController.inl
${BEAMADAPTER_SRC}/component/controller/SutureController.h
@@ -55,6 +57,7 @@ set(HEADER_FILES
${BEAMADAPTER_SRC}/component/mapping/MultiAdaptiveBeamMapping.inl
${BEAMADAPTER_SRC}/utils/BeamSection.h
+ ${BEAMADAPTER_SRC}/utils/BeamActions.h
${BEAMADAPTER_SRC}/utils/deprecatedcomponent.h
)
@@ -68,6 +71,7 @@ set(SOURCE_FILES
${BEAMADAPTER_SRC}/component/constraint/AdaptiveBeamLengthConstraint.cpp
${BEAMADAPTER_SRC}/component/controller/AdaptiveBeamController.cpp
+ ${BEAMADAPTER_SRC}/component/controller/BeamAdapterActionController.cpp
${BEAMADAPTER_SRC}/component/controller/InterventionalRadiologyController.cpp
${BEAMADAPTER_SRC}/component/controller/SutureController.cpp
diff --git a/src/BeamAdapter/component/controller/BeamAdapterActionController.cpp b/src/BeamAdapter/component/controller/BeamAdapterActionController.cpp
new file mode 100644
index 000000000..604c673c9
--- /dev/null
+++ b/src/BeamAdapter/component/controller/BeamAdapterActionController.cpp
@@ -0,0 +1,40 @@
+/******************************************************************************
+* BeamAdapter plugin *
+* (c) 2006 Inria, University of Lille, CNRS *
+* *
+* This program is free software; you can redistribute it and/or modify it *
+* under the terms of the GNU Lesser General Public License as published by *
+* the Free Software Foundation; either version 2.1 of the License, or (at *
+* your option) any later version. *
+* *
+* This program is distributed in the hope that it will be useful, but WITHOUT *
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
+* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License *
+* for more details. *
+* *
+* You should have received a copy of the GNU Lesser General Public License *
+* along with this program. If not, see . *
+*******************************************************************************
+* Authors: see Authors.md *
+* *
+* Contact information: contact@sofa-framework.org *
+******************************************************************************/
+#define SOFA_PLUGIN_BEAMADAPTER_ACTIONCONTROLLER_CPP
+
+#include
+#include
+#include
+
+#include
+#include
+
+namespace sofa::component::controller
+{
+
+const static int BeamAdapterActionControllerClass = core::RegisterObject("BeamAdapterActionController")
+ .add< BeamAdapterActionController >()
+ ;
+
+template class SOFA_BEAMADAPTER_API BeamAdapterActionController;
+
+} // namespace sofa::component::controller
diff --git a/src/BeamAdapter/component/controller/BeamAdapterActionController.h b/src/BeamAdapter/component/controller/BeamAdapterActionController.h
new file mode 100644
index 000000000..115f37449
--- /dev/null
+++ b/src/BeamAdapter/component/controller/BeamAdapterActionController.h
@@ -0,0 +1,86 @@
+/******************************************************************************
+* BeamAdapter plugin *
+* (c) 2006 Inria, University of Lille, CNRS *
+* *
+* This program is free software; you can redistribute it and/or modify it *
+* under the terms of the GNU Lesser General Public License as published by *
+* the Free Software Foundation; either version 2.1 of the License, or (at *
+* your option) any later version. *
+* *
+* This program is distributed in the hope that it will be useful, but WITHOUT *
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
+* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License *
+* for more details. *
+* *
+* You should have received a copy of the GNU Lesser General Public License *
+* along with this program. If not, see . *
+*******************************************************************************
+* Authors: see Authors.md *
+* *
+* Contact information: contact@sofa-framework.org *
+******************************************************************************/
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace sofa::component::controller
+{
+
+/***
+* This class is a SOFA component inheriting from MechanicalStateController.
+* It can be used to script the InterventionalRadiologyController using @sa BeamActions
+* If @sa d_writeMode mode is on, each keyboard interaction used to control the Beam will be exported with key times.
+* Otherwise, it will load a list of @sa BeamActions with their corresponding key times to replay a navigation.
+*/
+template
+class BeamAdapterActionController : public MechanicalStateController
+{
+public:
+ SOFA_CLASS(SOFA_TEMPLATE(BeamAdapterActionController, DataTypes), SOFA_TEMPLATE(MechanicalStateController, DataTypes));
+
+ using BeamAdapterAction = sofa::beamadapter::BeamAdapterAction;
+ using interventionCtrl = InterventionalRadiologyController;
+ using Real = typename DataTypes::Real;
+
+ BeamAdapterActionController();
+ ~BeamAdapterActionController() override = default;
+
+ /// Method to init the component. Will search for a InterventionalRadiologyController using link @sa l_interventionController
+ void init() override;
+
+ /// Method called at each timestep. Will check if an action has to be read or write
+ void onBeginAnimationStep(const double dt) override;
+
+ /// Method to control the Beam using keyboard and save the actions in @sa d_actions
+ void onKeyPressedEvent(core::objectmodel::KeypressedEvent* kev) override;
+
+ /// Unused metho for mouse event
+ void onMouseEvent(core::objectmodel::MouseEvent* ev) override { SOFA_UNUSED(ev);}
+
+ Data d_writeMode; ///< If true, will accumulate actions in @sa d_actions for export. Press key E for export.
+ Data > d_actions; ///< List of actions to import or export.
+ Data > d_actionString; ///< List of actions to import or export as string.
+ Data > d_timeSteps; ///< List of key times corresponding to BeamActions in @sa d_actions or @sa d_actionString
+
+ /// Link to the InterventionalRadiologyController, controlling the Beam, to script.
+ SingleLink, InterventionalRadiologyController, BaseLink::FLAG_STOREPATH | BaseLink::FLAG_STRONGLINK> l_interventionController;
+
+private:
+ int m_readStep = 0; ///< counter to the current action to read in @sa d_actions
+ BeamAdapterAction m_currAction = BeamAdapterAction::NO_ACTION; ///< Current action imported or to export
+ BeamAdapterAction m_lastAction = BeamAdapterAction::NO_ACTION; ///< Previous action imported or to export
+
+ bool m_exportActions = false; ///< Bool to dump actions, will be set to true if key 'E' is pressed
+};
+
+#if !defined(SOFA_PLUGIN_BEAMADAPTER_ACTIONCONTROLLER_CPP)
+extern template class SOFA_BEAMADAPTER_API BeamAdapterActionController;
+#endif
+
+} /// namespace sofa::component::controller
diff --git a/src/BeamAdapter/component/controller/BeamAdapterActionController.inl b/src/BeamAdapter/component/controller/BeamAdapterActionController.inl
new file mode 100644
index 000000000..312550be8
--- /dev/null
+++ b/src/BeamAdapter/component/controller/BeamAdapterActionController.inl
@@ -0,0 +1,177 @@
+/******************************************************************************
+* BeamAdapter plugin *
+* (c) 2006 Inria, University of Lille, CNRS *
+* *
+* This program is free software; you can redistribute it and/or modify it *
+* under the terms of the GNU Lesser General Public License as published by *
+* the Free Software Foundation; either version 2.1 of the License, or (at *
+* your option) any later version. *
+* *
+* This program is distributed in the hope that it will be useful, but WITHOUT *
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
+* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License *
+* for more details. *
+* *
+* You should have received a copy of the GNU Lesser General Public License *
+* along with this program. If not, see . *
+*******************************************************************************
+* Authors: see Authors.md *
+* *
+* Contact information: contact@sofa-framework.org *
+******************************************************************************/
+#pragma once
+#include
+#include
+#include
+
+namespace sofa::component::controller
+{
+
+using namespace sofa::beamadapter;
+
+template
+BeamAdapterActionController::BeamAdapterActionController()
+ : d_writeMode(initData(&d_writeMode, false, "writeMode", "If true, will accumulate actions from keyboard and dump the actions and times when key 'E' is pressed."))
+ , d_actions(initData(&d_actions, "actions", "List of actions to script the BeamAdapter"))
+ , d_actionString(initData(&d_actionString, "actionString", "List of actions as string to script the BeamAdapter"))
+ , d_timeSteps(initData(&d_timeSteps, "timeSteps", "List of key times corresponding to the actions"))
+ , l_interventionController(initLink("interventionController", "Path to the InterventionalRadiologyController component on scene"))
+{
+
+}
+
+template
+void BeamAdapterActionController::init()
+{
+ this->d_componentState.setValue(sofa::core::objectmodel::ComponentState::Loading);
+ if (!l_interventionController.get())
+ {
+ msg_error() << "No l_interventionController given. Component will be set to Invalid.";
+ this->d_componentState.setValue(sofa::core::objectmodel::ComponentState::Invalid);
+ }
+
+ // the controller must listen to the event (in particular BeginAnimationStep event)
+ this->f_listening.setValue(true);
+
+ this->d_componentState.setValue(sofa::core::objectmodel::ComponentState::Valid);
+}
+
+
+template
+void BeamAdapterActionController::onKeyPressedEvent(core::objectmodel::KeypressedEvent* kev)
+{
+ if (!d_writeMode.getValue())
+ return;
+
+ /// Control keys for interventonal Radiology simulations:
+ switch (kev->getKey())
+ {
+ case 'E':
+ m_currAction = BeamAdapterAction::NO_ACTION;
+ m_exportActions = !m_exportActions;
+ break;
+ case 'D':
+ m_currAction = BeamAdapterAction::DROP_TOOL;
+ break;
+ case '2':
+ m_currAction = BeamAdapterAction::USE_TOOL_2;
+ break;
+ case '1':
+ m_currAction = BeamAdapterAction::USE_TOOL_1;
+ break;
+ case '0':
+ m_currAction = BeamAdapterAction::USE_TOOL_0;
+ break;
+ case 20: // droite = 20
+ if (m_currAction == BeamAdapterAction::SPIN_RIGHT)
+ m_currAction = BeamAdapterAction::NO_ACTION;
+ else
+ m_currAction = BeamAdapterAction::SPIN_RIGHT;
+
+ break;
+ case 18: // gauche = 18
+ if (m_currAction == BeamAdapterAction::SPIN_LEFT)
+ m_currAction = BeamAdapterAction::NO_ACTION;
+ else
+ m_currAction = BeamAdapterAction::SPIN_LEFT;
+
+ break;
+ case 19: // fleche haut = 19
+ if (m_currAction == BeamAdapterAction::MOVE_FORWARD)
+ m_currAction = BeamAdapterAction::NO_ACTION;
+ else
+ m_currAction = BeamAdapterAction::MOVE_FORWARD;
+
+ break;
+ case 21: // bas = 21
+ if (m_currAction == BeamAdapterAction::MOVE_BACKWARD)
+ m_currAction = BeamAdapterAction::NO_ACTION;
+ else
+ m_currAction = BeamAdapterAction::MOVE_BACKWARD;
+
+ break;
+ default:
+ m_currAction = BeamAdapterAction::NO_ACTION;
+ break;
+ }
+}
+
+
+template
+void BeamAdapterActionController::onBeginAnimationStep(const double /*dt*/)
+{
+ const auto currentTime = this->getContext()->getTime();
+ if (d_writeMode.getValue())
+ {
+ interventionCtrl* ctrl = l_interventionController.get();
+ ctrl->applyAction(m_currAction);
+
+ if (m_lastAction != m_currAction)
+ {
+ auto times = sofa::helper::WriteAccessor(d_timeSteps);
+ auto actions = sofa::helper::WriteAccessor(d_actions);
+ times.push_back(currentTime);
+ actions.push_back(int(m_currAction));
+
+ if (m_exportActions)
+ {
+ std::cout << "timeSteps='" << times.wref() << "'" << std::endl;
+ std::cout << "actions='" << actions.wref() << "'" << std::endl;
+ }
+
+ m_lastAction = m_currAction;
+ }
+
+ if (m_currAction >= BeamAdapterAction::SWITCH_NEXT_TOOL) // action regarding tool needs only to be triggered once
+ {
+ m_currAction = BeamAdapterAction::NO_ACTION;
+ }
+ }
+ else
+ {
+ const type::vector& times = d_timeSteps.getValue();
+ if (!times.empty())
+ {
+ if (m_readStep < times.size())
+ {
+ Real time = times[m_readStep];
+ if (currentTime >= time) // check if another key time has been reached and change action
+ {
+ m_currAction = BeamAdapterAction(d_actions.getValue()[m_readStep]);
+ m_readStep++;
+ }
+ }
+
+ interventionCtrl* ctrl = l_interventionController.get();
+ ctrl->applyAction(m_currAction);
+
+ if (m_currAction >= BeamAdapterAction::SWITCH_NEXT_TOOL) // action regarding tool needs only to be triggered once
+ {
+ m_currAction = BeamAdapterAction::NO_ACTION;
+ }
+ }
+ }
+}
+
+
+} // namespace sofa::component::controller
diff --git a/src/BeamAdapter/component/controller/InterventionalRadiologyController.h b/src/BeamAdapter/component/controller/InterventionalRadiologyController.h
index c3c824edc..0eeabc68d 100644
--- a/src/BeamAdapter/component/controller/InterventionalRadiologyController.h
+++ b/src/BeamAdapter/component/controller/InterventionalRadiologyController.h
@@ -41,6 +41,7 @@
#include
#include
+#include
#include
#include
#include
@@ -108,8 +109,11 @@ class InterventionalRadiologyController : public MechanicalStateController >& get_id_instrument_curvAbs_table()const;
int getTotalNbEdges()const;
+ void applyAction(sofa::beamadapter::BeamAdapterAction action);
+
/// Getter to the tools curviline abscisses sorted @sa m_nodeCurvAbs at the current timestep.
[[nodiscard]] const type::vector& getCurrentCurvAbscisses() const { return m_nodeCurvAbs; }
+
public:
using Inherit1::f_printLog;
diff --git a/src/BeamAdapter/component/controller/InterventionalRadiologyController.inl b/src/BeamAdapter/component/controller/InterventionalRadiologyController.inl
index 31f36b58b..a38a3c144 100644
--- a/src/BeamAdapter/component/controller/InterventionalRadiologyController.inl
+++ b/src/BeamAdapter/component/controller/InterventionalRadiologyController.inl
@@ -55,6 +55,7 @@ using core::objectmodel::BaseContext;
using helper::WriteAccessor;
using core::objectmodel::KeypressedEvent;
using core::objectmodel::MouseEvent;
+using namespace sofa::beamadapter;
template
@@ -242,72 +243,29 @@ void InterventionalRadiologyController::onKeyPressedEvent(KeypressedE
switch(kev->getKey())
{
case 'D':
- m_dropCall = true;
+ applyAction(BeamAdapterAction::DROP_TOOL);
break;
-
case '2':
- {
- if (2 >= (int)m_instrumentsList.size() && f_printLog.getValue() )
- msg_warning()<<"Controlled Instument num 2 do not exist (size ="<< m_instrumentsList.size() <<") do not change the instrument id";
- else
- d_controlledInstrument.setValue(2);
- }
+ applyAction(BeamAdapterAction::USE_TOOL_2);
break;
-
case '1':
- {
- if (1 >= (int)m_instrumentsList.size() && f_printLog.getValue() )
- msg_warning()<<"Controlled Instument num 1 do not exist (size ="<< m_instrumentsList.size() <<") do not change the instrument id";
- else
- d_controlledInstrument.setValue(1);
- }
+ applyAction(BeamAdapterAction::USE_TOOL_1);
break;
-
case '0':
- d_controlledInstrument.setValue(0);
+ applyAction(BeamAdapterAction::USE_TOOL_0);
break;
-
case 20: // droite = 20
- {
- auto rotInstrument = sofa::helper::getWriteOnlyAccessor(d_rotationInstrument);
- int id = d_controlledInstrument.getValue();
- rotInstrument[id] += d_angularStep.getValue();
- }
+ applyAction(BeamAdapterAction::SPIN_RIGHT);
break;
case 18: // gauche = 18
- {
- int id = d_controlledInstrument.getValue();
- auto rotInstrument = sofa::helper::getWriteOnlyAccessor(d_rotationInstrument);
- rotInstrument[id] -= d_angularStep.getValue();
- }
+ applyAction(BeamAdapterAction::SPIN_LEFT);
break;
-
case 19: // fleche haut = 19
- {
- int id = d_controlledInstrument.getValue();
- auto xInstrTip = sofa::helper::getWriteOnlyAccessor(d_xTip);
- if (id >= (int)xInstrTip.size())
- {
- msg_warning()<<"Controlled Instument num "<= (int)xInstrTip.size())
- {
- msg_warning()<<"Controlled Instument num "<::onKeyPressedEvent(KeypressedE
}
}
break;
-
case '/':
{
if(m_FF)
@@ -397,6 +354,89 @@ void InterventionalRadiologyController::onBeginAnimationStep(const do
}
+template
+void InterventionalRadiologyController::applyAction(sofa::beamadapter::BeamAdapterAction action)
+{
+ int id = d_controlledInstrument.getValue();
+ if (id >= m_instrumentsList.size())
+ {
+ msg_warning() << "Controlled Instument num " << id << " does not exist (size =" << m_instrumentsList.size() << ").";
+ return;
+ }
+
+ switch (action)
+ {
+ case BeamAdapterAction::NO_ACTION:
+ break;
+ case BeamAdapterAction::MOVE_FORWARD:
+ {
+ auto xInstrTip = sofa::helper::getWriteOnlyAccessor(d_xTip);
+ xInstrTip[id] += d_step.getValue();
+ break;
+ }
+ case BeamAdapterAction::MOVE_BACKWARD:
+ {
+ auto xInstrTip = sofa::helper::getWriteOnlyAccessor(d_xTip);
+ xInstrTip[id] -= d_step.getValue();
+ break;
+ }
+ case BeamAdapterAction::SPIN_RIGHT:
+ {
+ auto rotInstrument = sofa::helper::getWriteOnlyAccessor(d_rotationInstrument);
+ rotInstrument[id] += d_angularStep.getValue();
+ break;
+ }
+ case BeamAdapterAction::SPIN_LEFT:
+ {
+ auto rotInstrument = sofa::helper::getWriteOnlyAccessor(d_rotationInstrument);
+ rotInstrument[id] -= d_angularStep.getValue();
+ break;
+ }
+ case BeamAdapterAction::SWITCH_NEXT_TOOL:
+ {
+ if (id + 1 >= m_instrumentsList.size())
+ msg_warning() << "Switching to next tool is not possible, no more instrument in list.";
+ else
+ d_controlledInstrument.setValue(id + 1);
+ break;
+ }
+ case BeamAdapterAction::SWITCH_PREVIOUS_TOOL:
+ {
+ if (id == 0)
+ msg_warning() << "Switching to previous tool is not possible, already controlling first instrument.";
+ else
+ d_controlledInstrument.setValue(id - 1);
+ break;
+ }
+ case BeamAdapterAction::USE_TOOL_0:
+ {
+ d_controlledInstrument.setValue(0);
+ break;
+ }
+ case BeamAdapterAction::USE_TOOL_1:
+ {
+ if (1 >= m_instrumentsList.size())
+ msg_warning() << "Controlled Instument num 1 do not exist (size =" << m_instrumentsList.size() << ") do not change the instrument id";
+ else
+ d_controlledInstrument.setValue(1);
+ break;
+ }
+ case BeamAdapterAction::USE_TOOL_2:
+ {
+ if (2 >= m_instrumentsList.size())
+ msg_warning() << "Controlled Instument num 2 do not exist (size =" << m_instrumentsList.size() << ") do not change the instrument id";
+ else
+ d_controlledInstrument.setValue(2);
+ break;
+ }
+ case BeamAdapterAction::DROP_TOOL:
+ {
+ m_dropCall = true;
+ }
+ }
+}
+
+
template
void InterventionalRadiologyController::processDrop(unsigned int &previousNumControlledNodes,
unsigned int &segRemove)
diff --git a/src/BeamAdapter/utils/BeamActions.h b/src/BeamAdapter/utils/BeamActions.h
new file mode 100644
index 000000000..ea91418de
--- /dev/null
+++ b/src/BeamAdapter/utils/BeamActions.h
@@ -0,0 +1,68 @@
+/******************************************************************************
+* BeamAdapter plugin *
+* (c) 2006 Inria, University of Lille, CNRS *
+* *
+* This program is free software; you can redistribute it and/or modify it *
+* under the terms of the GNU Lesser General Public License as published by *
+* the Free Software Foundation; either version 2.1 of the License, or (at *
+* your option) any later version. *
+* *
+* This program is distributed in the hope that it will be useful, but WITHOUT *
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
+* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License *
+* for more details. *
+* *
+* You should have received a copy of the GNU Lesser General Public License *
+* along with this program. If not, see . *
+*******************************************************************************
+* Authors: see Authors.md *
+* *
+* Contact information: contact@sofa-framework.org *
+******************************************************************************/
+#pragma once
+
+namespace sofa::beamadapter
+{
+
+ /// \brief Enum class listing all possible actions during Beam navigation
+ enum class BeamAdapterAction
+ {
+ NO_ACTION = 0,
+ MOVE_FORWARD,
+ MOVE_BACKWARD,
+ SPIN_RIGHT,
+ SPIN_LEFT,
+ SWITCH_NEXT_TOOL,
+ SWITCH_PREVIOUS_TOOL,
+ DROP_TOOL,
+ USE_TOOL_0,
+ USE_TOOL_1,
+ USE_TOOL_2,
+ };
+
+ /// \brief map of action as string keyword instead of int for better clarity in scene scripting
+ const static std::map beamActionNames = {
+ {"stop", BeamAdapterAction::NO_ACTION},
+ {"fwd", BeamAdapterAction::MOVE_FORWARD},
+ {"bwd", BeamAdapterAction::MOVE_BACKWARD},
+ {"right", BeamAdapterAction::SPIN_RIGHT},
+ {"nextT", BeamAdapterAction::SWITCH_NEXT_TOOL},
+ {"prevT", BeamAdapterAction::SWITCH_PREVIOUS_TOOL},
+ {"dropT", BeamAdapterAction::DROP_TOOL},
+ {"tool0", BeamAdapterAction::USE_TOOL_0},
+ {"tool1", BeamAdapterAction::USE_TOOL_1},
+ {"tool2", BeamAdapterAction::USE_TOOL_2}
+ };
+
+ /// static method to convert an action as string into enum class using @sa beamActionNames
+ static BeamAdapterAction convertBeamAdapterAction(const std::string& sAction)
+ {
+ auto bAction = beamActionNames.find(sAction);
+
+ if (bAction != beamActionNames.end())
+ return bAction->second;
+ else
+ return BeamAdapterAction::NO_ACTION;
+ }
+
+} // namespace sofa::beamAdapter