Skip to content

Commit

Permalink
ENH: Beam transform now follows full IEC; Rationalize code
Browse files Browse the repository at this point in the history
Re #218
  • Loading branch information
cpinter committed Oct 9, 2023
1 parent dfbeac5 commit aa05089
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 96 deletions.
125 changes: 52 additions & 73 deletions Beams/Logic/vtkSlicerIECTransformLogic.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -199,47 +199,17 @@ void vtkSlicerIECTransformLogic::UpdateBeamTransform(vtkMRMLRTBeamNode* beamNode
return;
}

// Update transforms in IEC logic from beam node parameters
this->UpdateIECTransformsFromBeam(beamNode);

// Dynamic transform from Collimator to RAS
// Transformation path:
// Collimator -> Gantry -> FixedReference -> PatientSupport -> TableTopEccentricRotation -> TableTop -> Patient -> RAS
vtkSmartPointer<vtkGeneralTransform> beamGeneralTransform = vtkSmartPointer<vtkGeneralTransform>::New();
if (this->GetTransformBetween(Collimator, RAS, beamGeneralTransform))
{
// Convert general transform to linear
// This call also makes hard copy of the transform so that it doesn't change when other beam transforms change
vtkSmartPointer<vtkTransform> beamLinearTransform = vtkSmartPointer<vtkTransform>::New();
if (!vtkMRMLTransformNode::IsGeneralTransformLinear(beamGeneralTransform, beamLinearTransform))
{
vtkErrorMacro("UpdateBeamTransform: Unable to set transform with non-linear components to beam " << beamNode->GetName());
return;
}

// Set transform to beam node
beamTransformNode->SetAndObserveTransformToParent(beamLinearTransform);

// Update the name of the transform node too
// (the user may have renamed the beam, but it's very expensive to update the transform name on every beam modified event)
std::string transformName = std::string(beamNode->GetName()) + vtkMRMLRTBeamNode::BEAM_TRANSFORM_NODE_NAME_POSTFIX;
}
this->UpdateBeamTransform(beamNode, beamTransformNode);
}

//-----------------------------------------------------------------------------
void vtkSlicerIECTransformLogic::UpdateBeamTransform(vtkMRMLRTBeamNode* beamNode, vtkMRMLLinearTransformNode* beamTransformNode, double* isocenter)
void vtkSlicerIECTransformLogic::UpdateBeamTransform(vtkMRMLRTBeamNode* beamNode, vtkMRMLLinearTransformNode* beamTransformNode, double* isocenter/*=nullptr*/)
{
//TODO: Observe beam node's geometry modified event (vtkMRMLRTBeamNode::BeamGeometryModified)
// and its parent plan's POI markups fiducial's point modified event (vtkMRMLMarkupsNode::PointModifiedEvent)
// so that UpdateTransformsFromBeamGeometry is called. It may be needed to change the signature of the
// update function. It may be also needed to store a reference to the beam node (see defined nodes in SlicerRT)

if (!beamNode)
{
vtkErrorMacro("UpdateBeamTransform: Invalid beam node");
return;
}

if (!beamTransformNode)
{
vtkErrorMacro("UpdateBeamTransform: Invalid beam transform node");
Expand All @@ -249,28 +219,22 @@ void vtkSlicerIECTransformLogic::UpdateBeamTransform(vtkMRMLRTBeamNode* beamNode
// Update transforms in IEC logic from beam node parameters
this->UpdateIECTransformsFromBeam(beamNode, isocenter);

// Dynamic transform from Collimator to RAS
// Transformation path:
// Collimator -> Gantry -> FixedReference -> PatientSupport -> TableTopEccentricRotation -> TableTop -> Patient -> RAS
vtkSmartPointer<vtkGeneralTransform> beamGeneralTransform = vtkSmartPointer<vtkGeneralTransform>::New();
if (this->GetTransformBetween(Collimator, RAS, beamGeneralTransform))
{
// Convert general transform to linear
// This call also makes hard copy of the transform so that it doesn't change when other beam transforms change
vtkSmartPointer<vtkTransform> beamLinearTransform = vtkSmartPointer<vtkTransform>::New();
if (!vtkMRMLTransformNode::IsGeneralTransformLinear(beamGeneralTransform, beamLinearTransform))
{
vtkErrorMacro("UpdateBeamTransform: Unable to set transform with non-linear components to beam " << beamNode->GetName());
return;
}

// Set transform to beam node
beamTransformNode->SetAndObserveTransformToParent(beamLinearTransform);
// Dynamic transform from Collimator to World
vtkMRMLLinearTransformNode* collimatorToGantryTransformNode = this->GetTransformNodeBetween(Collimator, Gantry);
vtkNew<vtkGeneralTransform> beamGeneralTransform;
collimatorToGantryTransformNode->GetTransformToWorld(beamGeneralTransform);

// Update the name of the transform node too
// (the user may have renamed the beam, but it's very expensive to update the transform name on every beam modified event)
std::string transformName = std::string(beamNode->GetName()) + vtkMRMLRTBeamNode::BEAM_TRANSFORM_NODE_NAME_POSTFIX;
// Convert general transform to linear
// This call also makes hard copy of the transform so that it doesn't change when other beam transforms change
vtkNew<vtkTransform> beamLinearTransform;
if (!vtkMRMLTransformNode::IsGeneralTransformLinear(beamGeneralTransform, beamLinearTransform))
{
vtkErrorMacro("UpdateBeamTransform: Unable to set transform with non-linear components to beam " << beamNode->GetName());
return;
}

// Set transform to beam node
beamTransformNode->SetAndObserveTransformToParent(beamLinearTransform);
}

//-----------------------------------------------------------------------------
Expand All @@ -294,27 +258,9 @@ void vtkSlicerIECTransformLogic::UpdateIECTransformsFromBeam(vtkMRMLRTBeamNode*
// Make sure the transform hierarchy is set up
this->BuildIECTransformHierarchy();

//TODO: Code duplication (RevLogic::Update...)
vtkMRMLLinearTransformNode* gantryToFixedReferenceTransformNode =
this->GetTransformNodeBetween(Gantry, FixedReference);
vtkTransform* gantryToFixedReferenceTransform = vtkTransform::SafeDownCast(gantryToFixedReferenceTransformNode->GetTransformToParent());
gantryToFixedReferenceTransform->Identity();
gantryToFixedReferenceTransform->RotateY(beamNode->GetGantryAngle());
gantryToFixedReferenceTransform->Modified();

vtkMRMLLinearTransformNode* collimatorToGantryTransformNode =
this->GetTransformNodeBetween(Collimator, Gantry);
vtkTransform* collimatorToGantryTransform = vtkTransform::SafeDownCast(collimatorToGantryTransformNode->GetTransformToParent());
collimatorToGantryTransform->Identity();
collimatorToGantryTransform->RotateZ(beamNode->GetCollimatorAngle());
collimatorToGantryTransform->Modified();

vtkMRMLLinearTransformNode* patientSupportRotationToFixedReferenceTransformNode =
this->GetTransformNodeBetween(PatientSupportRotation, FixedReference);
vtkTransform* patientSupportToFixedReferenceTransform = vtkTransform::SafeDownCast(patientSupportRotationToFixedReferenceTransformNode->GetTransformToParent());
patientSupportToFixedReferenceTransform->Identity();
patientSupportToFixedReferenceTransform->RotateZ(-1. * beamNode->GetCouchAngle());
patientSupportToFixedReferenceTransform->Modified();
this->UpdateGantryToFixedReferenceTransform(beamNode->GetGantryAngle());
this->UpdateCollimatorToGantryTransform(beamNode->GetCollimatorAngle());
this->UpdatePatientSupportRotationToFixedReferenceTransform(-1.0 * beamNode->GetCouchAngle());

// Update IEC Patient to RAS transform based on the isocenter defined in the beam's parent plan
vtkMRMLLinearTransformNode* rasToPatientReferenceTransformNode =
Expand Down Expand Up @@ -407,6 +353,39 @@ void vtkSlicerIECTransformLogic::UpdateFixedReferenceToRASTransform(vtkMRMLRTPla
fixedReferenceToRasTransformNode->SetAndObserveTransformToParent(fixedReferenceToRASTransform);
}

//----------------------------------------------------------------------------
void vtkSlicerIECTransformLogic::UpdateGantryToFixedReferenceTransform(double gantryRotationAngleDeg)
{
vtkMRMLLinearTransformNode* gantryToFixedReferenceTransformNode =
this->GetTransformNodeBetween(Gantry, FixedReference);

vtkNew<vtkTransform> gantryToFixedReferenceTransform;
gantryToFixedReferenceTransform->RotateY(gantryRotationAngleDeg);
gantryToFixedReferenceTransformNode->SetAndObserveTransformToParent(gantryToFixedReferenceTransform);
}

//----------------------------------------------------------------------------
void vtkSlicerIECTransformLogic::UpdateCollimatorToGantryTransform(double collimatorRotationAngleDeg)
{
vtkMRMLLinearTransformNode* collimatorToGantryTransformNode =
this->GetTransformNodeBetween(Collimator, Gantry);

vtkNew<vtkTransform> collimatorToGantryTransform;
collimatorToGantryTransform->RotateZ(collimatorRotationAngleDeg);
collimatorToGantryTransformNode->SetAndObserveTransformToParent(collimatorToGantryTransform);
}

//-----------------------------------------------------------------------------
void vtkSlicerIECTransformLogic::UpdatePatientSupportRotationToFixedReferenceTransform(double patientSupportRotationAngleDeg)
{
vtkMRMLLinearTransformNode* patientSupportRotationToFixedReferenceTransformNode =
this->GetTransformNodeBetween(PatientSupportRotation, FixedReference);

vtkNew<vtkTransform> patientSupportToRotatedPatientSupportTransform;
patientSupportToRotatedPatientSupportTransform->RotateZ(patientSupportRotationAngleDeg);
patientSupportRotationToFixedReferenceTransformNode->SetAndObserveTransformToParent(patientSupportToRotatedPatientSupportTransform);
}

//-----------------------------------------------------------------------------
std::string vtkSlicerIECTransformLogic::GetTransformNodeNameBetween(
CoordinateSystemIdentifier fromFrame, CoordinateSystemIdentifier toFrame)
Expand Down
11 changes: 9 additions & 2 deletions Beams/Logic/vtkSlicerIECTransformLogic.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ class VTK_SLICER_BEAMS_LOGIC_EXPORT vtkSlicerIECTransformLogic : public vtkMRMLA
/// @param toFrame - proceed transformation to frame
/// @param outputTransform - General (linear) transform matrix fromFrame -> toFrame. Matrix is correct if return flag is true.
/// @param transformForBeam - calculate dynamic transformation for beam model or other models
/// (e.g. transformation from Patient RAS frame to Collimation frame: RAS -> Patient -> TableTop -> Eccentric -> Patient Support -> Fixed reference -> Gantry -> Collimator)
/// (e.g. transformation from Patient RAS frame to Collimation frame: RAS -> Patient -> TableTop -> Eccentric -> Patient Support -> Fixed reference -> Gantry -> Collimator) //TODO: Deprecated
/// \return Success flag (false on any error)
bool GetTransformBetween(CoordinateSystemIdentifier fromFrame, CoordinateSystemIdentifier toFrame,
vtkGeneralTransform* outputTransform, bool transformForBeam = true);
Expand All @@ -144,14 +144,21 @@ class VTK_SLICER_BEAMS_LOGIC_EXPORT vtkSlicerIECTransformLogic : public vtkMRMLA
void UpdateBeamTransform(vtkMRMLRTBeamNode* beamNode);
/// Update parent transform node of a given beam from the IEC transform hierarchy and the beam parameters
/// \warning This method is used only in vtkSlicerBeamsModuleLogic::UpdateTransformForBeam
void UpdateBeamTransform(vtkMRMLRTBeamNode* beamNode, vtkMRMLLinearTransformNode* beamTransformNode, double* isocenter);
void UpdateBeamTransform(vtkMRMLRTBeamNode* beamNode, vtkMRMLLinearTransformNode* beamTransformNode, double* isocenter=nullptr);

/// Update IEC transforms according to beam node
void UpdateIECTransformsFromBeam(vtkMRMLRTBeamNode* beamNode, double* isocenter = nullptr);

/// Update fixed reference to RAS transform based on isocenter and patient support transforms
void UpdateFixedReferenceToRASTransform(vtkMRMLRTPlanNode* planNode, double* isocenter = nullptr);

/// Update GantryToFixedReference transform based on gantry angle parameter
void UpdateGantryToFixedReferenceTransform(double gantryRotationAngleDeg);
/// Update CollimatorToGantry transform based on collimator angle parameter
void UpdateCollimatorToGantryTransform(double collimatorRotationAngleDeg);
/// Update PatientSupportRotrationToFixedReference transform based on patient support rotation parameter
void UpdatePatientSupportRotationToFixedReferenceTransform(double patientSupportRotationAngleDeg);

protected:
/// Get name of transform node between two coordinate systems
/// \return Transform node name between the specified coordinate frames.
Expand Down
22 changes: 3 additions & 19 deletions RoomsEyeView/Logic/vtkSlicerRoomsEyeViewModuleLogic.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -968,12 +968,7 @@ void vtkSlicerRoomsEyeViewModuleLogic::UpdateGantryToFixedReferenceTransform(vtk
return;
}

vtkMRMLLinearTransformNode* gantryToFixedReferenceTransformNode =
this->IECLogic->GetTransformNodeBetween(vtkSlicerIECTransformLogic::Gantry, vtkSlicerIECTransformLogic::FixedReference);

vtkNew<vtkTransform> gantryToFixedReferenceTransform;
gantryToFixedReferenceTransform->RotateY(parameterNode->GetGantryRotationAngle());
gantryToFixedReferenceTransformNode->SetAndObserveTransformToParent(gantryToFixedReferenceTransform);
this->IECLogic->UpdateGantryToFixedReferenceTransform(parameterNode->GetGantryRotationAngle());
}

//----------------------------------------------------------------------------
Expand All @@ -985,12 +980,7 @@ void vtkSlicerRoomsEyeViewModuleLogic::UpdateCollimatorToGantryTransform(vtkMRML
return;
}

vtkMRMLLinearTransformNode* collimatorToGantryTransformNode =
this->IECLogic->GetTransformNodeBetween(vtkSlicerIECTransformLogic::Collimator, vtkSlicerIECTransformLogic::Gantry);

vtkNew<vtkTransform> collimatorToGantryTransform;
collimatorToGantryTransform->RotateZ(parameterNode->GetCollimatorRotationAngle());
collimatorToGantryTransformNode->SetAndObserveTransformToParent(collimatorToGantryTransform);
this->IECLogic->UpdateCollimatorToGantryTransform(parameterNode->GetCollimatorRotationAngle());
}

//-----------------------------------------------------------------------------
Expand Down Expand Up @@ -1171,13 +1161,7 @@ void vtkSlicerRoomsEyeViewModuleLogic::UpdatePatientSupportRotationToFixedRefere
return;
}

vtkMRMLLinearTransformNode* patientSupportRotationToFixedReferenceTransformNode =
this->IECLogic->GetTransformNodeBetween(vtkSlicerIECTransformLogic::PatientSupportRotation, vtkSlicerIECTransformLogic::FixedReference);

double rotationAngle = parameterNode->GetPatientSupportRotationAngle();
vtkNew<vtkTransform> patientSupportToRotatedPatientSupportTransform;
patientSupportToRotatedPatientSupportTransform->RotateZ(rotationAngle);
patientSupportRotationToFixedReferenceTransformNode->SetAndObserveTransformToParent(patientSupportToRotatedPatientSupportTransform);
this->IECLogic->UpdatePatientSupportRotationToFixedReferenceTransform(parameterNode->GetPatientSupportRotationAngle());
}

//-----------------------------------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion RoomsEyeView/Logic/vtkSlicerRoomsEyeViewModuleLogic.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ class VTK_SLICER_ROOMSEYEVIEW_LOGIC_EXPORT vtkSlicerRoomsEyeViewModuleLogic :
/// Create or get transforms taking part in the IEC logic and additional devices, and build the transform hierarchy
void BuildRoomsEyeViewTransformHierarchy();

/// Update GantryToFixedReference transform based on gantry angle from UI slider
/// Update GantryToFixedReference transform based on gantry angle parameter
void UpdateGantryToFixedReferenceTransform(vtkMRMLRoomsEyeViewNode* parameterNode);

/// Update CollimatorToGantry transform based on collimator angle parameter
Expand Down
6 changes: 5 additions & 1 deletion RoomsEyeView/Resources/UI/qSlicerRoomsEyeViewModule.ui
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,11 @@
</widget>
</item>
<item row="0" column="1">
<widget class="qMRMLSegmentSelectorWidget" name="SegmentSelectorWidget_PatientBody"/>
<widget class="qMRMLSegmentSelectorWidget" name="SegmentSelectorWidget_PatientBody">
<property name="noneEnabled">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
Expand Down

0 comments on commit aa05089

Please sign in to comment.