From 0f7fcf6feb1e5365b4d571c7d471146ed3cfb9c2 Mon Sep 17 00:00:00 2001 From: Michael Colonel Date: Mon, 21 Mar 2022 15:37:06 +0300 Subject: [PATCH 1/7] ENH: Initial beam arc sequence --- Beams/Logic/CMakeLists.txt | 4 + Beams/Logic/vtkSlicerBeamsModuleLogic.cxx | 177 ++++++++++++++++++ Beams/Logic/vtkSlicerBeamsModuleLogic.h | 14 ++ Beams/MRML/vtkMRMLRTBeamNode.cxx | 6 - Beams/MRML/vtkMRMLRTBeamNode.h | 2 - .../UI/qSlicerExternalBeamPlanningModule.ui | 105 +++++++++-- .../Widgets/qSlicerDoseEngineLogic.cxx | 61 ++++++ .../Widgets/qSlicerDoseEngineLogic.h | 7 + ...SlicerExternalBeamPlanningModuleWidget.cxx | 70 ++++++- .../qSlicerExternalBeamPlanningModuleWidget.h | 3 + 10 files changed, 420 insertions(+), 29 deletions(-) diff --git a/Beams/Logic/CMakeLists.txt b/Beams/Logic/CMakeLists.txt index 47d6fecfa..809339045 100644 --- a/Beams/Logic/CMakeLists.txt +++ b/Beams/Logic/CMakeLists.txt @@ -8,6 +8,8 @@ set(${KIT}_INCLUDE_DIRECTORIES ${SlicerRtCommon_INCLUDE_DIRS} ${vtkSlicerBeamsModuleMRML_INCLUDE_DIRS} ${vtkSlicerSubjectHierarchyModuleLogic_INCLUDE_DIRS} + ${vtkSlicerSequencesModuleMRML_INCLUDE_DIRS} + ${vtkSlicerSequencesModuleLogic_INCLUDE_DIRS} ) set(${KIT}_SRCS @@ -26,6 +28,8 @@ set(${KIT}_TARGET_LIBRARIES vtkSlicerRtCommon vtkSlicerMarkupsModuleMRML vtkSlicerSubjectHierarchyModuleLogic + vtkSlicerSequencesModuleMRML + vtkSlicerSequencesModuleLogic ${ITK_LIBRARIES} ${VTK_LIBRARIES} ) diff --git a/Beams/Logic/vtkSlicerBeamsModuleLogic.cxx b/Beams/Logic/vtkSlicerBeamsModuleLogic.cxx index 752e1e4be..308cc6554 100644 --- a/Beams/Logic/vtkSlicerBeamsModuleLogic.cxx +++ b/Beams/Logic/vtkSlicerBeamsModuleLogic.cxx @@ -27,7 +27,20 @@ #include "vtkMRMLRTPlanNode.h" #include "vtkMRMLRTBeamNode.h" +#include + +// SubjectHierarchy includes +#include "vtkMRMLSubjectHierarchyConstants.h" +#include "vtkMRMLSubjectHierarchyNode.h" +#include "vtkSlicerSubjectHierarchyModuleLogic.h" + +// Sequences inludes +#include +#include + // MRML includes +#include +#include #include #include #include @@ -299,3 +312,167 @@ void vtkSlicerBeamsModuleLogic::ProcessMRMLNodesEvents(vtkObject* caller, unsign } } } + +//---------------------------------------------------------------------------- +bool vtkSlicerBeamsModuleLogic::CreateArcBeamDynamicSequence( + double initialAngle, double finalAngle, bool direction, + vtkMRMLRTPlanNode* planNode, vtkMRMLSequenceBrowserNode* beamSequenceBrowserNode, + vtkMRMLSequenceNode* beamSequenceNode, vtkMRMLSequenceNode* transformSequenceNode) +{ + std::vector angles; + if (vtkSlicerRtCommon::AreEqualWithTolerance(finalAngle, initialAngle)) + { + return false; + } + + if (finalAngle < 0. && finalAngle > 360. && initialAngle < 0. && initialAngle > 360.) + { + return false; + } + + if (!direction && initialAngle < finalAngle) // CW, ini < fin + { + for (double angle = initialAngle; angle <= finalAngle; angle += 1.) + { + angles.push_back(angle); + } + } + else if (!direction && initialAngle > finalAngle) // CW, ini > fin + { + for (double angle = initialAngle; angle <= 360.; angle += 1.) + { + angles.push_back(angle); + } + for (double angle = 1.; angle <= finalAngle; angle += 1.) + { + angles.push_back(angle); + } + } + else if (direction && initialAngle < finalAngle) // CCW, ini < fin + { + for (double angle = initialAngle; angle >= 0.0; angle -= 1.) + { + angles.push_back(angle); + } + for (double angle = 359.; angle >= finalAngle; angle -= 1.) + { + angles.push_back(angle); + } + } + else if (direction && initialAngle > finalAngle) // CCW, ini > fin + { + for (double angle = initialAngle; angle >= finalAngle; angle -= 1.) + { + angles.push_back(angle); + } + } + vtkMRMLScene* scene = planNode->GetScene(); + + vtkMRMLSubjectHierarchyNode* shNode = vtkMRMLSubjectHierarchyNode::GetSubjectHierarchyNode(scene); + if (!shNode) + { + vtkErrorMacro("CreateArcBeamDynamicSequence: Failed to access subject hierarchy node"); + return false; + } + vtkSmartPointer beamModelHierarchyRootNode; + + const char* beamName = "Beam [Arc]"; + // Create sequence node for RTBeam, transformation, table + + // beamSequenceNode; + beamSequenceNode->SetName(beamName); + beamSequenceNode->SetIndexName("Control point"); + beamSequenceNode->SetIndexUnit("index"); + beamSequenceNode->SetIndexType(vtkMRMLSequenceNode::NumericIndex); + + // transformSequenceNode; + std::string name = std::string(beamName) + vtkMRMLRTBeamNode::BEAM_TRANSFORM_NODE_NAME_POSTFIX; + transformSequenceNode->SetName(name.c_str()); + transformSequenceNode->SetIndexName("Control point"); + transformSequenceNode->SetIndexUnit("index"); + transformSequenceNode->SetIndexType(vtkMRMLSequenceNode::NumericIndex); + + // beamSequenceBrowserNode; + name = std::string(beamName) + "_SequenceBrowser"; + beamSequenceBrowserNode->SetName(name.c_str()); + + // Add sequence nodes to the scene + scene->AddNode(beamSequenceNode); + scene->AddNode(transformSequenceNode); + scene->AddNode(beamSequenceBrowserNode); + + for ( auto it = angles.begin(); it != angles.end(); ++it) + { + unsigned int controlPointIndex = it - angles.begin(); + // Create the beam node for each control point + vtkSmartPointer beamNode = vtkSmartPointer::New(); + + std::ostringstream nameStream; + nameStream << beamName << " [Dynamic] : CP" << controlPointIndex; + + std::string newBeamName = nameStream.str(); + beamNode->SetName(newBeamName.c_str()); + + // Set beam geometry parameters from DICOM + double jawPositions[2][2] = {{-40.0, 40.0},{-40.0, 40.0}}; + beamNode->SetX1Jaw(jawPositions[0][0]); + beamNode->SetX2Jaw(jawPositions[0][1]); + beamNode->SetY1Jaw(jawPositions[1][0]); + beamNode->SetY2Jaw(jawPositions[1][1]); + + beamNode->SetGantryAngle(*it); + + // SAD for RTPlan, source to beam limiting devices (Jaws, MLC) + if (beamNode) + { +// beamNode->SetSAD(rtReader->GetBeamSourceAxisDistance(dicomBeamNumber)); +// beamNode->SetSourceToJawsDistanceX(rtReader->GetBeamSourceToJawsDistanceX(dicomBeamNumber)); +// beamNode->SetSourceToJawsDistanceY(rtReader->GetBeamSourceToJawsDistanceY(dicomBeamNumber)); +// beamNode->SetSourceToMultiLeafCollimatorDistance(rtReader->GetBeamSourceToMultiLeafCollimatorDistance(dicomBeamNumber)); + } + + // Add beam to beam sequence node + if (beamNode) + { + beamSequenceNode->SetDataNodeAtValue( beamNode, std::to_string(controlPointIndex)); + } + + // Get or create RTBeam transformation from RTPlan without adding beam to the plan + // Add beam transformation to transform sequence + vtkMRMLLinearTransformNode* transformNode = beamNode->CreateBeamTransformNode(scene); + if (transformNode) + { + double isocenter[3] = { 0., 0., 0.}; + planNode->GetIsocenterPosition(isocenter); + { + // Update beam transform without translation to isocenter + this->UpdateTransformForBeam( beamSequenceNode->GetSequenceScene(), beamNode, transformNode, isocenter); + + vtkTransform* transform = vtkTransform::SafeDownCast(transformNode->GetTransformToParent()); + if (isocenter) + { + // Actual translation to isocenter + transform->Translate( isocenter[0], isocenter[1], isocenter[2]); + transformNode->Modified(); + } + + transformSequenceNode->SetDataNodeAtValue( transformNode, std::to_string(controlPointIndex)); + } + } + } // end of a control point + + // Synchronize beam sequence, beam transform sequence, table sequence nodes + beamSequenceBrowserNode->SetAndObserveMasterSequenceNodeID(beamSequenceNode->GetID()); + beamSequenceBrowserNode->AddSynchronizedSequenceNode(transformSequenceNode); + + // Get proxy beam node + vtkMRMLNode* node = beamSequenceBrowserNode->GetProxyNode(beamSequenceNode); + if (vtkMRMLRTBeamNode::SafeDownCast(node)) + { + return true; + } + else + { + return false; + } +} diff --git a/Beams/Logic/vtkSlicerBeamsModuleLogic.h b/Beams/Logic/vtkSlicerBeamsModuleLogic.h index 93e664c9b..40ffce015 100644 --- a/Beams/Logic/vtkSlicerBeamsModuleLogic.h +++ b/Beams/Logic/vtkSlicerBeamsModuleLogic.h @@ -35,6 +35,8 @@ #include "vtkMRMLRTBeamNode.h" class vtkSlicerMLCPositionLogic; +class vtkMRMLSequenceBrowserNode; +class vtkMRMLSequenceNode; /// \ingroup SlicerRt_QtModules_Beams class VTK_SLICER_BEAMS_LOGIC_EXPORT vtkSlicerBeamsModuleLogic : @@ -60,6 +62,18 @@ class VTK_SLICER_BEAMS_LOGIC_EXPORT vtkSlicerBeamsModuleLogic : void UpdateTransformForBeam( vtkMRMLScene* beamSequenceScene, vtkMRMLRTBeamNode* beamNode, vtkMRMLLinearTransformNode* beamTransformNode, double isocenter[3]); + /// Create arc delivery beam sequence + /// @param initialAngle - initial angle in degrees + /// @param finalAngle - final angle in degrees + /// @param direction - 0 - clockwise, 1 - counter-clockwise + /// @param planNode - input plan node, which contains reference volume node and isocenter position + /// @param sequenceBrowserNode - output sequence browser node + /// @param sequenceBeamNode - output beam node + /// @param sequenceTransformNode - output transform node + bool CreateArcBeamDynamicSequence( double initialAngle, double finalAngle, bool direction, + vtkMRMLRTPlanNode* planNode, vtkMRMLSequenceBrowserNode* beamSequenceBrowserNode, + vtkMRMLSequenceNode* beamSequenceNode, vtkMRMLSequenceNode* transformSequenceNode); + protected: vtkSlicerBeamsModuleLogic(); ~vtkSlicerBeamsModuleLogic() override; diff --git a/Beams/MRML/vtkMRMLRTBeamNode.cxx b/Beams/MRML/vtkMRMLRTBeamNode.cxx index 37d39e02d..da44dc1ea 100644 --- a/Beams/MRML/vtkMRMLRTBeamNode.cxx +++ b/Beams/MRML/vtkMRMLRTBeamNode.cxx @@ -916,9 +916,3 @@ void vtkMRMLRTBeamNode::CreateMLCPointsFromSectionBorder( double jawBegin, } side12.push_back(side2.back()); } - -//---------------------------------------------------------------------------- -bool vtkMRMLRTBeamNode::AreEqual( double v1, double v2) -{ - return vtkSlicerRtCommon::AreEqualWithTolerance( v1, v2); -} diff --git a/Beams/MRML/vtkMRMLRTBeamNode.h b/Beams/MRML/vtkMRMLRTBeamNode.h index 2efea701d..4f66dfe4d 100644 --- a/Beams/MRML/vtkMRMLRTBeamNode.h +++ b/Beams/MRML/vtkMRMLRTBeamNode.h @@ -261,8 +261,6 @@ class VTK_SLICER_BEAMS_MODULE_MRML_EXPORT vtkMRMLRTBeamNode : public vtkMRMLMode void CreateMLCPointsFromSectionBorder( double jawBegin, double jawEnd, bool mlcType, const MLCSectionVector::value_type& sectionBorder, MLCVisiblePointVector& side12); - - static bool AreEqual( double v1, double v2); }; #endif // __vtkMRMLRTBeamNode_h diff --git a/ExternalBeamPlanning/Resources/UI/qSlicerExternalBeamPlanningModule.ui b/ExternalBeamPlanning/Resources/UI/qSlicerExternalBeamPlanningModule.ui index 8146e4c9b..0c6afee6c 100644 --- a/ExternalBeamPlanning/Resources/UI/qSlicerExternalBeamPlanningModule.ui +++ b/ExternalBeamPlanning/Resources/UI/qSlicerExternalBeamPlanningModule.ui @@ -7,7 +7,7 @@ 0 0 482 - 586 + 632 @@ -185,10 +185,10 @@ 0 - + true - + false @@ -490,16 +490,6 @@ 4 - - - - 0 - 0 - - - - - @@ -550,6 +540,92 @@ + + + + + 0 + 0 + + + + + + + + Arc beam sequence + + + true + + + false + + + + + + + + Final angle + + + + + + + Initial angle + + + + + + + 359.000000000000000 + + + + + + + CCW + + + ButtonGroup_ArcBeamRotationDirection + + + + + + + 2 + + + 359.000000000000000 + + + 180.000000000000000 + + + + + + + CW + + + true + + + ButtonGroup_ArcBeamRotationDirection + + + + + + + + @@ -737,4 +813,7 @@ + + + diff --git a/ExternalBeamPlanning/Widgets/qSlicerDoseEngineLogic.cxx b/ExternalBeamPlanning/Widgets/qSlicerDoseEngineLogic.cxx index 174ce598e..e075380f3 100644 --- a/ExternalBeamPlanning/Widgets/qSlicerDoseEngineLogic.cxx +++ b/ExternalBeamPlanning/Widgets/qSlicerDoseEngineLogic.cxx @@ -33,6 +33,7 @@ #include "vtkMRMLDoseAccumulationNode.h" #include "vtkSlicerDoseAccumulationModuleLogic.h" #include "vtkSlicerIsodoseModuleLogic.h" +#include // MRML includes #include @@ -44,10 +45,19 @@ #include #include +#include +#include +#include + // Slicer includes #include "qSlicerCoreApplication.h" #include "vtkSlicerApplicationLogic.h" +#include +#include +#include +#include + // VTK includes #include @@ -479,3 +489,54 @@ vtkMRMLRTBeamNode* qSlicerDoseEngineLogic::createBeamInPlan(vtkMRMLRTPlanNode* p return beamNode; } + +//--------------------------------------------------------------------------- +vtkMRMLRTBeamNode* qSlicerDoseEngineLogic::createArcBeamInPlan( vtkMRMLRTPlanNode* planNode, + double initialAngle, double finalAngle, bool rotationDirection) +{ + // Set beams logic + vtkSlicerBeamsModuleLogic* beamsLogic = nullptr; + qSlicerAbstractCoreModule* beamsModule = qSlicerCoreApplication::application()->moduleManager()->module("Beams"); + if (beamsModule) + { + beamsLogic = vtkSlicerBeamsModuleLogic::SafeDownCast(beamsModule->logic()); + } + else + { + qCritical() << Q_FUNC_INFO << ": Beams module is not found"; + } + + if (!planNode || !planNode->GetScene()) + { + qCritical() << Q_FUNC_INFO << ": Invalid plan node!"; + return nullptr; + } + + vtkNew beamSequenceNode; + vtkNew transformSequenceNode; + vtkNew beamSequenceBrowserNode; + + if (beamsLogic->CreateArcBeamDynamicSequence( initialAngle, finalAngle, rotationDirection, + planNode, beamSequenceBrowserNode, beamSequenceNode, transformSequenceNode)) + { +// vtkMRMLNode* node = beamSequenceBrowserNode->GetProxyNode(beamSequenceNode); +// return vtkMRMLRTBeamNode::SafeDownCast(node); + + // Get proxy beam node + vtkMRMLNode* node = beamSequenceBrowserNode->GetProxyNode(beamSequenceNode); + vtkMRMLRTBeamNode* proxyBeamNode = vtkMRMLRTBeamNode::SafeDownCast(node); + + // Get proxy transform node + node = beamSequenceBrowserNode->GetProxyNode(transformSequenceNode); + vtkMRMLLinearTransformNode* proxyTransformNode = vtkMRMLLinearTransformNode::SafeDownCast(node); + + if (proxyBeamNode && proxyTransformNode) + { + // Add proxy beam node to plan (create Subject Hierarchy for beam node) + planNode->AddProxyBeam(proxyBeamNode); + proxyBeamNode->SetAndObserveTransformNodeID(proxyTransformNode->GetID()); + return proxyBeamNode; + } + } + return nullptr; +} diff --git a/ExternalBeamPlanning/Widgets/qSlicerDoseEngineLogic.h b/ExternalBeamPlanning/Widgets/qSlicerDoseEngineLogic.h index bc8d171d8..8f78c4433 100644 --- a/ExternalBeamPlanning/Widgets/qSlicerDoseEngineLogic.h +++ b/ExternalBeamPlanning/Widgets/qSlicerDoseEngineLogic.h @@ -72,6 +72,13 @@ class Q_SLICER_MODULE_EXTERNALBEAMPLANNING_WIDGETS_EXPORT qSlicerDoseEngineLogic /// Create a beam for a plan (with beam parameters defined by the dose engine of the plan) /// @param planNode - node of the current plan Q_INVOKABLE vtkMRMLRTBeamNode* createBeamInPlan(vtkMRMLRTPlanNode* planNode); + /// Create a beam for a RT plan (with beam parameters defined by the dose engine of the plan) + /// @param planNode - node of the current plan + /// @param initialAngle - initial angle + /// @param finalAngle - final angle + /// @param direction - rotation direction (0 == CW, 1 == CCW) + Q_INVOKABLE vtkMRMLRTBeamNode* createArcBeamInPlan(vtkMRMLRTPlanNode* planNode, + double initialAngle, double finalAngle, bool rotationDirection); signals: /// Signals for dose calculation progress update diff --git a/ExternalBeamPlanning/qSlicerExternalBeamPlanningModuleWidget.cxx b/ExternalBeamPlanning/qSlicerExternalBeamPlanningModuleWidget.cxx index ba2cba2cc..aea445a3d 100644 --- a/ExternalBeamPlanning/qSlicerExternalBeamPlanningModuleWidget.cxx +++ b/ExternalBeamPlanning/qSlicerExternalBeamPlanningModuleWidget.cxx @@ -23,7 +23,7 @@ #include "qSlicerExternalBeamPlanningModuleWidget.h" #include "ui_qSlicerExternalBeamPlanningModule.h" -// Slicer includes +// SlicerQt includes #include #include #include @@ -34,7 +34,8 @@ #include "vtkMRMLSegmentationNode.h" // SlicerRT includes -#include "vtkSlicerRtCommon.h" +#include +#include // Beams includes #include "vtkMRMLRTBeamNode.h" @@ -247,6 +248,9 @@ void qSlicerExternalBeamPlanningModuleWidget::setup() // Beams section //connect( d->BeamsTableView, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(beamSelectionChanged(QItemSelection,QItemSelection) ) ); + connect( d->ButtonGroup_ArcBeamRotationDirection, SIGNAL(buttonClicked(QAbstractButton*)), + this, SLOT(onArcBeamDirectionClicked(QAbstractButton*))); + connect( d->pushButton_AddBeam, SIGNAL(clicked()), this, SLOT(addBeamClicked()) ); connect( d->pushButton_RemoveBeam, SIGNAL(clicked()), this, SLOT(removeBeamClicked()) ); @@ -910,12 +914,42 @@ void qSlicerExternalBeamPlanningModuleWidget::addBeamClicked() return; } - // Create new beam node by replicating currently selected beam - vtkMRMLRTBeamNode* beamNode = d->DoseEngineLogic->createBeamInPlan(planNode); - if (!beamNode) + vtkMRMLRTBeamNode* beamNode = nullptr; + if (!d->GroupBox_ArcBeamSequence->isChecked()) // Static beam { - qCritical() << Q_FUNC_INFO << ": Failed to add beam"; - return; + // Create new beam node by replicating currently selected beam + beamNode = d->DoseEngineLogic->createBeamInPlan(planNode); + if (!beamNode) + { + qCritical() << Q_FUNC_INFO << ": Failed to add beam"; + return; + } + } + else // Dynamic beam sequence + { + int direction = -1; + if (d->RadioButton_ArcBeamRotationCW->isChecked()) + { + direction = 0; + } + else if (d->RadioButton_ArcBeamRotationCCW->isChecked()) + { + direction = 1; + } + + if (direction != -1) + { + // Create a new dynamic beam sequence node by replicating currently selected beam + beamNode = d->DoseEngineLogic->createArcBeamInPlan( planNode, + d->DoubleSpinBox_ArcBeamInitialAngle->value(), d->DoubleSpinBox_ArcBeamFinalAngle->value(), + static_cast(direction)); + + if (!beamNode) + { + qCritical() << Q_FUNC_INFO << ": Failed to add beam"; + return; + } + } } // Add engine-specific beam parameters to newly created beam node @@ -926,7 +960,11 @@ void qSlicerExternalBeamPlanningModuleWidget::addBeamClicked() qCritical() << Q_FUNC_INFO << ": Failed to access dose engine with name" << planNode->GetDoseEngineName(); return; } - selectedEngine->addBeamParameterAttributesToBeamNode(beamNode); + + if (beamNode) + { + selectedEngine->addBeamParameterAttributesToBeamNode(beamNode); + } // Clear instruction text d->label_CalculateDoseStatus->setText(""); @@ -1127,3 +1165,19 @@ bool qSlicerExternalBeamPlanningModuleWidget::setEditedNode(vtkMRMLNode* node, Q } return false; } + +//----------------------------------------------------------------------------- +void qSlicerExternalBeamPlanningModuleWidget::onArcBeamDirectionClicked(QAbstractButton* button) +{ + Q_D(qSlicerExternalBeamPlanningModuleWidget); + + QRadioButton* rButton = qobject_cast(button); + if (rButton == d->RadioButton_ArcBeamRotationCCW) + { + qDebug() << Q_FUNC_INFO << ": Counter clockwise"; + } + else if (rButton == d->RadioButton_ArcBeamRotationCW) + { + qDebug() << Q_FUNC_INFO << ": Clockwise"; + } +} diff --git a/ExternalBeamPlanning/qSlicerExternalBeamPlanningModuleWidget.h b/ExternalBeamPlanning/qSlicerExternalBeamPlanningModuleWidget.h index 54a699b5c..1f01fc903 100644 --- a/ExternalBeamPlanning/qSlicerExternalBeamPlanningModuleWidget.h +++ b/ExternalBeamPlanning/qSlicerExternalBeamPlanningModuleWidget.h @@ -33,6 +33,7 @@ class vtkMRMLRTBeamNode; class vtkMRMLRTPlanNode; class QString; class QItemSelection; +class QAbstractButton; /// \ingroup SlicerRt_QtModules_ExternalBeamPlanning class Q_SLICER_QTMODULES_EXTERNALBEAMPLANNING_EXPORT qSlicerExternalBeamPlanningModuleWidget : @@ -58,6 +59,8 @@ public slots: /// Update the entire widget based on the current parameter node void updateWidgetFromMRML(); + void onArcBeamDirectionClicked(QAbstractButton* button); + public: /// Get selected beam node. If there are multiple selections then return the first one vtkMRMLRTBeamNode* currentBeamNode(); From f926e2a78ccc3d810a9b19eb30c81ba432788200 Mon Sep 17 00:00:00 2001 From: Mikhail Polkovnikov Date: Tue, 22 Mar 2022 07:40:10 +0300 Subject: [PATCH 2/7] ENH: Initial beam arc sequence --- Beams/Logic/vtkSlicerBeamsModuleLogic.cxx | 10 +++--- Beams/Logic/vtkSlicerBeamsModuleLogic.h | 6 ++-- .../Logic/vtkSlicerDicomRtReader.cxx | 32 ++++++++++++++++++- .../Widgets/qSlicerDoseEngineLogic.cxx | 5 +-- ...SlicerExternalBeamPlanningModuleWidget.cxx | 3 ++ 5 files changed, 44 insertions(+), 12 deletions(-) diff --git a/Beams/Logic/vtkSlicerBeamsModuleLogic.cxx b/Beams/Logic/vtkSlicerBeamsModuleLogic.cxx index 308cc6554..b8ff0ee82 100644 --- a/Beams/Logic/vtkSlicerBeamsModuleLogic.cxx +++ b/Beams/Logic/vtkSlicerBeamsModuleLogic.cxx @@ -315,7 +315,7 @@ void vtkSlicerBeamsModuleLogic::ProcessMRMLNodesEvents(vtkObject* caller, unsign //---------------------------------------------------------------------------- bool vtkSlicerBeamsModuleLogic::CreateArcBeamDynamicSequence( - double initialAngle, double finalAngle, bool direction, + double initialAngle, double finalAngle, bool direction, double angleStep, vtkMRMLRTPlanNode* planNode, vtkMRMLSequenceBrowserNode* beamSequenceBrowserNode, vtkMRMLSequenceNode* beamSequenceNode, vtkMRMLSequenceNode* transformSequenceNode) { @@ -376,23 +376,23 @@ bool vtkSlicerBeamsModuleLogic::CreateArcBeamDynamicSequence( } vtkSmartPointer beamModelHierarchyRootNode; - const char* beamName = "Beam [Arc]"; + const char* beamName = "Beam [ArcDelivery]"; // Create sequence node for RTBeam, transformation, table - // beamSequenceNode; + // beamSequenceNode beamSequenceNode->SetName(beamName); beamSequenceNode->SetIndexName("Control point"); beamSequenceNode->SetIndexUnit("index"); beamSequenceNode->SetIndexType(vtkMRMLSequenceNode::NumericIndex); - // transformSequenceNode; + // transformSequenceNode std::string name = std::string(beamName) + vtkMRMLRTBeamNode::BEAM_TRANSFORM_NODE_NAME_POSTFIX; transformSequenceNode->SetName(name.c_str()); transformSequenceNode->SetIndexName("Control point"); transformSequenceNode->SetIndexUnit("index"); transformSequenceNode->SetIndexType(vtkMRMLSequenceNode::NumericIndex); - // beamSequenceBrowserNode; + // beamSequenceBrowserNode name = std::string(beamName) + "_SequenceBrowser"; beamSequenceBrowserNode->SetName(name.c_str()); diff --git a/Beams/Logic/vtkSlicerBeamsModuleLogic.h b/Beams/Logic/vtkSlicerBeamsModuleLogic.h index 40ffce015..c6f65be3a 100644 --- a/Beams/Logic/vtkSlicerBeamsModuleLogic.h +++ b/Beams/Logic/vtkSlicerBeamsModuleLogic.h @@ -66,12 +66,14 @@ class VTK_SLICER_BEAMS_LOGIC_EXPORT vtkSlicerBeamsModuleLogic : /// @param initialAngle - initial angle in degrees /// @param finalAngle - final angle in degrees /// @param direction - 0 - clockwise, 1 - counter-clockwise + /// @param angleStep - single angle step within arc /// @param planNode - input plan node, which contains reference volume node and isocenter position /// @param sequenceBrowserNode - output sequence browser node /// @param sequenceBeamNode - output beam node /// @param sequenceTransformNode - output transform node - bool CreateArcBeamDynamicSequence( double initialAngle, double finalAngle, bool direction, - vtkMRMLRTPlanNode* planNode, vtkMRMLSequenceBrowserNode* beamSequenceBrowserNode, + bool CreateArcBeamDynamicSequence( double initialAngle, double finalAngle, + bool direction, double angleStep, vtkMRMLRTPlanNode* planNode, + vtkMRMLSequenceBrowserNode* beamSequenceBrowserNode, vtkMRMLSequenceNode* beamSequenceNode, vtkMRMLSequenceNode* transformSequenceNode); protected: diff --git a/DicomRtImportExport/Logic/vtkSlicerDicomRtReader.cxx b/DicomRtImportExport/Logic/vtkSlicerDicomRtReader.cxx index 43b1b45f2..76f8736cf 100644 --- a/DicomRtImportExport/Logic/vtkSlicerDicomRtReader.cxx +++ b/DicomRtImportExport/Logic/vtkSlicerDicomRtReader.cxx @@ -268,6 +268,8 @@ class vtkSlicerDicomRtReader::vtkInternal ScanMode("NONE") { } + bool IsArcDeliverySequence(double& initialAngle, double& finalAngle, bool& rotationDirection) const; + unsigned int Number; std::string Name; std::string Type; @@ -317,7 +319,7 @@ class vtkSlicerDicomRtReader::vtkInternal std::string InstitutionalDepartmentName; std::string ManufacturerModelName; }; - + /// List of loaded beams from external beam plan std::vector BeamSequenceVector; @@ -571,6 +573,34 @@ vtkSlicerDicomRtReader::vtkInternal::ControlPointEntry::operator=(const ControlP return *this; } +bool vtkSlicerDicomRtReader::vtkInternal::BeamEntry::IsArcDeliverySequence( + double& initialAngle, double& finalAngle, bool& rotationDirection) const +{ + bool res = false; + if (ControlPointSequenceVector.size() == 2) + { + const ControlPointEntry& cp0 = ControlPointSequenceVector[0]; + bool point0 = (cp0.CumulativeMetersetWeight == 0.) && + (cp0.GantryRotationDirection == "CC" || cp0.GantryRotationDirection == "CW") && + (cp0.GantryAngle >= 0. && cp0.GantryAngle <= 360.); + const ControlPointEntry& cp1 = ControlPointSequenceVector[1]; + bool point1 = (cp1.CumulativeMetersetWeight == 1.) && + (cp1.GantryRotationDirection == "NONE") && + (cp1.GantryAngle >= 0. && cp1.GantryAngle <= 360.); + + res = point0 && point1; + if (point0 && point1) + { + res = true; + initialAngle = cp0.GantryAngle; + finalAngle = cp1.GantryAngle; + // rotation: 0 - clockwise, 1 - counter clockwise + rotationDirection = (cp0.GantryRotationDirection == "CC"); + } + } + return res; +} + //---------------------------------------------------------------------------- vtkSlicerDicomRtReader::vtkInternal::BeamEntry* vtkSlicerDicomRtReader::vtkInternal::FindBeamByNumber(unsigned int beamNumber) { diff --git a/ExternalBeamPlanning/Widgets/qSlicerDoseEngineLogic.cxx b/ExternalBeamPlanning/Widgets/qSlicerDoseEngineLogic.cxx index e075380f3..122cb58b8 100644 --- a/ExternalBeamPlanning/Widgets/qSlicerDoseEngineLogic.cxx +++ b/ExternalBeamPlanning/Widgets/qSlicerDoseEngineLogic.cxx @@ -516,12 +516,9 @@ vtkMRMLRTBeamNode* qSlicerDoseEngineLogic::createArcBeamInPlan( vtkMRMLRTPlanNod vtkNew transformSequenceNode; vtkNew beamSequenceBrowserNode; - if (beamsLogic->CreateArcBeamDynamicSequence( initialAngle, finalAngle, rotationDirection, + if (beamsLogic->CreateArcBeamDynamicSequence( initialAngle, finalAngle, rotationDirection, 1., planNode, beamSequenceBrowserNode, beamSequenceNode, transformSequenceNode)) { -// vtkMRMLNode* node = beamSequenceBrowserNode->GetProxyNode(beamSequenceNode); -// return vtkMRMLRTBeamNode::SafeDownCast(node); - // Get proxy beam node vtkMRMLNode* node = beamSequenceBrowserNode->GetProxyNode(beamSequenceNode); vtkMRMLRTBeamNode* proxyBeamNode = vtkMRMLRTBeamNode::SafeDownCast(node); diff --git a/ExternalBeamPlanning/qSlicerExternalBeamPlanningModuleWidget.cxx b/ExternalBeamPlanning/qSlicerExternalBeamPlanningModuleWidget.cxx index aea445a3d..43aea1a7e 100644 --- a/ExternalBeamPlanning/qSlicerExternalBeamPlanningModuleWidget.cxx +++ b/ExternalBeamPlanning/qSlicerExternalBeamPlanningModuleWidget.cxx @@ -927,6 +927,7 @@ void qSlicerExternalBeamPlanningModuleWidget::addBeamClicked() } else // Dynamic beam sequence { + QApplication::setOverrideCursor(Qt::WaitCursor); int direction = -1; if (d->RadioButton_ArcBeamRotationCW->isChecked()) { @@ -947,9 +948,11 @@ void qSlicerExternalBeamPlanningModuleWidget::addBeamClicked() if (!beamNode) { qCritical() << Q_FUNC_INFO << ": Failed to add beam"; + QApplication::restoreOverrideCursor(); return; } } + QApplication::restoreOverrideCursor(); } // Add engine-specific beam parameters to newly created beam node From 36f5b733112e0b02cfea2b0996c4555f193d86a6 Mon Sep 17 00:00:00 2001 From: Michael Colonel Date: Thu, 24 Mar 2022 16:43:47 +0300 Subject: [PATCH 3/7] ENH: Initial beam arc sequence --- Beams/Logic/vtkSlicerBeamsModuleLogic.cxx | 98 ++++++++++++++++- Beams/Logic/vtkSlicerBeamsModuleLogic.h | 4 +- .../UI/qMRMLBeamParametersTabWidget.ui | 2 +- .../UI/qSlicerExternalBeamPlanningModule.ui | 101 ++++++++++++------ .../Widgets/qSlicerDoseEngineLogic.cxx | 6 +- .../Widgets/qSlicerDoseEngineLogic.h | 3 +- ...SlicerExternalBeamPlanningModuleWidget.cxx | 4 +- 7 files changed, 177 insertions(+), 41 deletions(-) diff --git a/Beams/Logic/vtkSlicerBeamsModuleLogic.cxx b/Beams/Logic/vtkSlicerBeamsModuleLogic.cxx index b8ff0ee82..f58696c45 100644 --- a/Beams/Logic/vtkSlicerBeamsModuleLogic.cxx +++ b/Beams/Logic/vtkSlicerBeamsModuleLogic.cxx @@ -315,7 +315,7 @@ void vtkSlicerBeamsModuleLogic::ProcessMRMLNodesEvents(vtkObject* caller, unsign //---------------------------------------------------------------------------- bool vtkSlicerBeamsModuleLogic::CreateArcBeamDynamicSequence( - double initialAngle, double finalAngle, bool direction, double angleStep, + double initialAngle, double finalAngle, double stepAngle, bool direction, vtkMRMLRTPlanNode* planNode, vtkMRMLSequenceBrowserNode* beamSequenceBrowserNode, vtkMRMLSequenceNode* beamSequenceNode, vtkMRMLSequenceNode* transformSequenceNode) { @@ -330,6 +330,95 @@ bool vtkSlicerBeamsModuleLogic::CreateArcBeamDynamicSequence( return false; } + if (!direction && initialAngle < finalAngle) // CW, ini < fin + { + for (double angle = initialAngle; angle <= finalAngle; angle += stepAngle) + { + angles.push_back(angle); + } + if (angles.back() < finalAngle) + { + angles.push_back(finalAngle); + } + std::cout << "Angles " << angles.size() << '\n'; + std::for_each( angles.begin(), angles.end(), [](double v){ std::cout << v << ' '; }); + std::cout << std::endl; + } + else if (!direction && initialAngle > finalAngle) // CW, ini > fin + { + for (double angle = initialAngle; angle <= 360.; angle += stepAngle) + { + angles.push_back(angle); + } + double startAngle = 1.; + if (angles.back() < 360.) + { + startAngle = angles.back() + stepAngle - 360.; + } + else if (angles.back() == 360.) + { + startAngle = stepAngle; + } + + for (double angle = startAngle; angle <= finalAngle; angle += stepAngle) + { + angles.push_back(angle); + } + if (angles.back() < finalAngle) + { + angles.push_back(finalAngle); + } + std::cout << "Angles " << angles.size() << '\n'; + std::for_each( angles.begin(), angles.end(), [](double v){ std::cout << v << ' '; }); + std::cout << std::endl; + } + else if (direction && initialAngle < finalAngle) // CCW, ini < fin + { + for (double angle = initialAngle; angle >= 0.0; angle -= stepAngle) + { + angles.push_back(angle); + } + double startAngle = 359.; + if (angles.back() > 0.) + { + startAngle = angles.back() - stepAngle + 360.; + } + else if (angles.back() == 0.) + { + startAngle = 360. - stepAngle; + } + for (double angle = startAngle; angle >= finalAngle; angle -= stepAngle) + { + angles.push_back(angle); + } + if (angles.back() > finalAngle) + { + angles.push_back(finalAngle); + } + std::cout << "Angles " << angles.size() << '\n'; + std::for_each( angles.begin(), angles.end(), [](double v){ std::cout << v << ' '; }); + std::cout << std::endl; + } + else if (direction && initialAngle > finalAngle) // CCW, ini > fin + { + for (double angle = initialAngle; angle >= finalAngle; angle -= stepAngle) + { + angles.push_back(angle); + } + if (angles.back() > finalAngle) + { + angles.push_back(finalAngle); + } + std::cout << "Angles " << angles.size() << '\n'; + std::for_each( angles.begin(), angles.end(), [](double v){ std::cout << v << ' '; }); + std::cout << std::endl; + } +/* + if (finalAngle < 0. && finalAngle > 360. && initialAngle < 0. && initialAngle > 360.) + { + return false; + } + if (!direction && initialAngle < finalAngle) // CW, ini < fin { for (double angle = initialAngle; angle <= finalAngle; angle += 1.) @@ -366,6 +455,13 @@ bool vtkSlicerBeamsModuleLogic::CreateArcBeamDynamicSequence( angles.push_back(angle); } } +*/ + if (angles.size() < 2) + { + vtkErrorMacro("CreateArcBeamDynamicSequence: Number of angle elements is less than 2"); + return false; + } + vtkMRMLScene* scene = planNode->GetScene(); vtkMRMLSubjectHierarchyNode* shNode = vtkMRMLSubjectHierarchyNode::GetSubjectHierarchyNode(scene); diff --git a/Beams/Logic/vtkSlicerBeamsModuleLogic.h b/Beams/Logic/vtkSlicerBeamsModuleLogic.h index c6f65be3a..8869840f3 100644 --- a/Beams/Logic/vtkSlicerBeamsModuleLogic.h +++ b/Beams/Logic/vtkSlicerBeamsModuleLogic.h @@ -65,14 +65,14 @@ class VTK_SLICER_BEAMS_LOGIC_EXPORT vtkSlicerBeamsModuleLogic : /// Create arc delivery beam sequence /// @param initialAngle - initial angle in degrees /// @param finalAngle - final angle in degrees - /// @param direction - 0 - clockwise, 1 - counter-clockwise /// @param angleStep - single angle step within arc + /// @param direction - 0 - clockwise, 1 - counter-clockwise /// @param planNode - input plan node, which contains reference volume node and isocenter position /// @param sequenceBrowserNode - output sequence browser node /// @param sequenceBeamNode - output beam node /// @param sequenceTransformNode - output transform node bool CreateArcBeamDynamicSequence( double initialAngle, double finalAngle, - bool direction, double angleStep, vtkMRMLRTPlanNode* planNode, + double angleStep, bool direction, vtkMRMLRTPlanNode* planNode, vtkMRMLSequenceBrowserNode* beamSequenceBrowserNode, vtkMRMLSequenceNode* beamSequenceNode, vtkMRMLSequenceNode* transformSequenceNode); diff --git a/Beams/Widgets/Resources/UI/qMRMLBeamParametersTabWidget.ui b/Beams/Widgets/Resources/UI/qMRMLBeamParametersTabWidget.ui index f46d96c01..010b07b92 100644 --- a/Beams/Widgets/Resources/UI/qMRMLBeamParametersTabWidget.ui +++ b/Beams/Widgets/Resources/UI/qMRMLBeamParametersTabWidget.ui @@ -14,7 +14,7 @@ TabWidget - 0 + 2 diff --git a/ExternalBeamPlanning/Resources/UI/qSlicerExternalBeamPlanningModule.ui b/ExternalBeamPlanning/Resources/UI/qSlicerExternalBeamPlanningModule.ui index 0c6afee6c..28187a7cf 100644 --- a/ExternalBeamPlanning/Resources/UI/qSlicerExternalBeamPlanningModule.ui +++ b/ExternalBeamPlanning/Resources/UI/qSlicerExternalBeamPlanningModule.ui @@ -6,8 +6,8 @@ 0 0 - 482 - 632 + 483 + 704 @@ -564,17 +564,23 @@ - - - - Final angle + + + + 2 + + + 359.000000000000000 + + + 180.000000000000000 - - + + - Initial angle + Final angle @@ -585,41 +591,72 @@ - - + + - CCW + Initial angle - - ButtonGroup_ArcBeamRotationDirection - - - + + + + Angle step + + + + + - 2 + 1 + + + 0.100000000000000 - 359.000000000000000 + 10.000000000000000 + + + 0.100000000000000 - 180.000000000000000 + 1.000000000000000 - - - - CW - - - true - - - ButtonGroup_ArcBeamRotationDirection - - + + + + + + Rotation direction + + + + + + + CW + + + true + + + ButtonGroup_ArcBeamRotationDirection + + + + + + + CCW + + + ButtonGroup_ArcBeamRotationDirection + + + + diff --git a/ExternalBeamPlanning/Widgets/qSlicerDoseEngineLogic.cxx b/ExternalBeamPlanning/Widgets/qSlicerDoseEngineLogic.cxx index 122cb58b8..9844ed3ec 100644 --- a/ExternalBeamPlanning/Widgets/qSlicerDoseEngineLogic.cxx +++ b/ExternalBeamPlanning/Widgets/qSlicerDoseEngineLogic.cxx @@ -492,7 +492,7 @@ vtkMRMLRTBeamNode* qSlicerDoseEngineLogic::createBeamInPlan(vtkMRMLRTPlanNode* p //--------------------------------------------------------------------------- vtkMRMLRTBeamNode* qSlicerDoseEngineLogic::createArcBeamInPlan( vtkMRMLRTPlanNode* planNode, - double initialAngle, double finalAngle, bool rotationDirection) + double initialAngle, double finalAngle, double stepAngle, bool rotationDirection) { // Set beams logic vtkSlicerBeamsModuleLogic* beamsLogic = nullptr; @@ -516,8 +516,8 @@ vtkMRMLRTBeamNode* qSlicerDoseEngineLogic::createArcBeamInPlan( vtkMRMLRTPlanNod vtkNew transformSequenceNode; vtkNew beamSequenceBrowserNode; - if (beamsLogic->CreateArcBeamDynamicSequence( initialAngle, finalAngle, rotationDirection, 1., - planNode, beamSequenceBrowserNode, beamSequenceNode, transformSequenceNode)) + if (beamsLogic->CreateArcBeamDynamicSequence( initialAngle, finalAngle, stepAngle, + rotationDirection, planNode, beamSequenceBrowserNode, beamSequenceNode, transformSequenceNode)) { // Get proxy beam node vtkMRMLNode* node = beamSequenceBrowserNode->GetProxyNode(beamSequenceNode); diff --git a/ExternalBeamPlanning/Widgets/qSlicerDoseEngineLogic.h b/ExternalBeamPlanning/Widgets/qSlicerDoseEngineLogic.h index 8f78c4433..e3a9f3bb1 100644 --- a/ExternalBeamPlanning/Widgets/qSlicerDoseEngineLogic.h +++ b/ExternalBeamPlanning/Widgets/qSlicerDoseEngineLogic.h @@ -76,9 +76,10 @@ class Q_SLICER_MODULE_EXTERNALBEAMPLANNING_WIDGETS_EXPORT qSlicerDoseEngineLogic /// @param planNode - node of the current plan /// @param initialAngle - initial angle /// @param finalAngle - final angle + /// @param stepAngle - step angle /// @param direction - rotation direction (0 == CW, 1 == CCW) Q_INVOKABLE vtkMRMLRTBeamNode* createArcBeamInPlan(vtkMRMLRTPlanNode* planNode, - double initialAngle, double finalAngle, bool rotationDirection); + double initialAngle = 0., double finalAngle = 360., double stepAngle = 1., bool rotationDirection = false); signals: /// Signals for dose calculation progress update diff --git a/ExternalBeamPlanning/qSlicerExternalBeamPlanningModuleWidget.cxx b/ExternalBeamPlanning/qSlicerExternalBeamPlanningModuleWidget.cxx index 43aea1a7e..3e92a3666 100644 --- a/ExternalBeamPlanning/qSlicerExternalBeamPlanningModuleWidget.cxx +++ b/ExternalBeamPlanning/qSlicerExternalBeamPlanningModuleWidget.cxx @@ -942,7 +942,9 @@ void qSlicerExternalBeamPlanningModuleWidget::addBeamClicked() { // Create a new dynamic beam sequence node by replicating currently selected beam beamNode = d->DoseEngineLogic->createArcBeamInPlan( planNode, - d->DoubleSpinBox_ArcBeamInitialAngle->value(), d->DoubleSpinBox_ArcBeamFinalAngle->value(), + d->DoubleSpinBox_ArcBeamInitialAngle->value(), + d->DoubleSpinBox_ArcBeamFinalAngle->value(), + d->DoubleSpinBox_ArcBeamAngleStep->value(), static_cast(direction)); if (!beamNode) From c560fabbe1ecef13b7f70bd1f8106c9ec8b013d7 Mon Sep 17 00:00:00 2001 From: Mikhail Polkovnikov Date: Thu, 24 Mar 2022 23:06:15 +0300 Subject: [PATCH 4/7] ENH: Initial beam arc sequence --- DicomRtImportExport/Logic/vtkSlicerDicomRtReader.cxx | 12 ++++++++++++ DicomRtImportExport/Logic/vtkSlicerDicomRtReader.h | 9 +++++++++ 2 files changed, 21 insertions(+) diff --git a/DicomRtImportExport/Logic/vtkSlicerDicomRtReader.cxx b/DicomRtImportExport/Logic/vtkSlicerDicomRtReader.cxx index 76f8736cf..48ab5c4c9 100644 --- a/DicomRtImportExport/Logic/vtkSlicerDicomRtReader.cxx +++ b/DicomRtImportExport/Logic/vtkSlicerDicomRtReader.cxx @@ -2706,6 +2706,18 @@ const char* vtkSlicerDicomRtReader::GetBeamTreatmentDeliveryType(unsigned int be return beam->TreatmentDeliveryType.c_str(); } +//---------------------------------------------------------------------------- +bool vtkSlicerDicomRtReader::CheckBeamArcDeliveryType(unsigned int beamNumber, + double& initialAngle, double& finalAngle, bool& rotationDirection) const +{ + const vtkInternal::BeamEntry* beam=this->Internal->FindBeamByNumber(beamNumber); + if (!beam) + { + return false; + } + return beam->IsArcDeliverySequence(initialAngle, finalAngle, rotationDirection); +} + //---------------------------------------------------------------------------- const char* vtkSlicerDicomRtReader::GetBeamRadiationType(unsigned int beamNumber) { diff --git a/DicomRtImportExport/Logic/vtkSlicerDicomRtReader.h b/DicomRtImportExport/Logic/vtkSlicerDicomRtReader.h index b6813b2be..9aafd2242 100644 --- a/DicomRtImportExport/Logic/vtkSlicerDicomRtReader.h +++ b/DicomRtImportExport/Logic/vtkSlicerDicomRtReader.h @@ -94,6 +94,15 @@ class VTK_SLICER_DICOMRTIMPORTEXPORT_LOGIC_EXPORT vtkSlicerDicomRtReader : publi /// Get radiation type (primary particle) for a given beam const char* GetBeamTreatmentDeliveryType(unsigned int beamNumber); + /// Check if beam control point sequence is an arc beam delivery + /// @param beamNumber - beam number + /// @param initialAngle - initial angle of the arc sequence in degrees + /// @param finalAngle - final angle of the arc sequence in degrees + /// @param rotationDirection - rotation direction of the gantry (0 - clockwise, 1 - counter clockwise) + /// @return true if beam control point sequence is arc, false otherwise + bool CheckBeamArcDeliveryType(unsigned int beamNumber, double& initialAngle, + double& finalAngle, bool& rotationDirection) const; + /// Get radiation type (primary particle) for a given beam const char* GetBeamRadiationType(unsigned int beamNumber); From e9b6e98594d6b64818aed774758219c78e2733e8 Mon Sep 17 00:00:00 2001 From: Mikhail Polkovnikov Date: Tue, 2 Jul 2024 17:07:36 +0300 Subject: [PATCH 5/7] ENH: Initial beam arc sequence --- Beams/qSlicerBeamsModule.cxx | 7 +++---- Isodose/qSlicerIsodoseModule.cxx | 7 +++---- PlanarImage/qSlicerPlanarImageModule.cxx | 7 +++---- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/Beams/qSlicerBeamsModule.cxx b/Beams/qSlicerBeamsModule.cxx index da307fb87..35c9f75a2 100644 --- a/Beams/qSlicerBeamsModule.cxx +++ b/Beams/qSlicerBeamsModule.cxx @@ -82,10 +82,9 @@ QStringList qSlicerBeamsModule::dependencies()const //----------------------------------------------------------------------------- QString qSlicerBeamsModule::helpText()const { - QString help = - "This module displays and handles beam geometry models created from the loaded isocenter and source fiducials. " - "For more information see %1/Documentation/%2.%3/Modules/Beams
"; - return help.arg(this->slicerWikiUrl()).arg(Slicer_VERSION_MAJOR).arg(Slicer_VERSION_MINOR); + return QString("This module displays and handles beam geometry models created from the loaded isocenter and source fiducials. " + "For more information see %1/Documentation/%2.%3/Modules/Beams
").arg( + this->slicerWikiUrl()).arg(qSlicerCoreApplication::application()->majorVersion()).arg(qSlicerCoreApplication::application()->minorVersion()); } //----------------------------------------------------------------------------- diff --git a/Isodose/qSlicerIsodoseModule.cxx b/Isodose/qSlicerIsodoseModule.cxx index 6c9d5b09c..4b267e91a 100644 --- a/Isodose/qSlicerIsodoseModule.cxx +++ b/Isodose/qSlicerIsodoseModule.cxx @@ -82,10 +82,9 @@ qSlicerIsodoseModule::~qSlicerIsodoseModule() = default; //----------------------------------------------------------------------------- QString qSlicerIsodoseModule::helpText()const { - QString help = - "This module generates iso dose surface models using user defined dose levels. " - "For more information see %1/Documentation/%2.%3/Modules/Isodose
"; - return help.arg(this->slicerWikiUrl()).arg(Slicer_VERSION_MAJOR).arg(Slicer_VERSION_MINOR); + return QString("This module generates iso dose surface models using user defined dose levels. " + "For more information see %1/Documentation/%2.%3/Modules/Isodose
").arg( + this->slicerWikiUrl()).arg(qSlicerCoreApplication::application()->majorVersion()).arg(qSlicerCoreApplication::application()->minorVersion()); } //----------------------------------------------------------------------------- diff --git a/PlanarImage/qSlicerPlanarImageModule.cxx b/PlanarImage/qSlicerPlanarImageModule.cxx index 5fb395b4e..00951780d 100644 --- a/PlanarImage/qSlicerPlanarImageModule.cxx +++ b/PlanarImage/qSlicerPlanarImageModule.cxx @@ -71,10 +71,9 @@ qSlicerPlanarImageModule::~qSlicerPlanarImageModule() = default; //----------------------------------------------------------------------------- QString qSlicerPlanarImageModule::helpText()const { - QString help = - "This module displays and handles planar images (single-slice volumes) as textured models. " - "For more information see %1/Documentation/%2.%3/Modules/PlanarImage
"; - return help.arg(this->slicerWikiUrl()).arg(Slicer_VERSION_MAJOR).arg(Slicer_VERSION_MINOR); + return QString("This module displays and handles planar images (single-slice volumes) as textured models. " + "For more information see %1/Documentation/%2.%3/Modules/PlanarImage
").arg( + this->slicerWikiUrl()).arg(qSlicerCoreApplication::application()->majorVersion()).arg(qSlicerCoreApplication::application()->minorVersion()); } //----------------------------------------------------------------------------- From 1ea1d2282ae0afaa94c725c285c225d2c3e94eb4 Mon Sep 17 00:00:00 2001 From: Mikhail Polkovnikov Date: Tue, 2 Jul 2024 17:18:13 +0300 Subject: [PATCH 6/7] ENH: Initial beam arc sequence --- ...anarContourToClosedSurfaceConversionRule.cxx | 17 ++++++++++++----- .../qSlicerDicomRtImportExportModule.cxx | 7 +++---- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/DicomRtImportExport/ConversionRules/vtkPlanarContourToClosedSurfaceConversionRule.cxx b/DicomRtImportExport/ConversionRules/vtkPlanarContourToClosedSurfaceConversionRule.cxx index 42ae1c7f6..0cef4cd04 100644 --- a/DicomRtImportExport/ConversionRules/vtkPlanarContourToClosedSurfaceConversionRule.cxx +++ b/DicomRtImportExport/ConversionRules/vtkPlanarContourToClosedSurfaceConversionRule.cxx @@ -21,6 +21,7 @@ #include "vtkPlanarContourToClosedSurfaceConversionRule.h" // VTK includes +#include #include #include #include @@ -80,9 +81,9 @@ vtkPlanarContourToClosedSurfaceConversionRule::vtkPlanarContourToClosedSurfaceCo this->ImagePadding[1] = 4; this->ImagePadding[2] = 0; - this->ConversionParameters[this->GetDefaultSliceThicknessParameterName()] = std::make_pair("0.0", + this->ConversionParameters->SetParameter(this->GetDefaultSliceThicknessParameterName(), "0.0", "Default thickness for contours if slice spacing cannot be calculated."); - this->ConversionParameters[this->GetEndCappingParameterName()] = std::make_pair("1", + this->ConversionParameters->SetParameter(this->GetEndCappingParameterName(), "1", "Create end cap to close surface inside contours on the top and bottom of the structure.\n" "0 = leave contours open on surface exterior.\n" "1 (default) = close surface by generating smooth end caps.\n" @@ -1180,7 +1181,7 @@ void vtkPlanarContourToClosedSurfaceConversionRule::EndCapping(vtkPolyData* inpu //---------------------------------------------------------------------------- double vtkPlanarContourToClosedSurfaceConversionRule::GetSpacingBetweenLines(vtkPolyData* inputROIPoints) { - double defaultSliceThickness = vtkVariant(this->ConversionParameters[this->GetDefaultSliceThicknessParameterName()].first).ToDouble(); + double defaultSliceThickness = this->ConversionParameters->GetValueAsDouble(this->GetDefaultSliceThicknessParameterName()); if (!inputROIPoints) { @@ -1355,6 +1356,12 @@ void vtkPlanarContourToClosedSurfaceConversionRule::CreateSmoothEndCapContour(vt linePolyData->SetPoints(inputROIPoints->GetPoints()); linePolyData->SetLines(lines); + // Remove unused points + vtkNew cleanFilter; + cleanFilter->SetInputData(linePolyData); + cleanFilter->Update(); + linePolyData->ShallowCopy(cleanFilter->GetOutput()); + double bounds[6] = { 0, 0, 0, 0, 0, 0 }; linePolyData->GetBounds(bounds); @@ -1383,7 +1390,7 @@ void vtkPlanarContourToClosedSurfaceConversionRule::CreateSmoothEndCapContour(vt static_cast(std::ceil((bounds[3] - bounds[2]) / spacing[1])), 1 }; double origin[3] = { bounds[0], bounds[2], bounds[4] }; - int extent[6] = { 0, dimensions[0] - 1, 0, dimensions[1] - 1, 0, 0 }; + int extent[6] = { 0, dimensions[0] - 1, 0, dimensions[1] - 1, 0, dimensions[2] - 1 }; vtkSmartPointer blankImage = vtkSmartPointer::New(); blankImage->SetSpacing(spacing); @@ -1471,7 +1478,7 @@ void vtkPlanarContourToClosedSurfaceConversionRule::CreateSmoothEndCapContour(vt // Calculate the decimation factor with the following formula: ( # of lines in input * number of points in original line ) / number of points in input double decimationFactor = (1.0 * newLines->GetNumberOfLines() * inputLine->GetNumberOfPoints() + 1) / newLines->GetNumberOfPoints(); - // Reduce the number of points in the line until the ration between the input and output lines meets the specified decimation factor + // Reduce the number of points in the line until the ratio between the input and output lines meets the specified decimation factor this->DecimateLines(newLines, decimationFactor); vtkSmartPointer inputPoints = inputROIPoints->GetPoints(); diff --git a/DicomRtImportExport/qSlicerDicomRtImportExportModule.cxx b/DicomRtImportExport/qSlicerDicomRtImportExportModule.cxx index b0adb3027..3d6671896 100644 --- a/DicomRtImportExport/qSlicerDicomRtImportExportModule.cxx +++ b/DicomRtImportExport/qSlicerDicomRtImportExportModule.cxx @@ -77,10 +77,9 @@ qSlicerDicomRtImportExportModule::~qSlicerDicomRtImportExportModule() = default; //----------------------------------------------------------------------------- QString qSlicerDicomRtImportExportModule::helpText()const { - QString help = - "The DicomRtImportExport module enables importing and loading DICOM RT files into the Slicer DICOM database and the Slicer scene, and exporting MRML nodes into DICOM-RT files" - "For more information see %1/Documentation/%2.%3/Modules/Models
"; - return help.arg(this->slicerWikiUrl()).arg(Slicer_VERSION_MAJOR).arg(Slicer_VERSION_MINOR); + return QString("The DicomRtImportExport module enables importing and loading DICOM RT files into the Slicer DICOM database and the Slicer scene, and exporting MRML nodes into DICOM-RT files" + "For more information see %1/Documentation/%2.%3/Modules/Models
").arg( + this->slicerWikiUrl()).arg(qSlicerCoreApplication::application()->majorVersion()).arg(qSlicerCoreApplication::application()->minorVersion()); } //----------------------------------------------------------------------------- From 5248e63ba88c74d8cadc7cd0e89c7edb5f3eaaa5 Mon Sep 17 00:00:00 2001 From: Mikhail Polkovnikov Date: Tue, 2 Jul 2024 17:36:38 +0300 Subject: [PATCH 7/7] ENH: Initial beam arc sequence --- .../qSlicerDicomSroImportExportModule.cxx | 7 +++---- .../qSlicerDoseAccumulationModule.cxx | 7 +++---- DoseComparison/qSlicerDoseComparisonModule.cxx | 11 +++++++---- .../qSlicerDoseVolumeHistogramModule.cxx | 7 +++---- .../qSlicerExternalBeamPlanningModule.cxx | 7 +++---- PlmDrr/plastimatch_slicer_drr.cxx | 18 +++++++++++++++--- RoomsEyeView/qSlicerRoomsEyeViewModule.cxx | 7 +++---- .../qSlicerSegmentComparisonModule.cxx | 11 +++++++---- 8 files changed, 44 insertions(+), 31 deletions(-) diff --git a/DicomSroImportExport/qSlicerDicomSroImportExportModule.cxx b/DicomSroImportExport/qSlicerDicomSroImportExportModule.cxx index a9217710c..a55931289 100644 --- a/DicomSroImportExport/qSlicerDicomSroImportExportModule.cxx +++ b/DicomSroImportExport/qSlicerDicomSroImportExportModule.cxx @@ -69,10 +69,9 @@ qSlicerDicomSroImportExportModule::~qSlicerDicomSroImportExportModule() = defaul //----------------------------------------------------------------------------- QString qSlicerDicomSroImportExportModule::helpText()const { - QString help = - "The DicomSroImportExport module enables importing and loading DICOM Spatial Registration Objects into the Slicer DICOM database and the Slicer scene, and exporting transformations as DICOM SROs. " - "For more information see %1/Documentation/%2.%3/Modules/Models
"; - return help.arg(this->slicerWikiUrl()).arg(Slicer_VERSION_MAJOR).arg(Slicer_VERSION_MINOR); + return QString("The DicomSroImportExport module enables importing and loading DICOM Spatial Registration Objects into the Slicer DICOM database and the Slicer scene, and exporting transformations as DICOM SROs. " + "For more information see %1/Documentation/%2.%3/Modules/Models
").arg( + this->slicerWikiUrl()).arg(qSlicerCoreApplication::application()->majorVersion()).arg(qSlicerCoreApplication::application()->minorVersion()); } //----------------------------------------------------------------------------- diff --git a/DoseAccumulation/qSlicerDoseAccumulationModule.cxx b/DoseAccumulation/qSlicerDoseAccumulationModule.cxx index 78b948edb..e69b8e8a7 100644 --- a/DoseAccumulation/qSlicerDoseAccumulationModule.cxx +++ b/DoseAccumulation/qSlicerDoseAccumulationModule.cxx @@ -70,10 +70,9 @@ qSlicerDoseAccumulationModule::~qSlicerDoseAccumulationModule() = default; //----------------------------------------------------------------------------- QString qSlicerDoseAccumulationModule::helpText()const { - QString help = - "This module accumulates the multiple dose distribution maps into one dose map. " - "For more information see %1/Documentation/%2.%3/Modules/DoseAccumulation
"; - return help.arg(this->slicerWikiUrl()).arg(Slicer_VERSION_MAJOR).arg(Slicer_VERSION_MINOR); + return QString("This module accumulates the multiple dose distribution maps into one dose map. " + "For more information see %1/Documentation/%2.%3/Modules/DoseAccumulation
").arg( + this->slicerWikiUrl()).arg(qSlicerCoreApplication::application()->majorVersion()).arg(qSlicerCoreApplication::application()->minorVersion()); } //----------------------------------------------------------------------------- diff --git a/DoseComparison/qSlicerDoseComparisonModule.cxx b/DoseComparison/qSlicerDoseComparisonModule.cxx index 95a79c3b8..523d82e76 100644 --- a/DoseComparison/qSlicerDoseComparisonModule.cxx +++ b/DoseComparison/qSlicerDoseComparisonModule.cxx @@ -18,6 +18,10 @@ ==============================================================================*/ +// Slicer includes +#include +#include + // SubjectHierarchy Plugins includes #include "qSlicerSubjectHierarchyPluginHandler.h" #include "qSlicerSubjectHierarchyGammaPlugin.h" @@ -65,10 +69,9 @@ qSlicerDoseComparisonModule::~qSlicerDoseComparisonModule() = default; //----------------------------------------------------------------------------- QString qSlicerDoseComparisonModule::helpText()const { - QString help = - "This module computes and displays dose comparison metrics. " - "For more information see %1/Documentation/%2.%3/Modules/DoseComparison
"; - return help.arg(this->slicerWikiUrl()).arg(Slicer_VERSION_MAJOR).arg(Slicer_VERSION_MINOR); + return QString("This module computes and displays dose comparison metrics. " + "For more information see %1/Documentation/%2.%3/Modules/DoseComparison
").arg( + this->slicerWikiUrl()).arg(qSlicerCoreApplication::application()->majorVersion()).arg(qSlicerCoreApplication::application()->minorVersion()); } //----------------------------------------------------------------------------- diff --git a/DoseVolumeHistogram/qSlicerDoseVolumeHistogramModule.cxx b/DoseVolumeHistogram/qSlicerDoseVolumeHistogramModule.cxx index 1eb918b1d..cf29ef537 100644 --- a/DoseVolumeHistogram/qSlicerDoseVolumeHistogramModule.cxx +++ b/DoseVolumeHistogram/qSlicerDoseVolumeHistogramModule.cxx @@ -69,10 +69,9 @@ qSlicerDoseVolumeHistogramModule::~qSlicerDoseVolumeHistogramModule() = default; //----------------------------------------------------------------------------- QString qSlicerDoseVolumeHistogramModule::helpText()const { - QString help = - "This module computes dose volume histogram (DVH) and metrics from a dose map and segmentation. " - "For more information see %1/Documentation/%2.%3/Modules/DoseVolumeHistogram
"; - return help.arg(this->slicerWikiUrl()).arg(Slicer_VERSION_MAJOR).arg(Slicer_VERSION_MINOR); + return QString("This module computes dose volume histogram (DVH) and metrics from a dose map and segmentation. " + "For more information see %1/Documentation/%2.%3/Modules/DoseVolumeHistogram
").arg( + this->slicerWikiUrl()).arg(qSlicerCoreApplication::application()->majorVersion()).arg(qSlicerCoreApplication::application()->minorVersion()); } //----------------------------------------------------------------------------- diff --git a/ExternalBeamPlanning/qSlicerExternalBeamPlanningModule.cxx b/ExternalBeamPlanning/qSlicerExternalBeamPlanningModule.cxx index 66ffe3042..5321fc16c 100644 --- a/ExternalBeamPlanning/qSlicerExternalBeamPlanningModule.cxx +++ b/ExternalBeamPlanning/qSlicerExternalBeamPlanningModule.cxx @@ -77,10 +77,9 @@ qSlicerExternalBeamPlanningModule::~qSlicerExternalBeamPlanningModule() = defaul //----------------------------------------------------------------------------- QString qSlicerExternalBeamPlanningModule::helpText()const { - QString help = - "The External Beam Planning module facilitates basic EBRT planning. " - "For more information see %1/Documentation/%2.%3/Modules/ExternalBeamPlanning
"; - return help.arg(this->slicerWikiUrl()).arg(Slicer_VERSION_MAJOR).arg(Slicer_VERSION_MINOR); + return QString("The External Beam Planning module facilitates basic EBRT planning. " + "For more information see %1/Documentation/%2.%3/Modules/ExternalBeamPlanning
").arg( + this->slicerWikiUrl()).arg(qSlicerCoreApplication::application()->majorVersion()).arg(qSlicerCoreApplication::application()->minorVersion()); } //----------------------------------------------------------------------------- diff --git a/PlmDrr/plastimatch_slicer_drr.cxx b/PlmDrr/plastimatch_slicer_drr.cxx index e2ac9d949..a1ceda988 100644 --- a/PlmDrr/plastimatch_slicer_drr.cxx +++ b/PlmDrr/plastimatch_slicer_drr.cxx @@ -43,7 +43,7 @@ namespace { -int DoSetupDRR( int argc, char * argv[], Drr_options& options ) throw( std::string ) +int DoSetupDRR( int argc, char * argv[], Drr_options& options ) { PARSE_ARGS; @@ -166,7 +166,7 @@ int DoSetupDRR( int argc, char * argv[], Drr_options& options ) throw( std::stri } template -int DoIt( int argc, char * argv[], Drr_options& options, TPixel ) throw( std::string, itk::ExceptionObject ) +int DoIt( int argc, char * argv[], Drr_options& options, TPixel ) { PARSE_ARGS; @@ -217,6 +217,18 @@ int DoIt( int argc, char * argv[], Drr_options& options, TPixel ) throw( std::st options.output_file = tmpDir + "outputVolume.raw"; mhdFilename = tmpDir + "outputVolume.mhd"; } + else if (found < inputVolume.size() && options.output_format == OUTPUT_FORMAT_PFM) + { + std::string tmpDir = inputVolume.substr( 0, found + 1); + options.input_file = tmpDir + "inputVolume.mha"; + options.output_file = tmpDir + "outputVolume.pfm"; + } + else if (found < inputVolume.size() && options.output_format == OUTPUT_FORMAT_PGM) + { + std::string tmpDir = inputVolume.substr( 0, found + 1); + options.input_file = tmpDir + "inputVolume.mha"; + options.output_file = tmpDir + "outputVolume.pgm"; + } else if (found == inputVolume.size() || found == std::string::npos) { throw std::string("Unable to find directory name"); @@ -237,7 +249,7 @@ int DoIt( int argc, char * argv[], Drr_options& options, TPixel ) throw( std::st drr_compute(&options); // Create mhd file for raw file loading - if (!mhdFilename.empty()) + if (!mhdFilename.empty() && options.output_format == OUTPUT_FORMAT_RAW) { // Plastimatch DRR pixel type (float) typedef float PlmDrrPixelType; diff --git a/RoomsEyeView/qSlicerRoomsEyeViewModule.cxx b/RoomsEyeView/qSlicerRoomsEyeViewModule.cxx index 8f056074b..83a3c82fc 100644 --- a/RoomsEyeView/qSlicerRoomsEyeViewModule.cxx +++ b/RoomsEyeView/qSlicerRoomsEyeViewModule.cxx @@ -78,10 +78,9 @@ QStringList qSlicerRoomsEyeViewModule::dependencies()const //----------------------------------------------------------------------------- QString qSlicerRoomsEyeViewModule::helpText()const { - QString help = - "This module displays and handles beam geometry models created from the loaded isocenter and source fiducials. " - "For more information see %1/Documentation/%2.%3/Modules/RoomsEyeView
"; - return help.arg(this->slicerWikiUrl()).arg(Slicer_VERSION_MAJOR).arg(Slicer_VERSION_MINOR); + return QString("This module displays and handles beam geometry models created from the loaded isocenter and source fiducials. " + "For more information see %1/Documentation/%2.%3/Modules/RoomsEyeView
").arg( + this->slicerWikiUrl()).arg(qSlicerCoreApplication::application()->majorVersion()).arg(qSlicerCoreApplication::application()->minorVersion()); } //----------------------------------------------------------------------------- diff --git a/SegmentComparison/qSlicerSegmentComparisonModule.cxx b/SegmentComparison/qSlicerSegmentComparisonModule.cxx index 6a4e5377e..55bf1e1af 100644 --- a/SegmentComparison/qSlicerSegmentComparisonModule.cxx +++ b/SegmentComparison/qSlicerSegmentComparisonModule.cxx @@ -18,6 +18,10 @@ ==============================================================================*/ +// Slicer includes +#include +#include + // SegmentComparison Logic includes #include @@ -61,10 +65,9 @@ qSlicerSegmentComparisonModule::~qSlicerSegmentComparisonModule() = default; //----------------------------------------------------------------------------- QString qSlicerSegmentComparisonModule::helpText()const { - QString help = - "This module computes segment similarity metrics. " - "For more information see %1/Documentation/%2.%3/Modules/SegmentComparison
"; - return help.arg(this->slicerWikiUrl()).arg(Slicer_VERSION_MAJOR).arg(Slicer_VERSION_MINOR); + return QString("This module computes segment similarity metrics. " + "For more information see %1/Documentation/%2.%3/Modules/SegmentComparison
").arg( + this->slicerWikiUrl()).arg(qSlicerCoreApplication::application()->majorVersion()).arg(qSlicerCoreApplication::application()->minorVersion()); } //-----------------------------------------------------------------------------