-
Notifications
You must be signed in to change notification settings - Fork 60
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ENH: Initial support of the arc beam sequence #215
base: master
Are you sure you want to change the base?
Changes from all commits
0f7fcf6
f926e2a
36f5b73
c560fab
e9b6e98
1ea1d22
5248e63
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -27,7 +27,20 @@ | |
#include "vtkMRMLRTPlanNode.h" | ||
#include "vtkMRMLRTBeamNode.h" | ||
|
||
#include <vtkSlicerRtCommon.h> | ||
|
||
// SubjectHierarchy includes | ||
#include "vtkMRMLSubjectHierarchyConstants.h" | ||
#include "vtkMRMLSubjectHierarchyNode.h" | ||
#include "vtkSlicerSubjectHierarchyModuleLogic.h" | ||
|
||
// Sequences inludes | ||
#include <vtkMRMLSequenceBrowserNode.h> | ||
#include <vtkMRMLSequenceNode.h> | ||
|
||
// MRML includes | ||
#include <vtkMRMLModelDisplayNode.h> | ||
#include <vtkMRMLModelHierarchyNode.h> | ||
#include <vtkMRMLScene.h> | ||
#include <vtkMRMLLinearTransformNode.h> | ||
#include <vtkMRMLMarkupsFiducialNode.h> | ||
|
@@ -299,3 +312,263 @@ void vtkSlicerBeamsModuleLogic::ProcessMRMLNodesEvents(vtkObject* caller, unsign | |
} | ||
} | ||
} | ||
|
||
//---------------------------------------------------------------------------- | ||
bool vtkSlicerBeamsModuleLogic::CreateArcBeamDynamicSequence( | ||
double initialAngle, double finalAngle, double stepAngle, bool direction, | ||
vtkMRMLRTPlanNode* planNode, vtkMRMLSequenceBrowserNode* beamSequenceBrowserNode, | ||
vtkMRMLSequenceNode* beamSequenceNode, vtkMRMLSequenceNode* transformSequenceNode) | ||
{ | ||
std::vector<double> 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 += 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.) | ||
{ | ||
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); | ||
} | ||
} | ||
*/ | ||
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); | ||
if (!shNode) | ||
{ | ||
vtkErrorMacro("CreateArcBeamDynamicSequence: Failed to access subject hierarchy node"); | ||
return false; | ||
} | ||
vtkSmartPointer<vtkMRMLModelHierarchyNode> beamModelHierarchyRootNode; | ||
|
||
const char* beamName = "Beam [ArcDelivery]"; | ||
// 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<vtkMRMLRTBeamNode> beamNode = vtkSmartPointer<vtkMRMLRTBeamNode>::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)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why are these commented out? If you want to keep them there please add a |
||
// 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)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it some new convention that there is a space after the opening parentheses but none before the closing one? If there is on good reason for this please use the style that is used everywhere else in SlicerRT. |
||
} | ||
|
||
// 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; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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,20 @@ 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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please use the docstring convention that is used everywhere in Slicer and do not introduce another one. Not just here but everywhere and in the other PR as well please. |
||
/// @param finalAngle - final angle in degrees | ||
/// @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, | ||
double angleStep, bool direction, vtkMRMLRTPlanNode* planNode, | ||
vtkMRMLSequenceBrowserNode* beamSequenceBrowserNode, | ||
vtkMRMLSequenceNode* beamSequenceNode, vtkMRMLSequenceNode* transformSequenceNode); | ||
|
||
protected: | ||
vtkSlicerBeamsModuleLogic(); | ||
~vtkSlicerBeamsModuleLogic() override; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
scene->GetSubjectHierarchyNode()
instead please