From 23d1f55c8385af4aeb46b5f1cf310c543ec495e2 Mon Sep 17 00:00:00 2001 From: Csaba Pinter Date: Wed, 17 May 2023 10:27:34 +0100 Subject: [PATCH] ENH: Use FixedReferenceToRAS to keep table top under patient in RoomsEyeView Re #218 --- .../vtkSlicerRoomsEyeViewModuleLogic.cxx | 92 ++++++++++--------- .../Logic/vtkSlicerRoomsEyeViewModuleLogic.h | 3 + .../qSlicerRoomsEyeViewModuleWidget.cxx | 4 + 3 files changed, 58 insertions(+), 41 deletions(-) diff --git a/RoomsEyeView/Logic/vtkSlicerRoomsEyeViewModuleLogic.cxx b/RoomsEyeView/Logic/vtkSlicerRoomsEyeViewModuleLogic.cxx index e50afcfa..4786b764 100644 --- a/RoomsEyeView/Logic/vtkSlicerRoomsEyeViewModuleLogic.cxx +++ b/RoomsEyeView/Logic/vtkSlicerRoomsEyeViewModuleLogic.cxx @@ -955,20 +955,27 @@ bool vtkSlicerRoomsEyeViewModuleLogic::GetPatientBodyPolyData(vtkMRMLRoomsEyeVie } //---------------------------------------------------------------------------- -void vtkSlicerRoomsEyeViewModuleLogic::UpdateCollimatorToGantryTransform(vtkMRMLRoomsEyeViewNode* parameterNode) +void vtkSlicerRoomsEyeViewModuleLogic::UpdateFixedReferenceToRASTransform(vtkMRMLRoomsEyeViewNode* parameterNode) { if (!parameterNode) { - vtkErrorMacro("UpdateFixedReferenceIsocenterToCollimatorRotatedTransform: Invalid parameter set node"); + vtkErrorMacro("UpdateFixedReferenceToRASTransform: Invalid parameter set node"); return; } - vtkMRMLLinearTransformNode* collimatorToGantryTransformNode = - this->IECLogic->GetTransformNodeBetween(vtkSlicerIECTransformLogic::Collimator, vtkSlicerIECTransformLogic::Gantry); + vtkMRMLLinearTransformNode* fixedReferenceToRasTransformNode = + this->IECLogic->GetTransformNodeBetween(vtkSlicerIECTransformLogic::FixedReference, vtkSlicerIECTransformLogic::RAS); - vtkNew collimatorToGantryTransform; - collimatorToGantryTransform->RotateZ(parameterNode->GetCollimatorRotationAngle()); - collimatorToGantryTransformNode->SetAndObserveTransformToParent(collimatorToGantryTransform); + vtkMRMLLinearTransformNode* patientSupportRotationToFixedReferenceTransformNode = + this->IECLogic->GetTransformNodeBetween(vtkSlicerIECTransformLogic::PatientSupportRotation, vtkSlicerIECTransformLogic::FixedReference); + vtkMRMLLinearTransformNode* tableTopToTableTopEccentricRotationTransformNode = + this->IECLogic->GetTransformNodeBetween(vtkSlicerIECTransformLogic::TableTop, vtkSlicerIECTransformLogic::TableTopEccentricRotation); + + vtkNew fixedReferenceToRASTransform; + fixedReferenceToRASTransform->Concatenate(vtkTransform::SafeDownCast(tableTopToTableTopEccentricRotationTransformNode->GetTransformFromParent())); + fixedReferenceToRASTransform->Concatenate(vtkTransform::SafeDownCast(patientSupportRotationToFixedReferenceTransformNode->GetTransformFromParent())); + + fixedReferenceToRasTransformNode->SetAndObserveTransformToParent(fixedReferenceToRASTransform); } //---------------------------------------------------------------------------- @@ -988,6 +995,23 @@ void vtkSlicerRoomsEyeViewModuleLogic::UpdateGantryToFixedReferenceTransform(vtk gantryToFixedReferenceTransformNode->SetAndObserveTransformToParent(gantryToFixedReferenceTransform); } +//---------------------------------------------------------------------------- +void vtkSlicerRoomsEyeViewModuleLogic::UpdateCollimatorToGantryTransform(vtkMRMLRoomsEyeViewNode* parameterNode) +{ + if (!parameterNode) + { + vtkErrorMacro("UpdateFixedReferenceIsocenterToCollimatorRotatedTransform: Invalid parameter set node"); + return; + } + + vtkMRMLLinearTransformNode* collimatorToGantryTransformNode = + this->IECLogic->GetTransformNodeBetween(vtkSlicerIECTransformLogic::Collimator, vtkSlicerIECTransformLogic::Gantry); + + vtkNew collimatorToGantryTransform; + collimatorToGantryTransform->RotateZ(parameterNode->GetCollimatorRotationAngle()); + collimatorToGantryTransformNode->SetAndObserveTransformToParent(collimatorToGantryTransform); +} + //----------------------------------------------------------------------------- void vtkSlicerRoomsEyeViewModuleLogic::UpdateLeftImagingPanelToGantryTransform(vtkMRMLRoomsEyeViewNode* parameterNode) { @@ -1192,52 +1216,38 @@ void vtkSlicerRoomsEyeViewModuleLogic::UpdatePatientSupportToPatientSupportRotat std::string machineType = this->Internal->GetTreatmentMachineFileNameWithoutExtension(parameterNode); vtkMRMLModelNode* patientSupportModel = this->Internal->GetTreatmentMachinePartModelNode(parameterNode, PatientSupport); - if (!patientSupportModel) + vtkMRMLModelNode* tableTopModel = this->Internal->GetTreatmentMachinePartModelNode(parameterNode, TableTop); + if (!patientSupportModel || !patientSupportModel->GetPolyData() || !tableTopModel || !tableTopModel->GetPolyData()) { - vtkErrorMacro("UpdatePatientSupportToPatientSupportRotationTransform: Invalid MRML model node"); + vtkErrorMacro("UpdatePatientSupportToPatientSupportRotationTransform: Failed to access treatment machine part models"); return; } - vtkPolyData* patientSupportModelPolyData = patientSupportModel->GetPolyData(); + + // Get bounds of parts involved in computation double patientSupportModelBounds[6] = { 0, 0, 0, 0, 0, 0 }; - patientSupportModelPolyData->GetBounds(patientSupportModelBounds); + patientSupportModel->GetPolyData()->GetBounds(patientSupportModelBounds); + double tableTopModelBounds[6] = { 0, 0, 0, 0, 0, 0 }; + tableTopModel->GetPolyData()->GetBounds(tableTopModelBounds); // Translation to origin for in-place vertical scaling - vtkNew patientSupportRotationToRasTransform; - double patientSupportTranslationToOrigin[3] = { 0, 0, (-1.0) * patientSupportModelBounds[4]}; //TODO: Subtract [1]? - patientSupportRotationToRasTransform->Translate(patientSupportTranslationToOrigin); + vtkNew patientSupportScalingTransform; + patientSupportScalingTransform->PostMultiply(); + double patientSupportTranslationToOrigin[3] = { 0, 0, (-1.0) * patientSupportModelBounds[4]}; + patientSupportScalingTransform->Translate(patientSupportTranslationToOrigin); - // Vertical scaling + // Apply patient support vertical scale double tableTopDisplacement = parameterNode->GetVerticalTableTopDisplacement(); - double tableTopDisplacementScaling = 1.0; - //TODO: Support this from the descriptor JSON file - //char* treatmentMachineType = parameterNode->GetTreatmentMachineType(); - //if (treatmentMachineType && !strcmp(treatmentMachineType, "VarianTrueBeamSTx")) - //{ - tableTopDisplacementScaling = 0.525; - //} - //else if (treatmentMachineType && !strcmp(treatmentMachineType, "SiemensArtiste")) - //{ - // tableTopDisplacementScaling = 0.095; - //} - vtkNew rasToScaledRasTransform; - rasToScaledRasTransform->Scale(1, 1, - ( ( fabs(patientSupportModelBounds[5]) + tableTopDisplacement*tableTopDisplacementScaling) - / fabs(patientSupportModelBounds[5]) ) ); //TODO: Subtract [2]? + double newPatientSupportHeight = /*tableTopModelBounds[4] + */patientSupportModelBounds[5] + tableTopDisplacement - patientSupportModelBounds[4]; + double originalPatientSupportHeight = patientSupportModelBounds[5] - patientSupportModelBounds[4]; + patientSupportScalingTransform->Scale(1.0, 1.0, newPatientSupportHeight / originalPatientSupportHeight); - // Translation back from origin after in-place scaling - vtkNew scaledRasToFixedReferenceTransform; - double patientSupportTranslationFromOrigin[3] = { 0, 0, patientSupportModelBounds[4] }; //TODO: Subtract [1]? - scaledRasToFixedReferenceTransform->Translate(patientSupportTranslationFromOrigin); + // Translate back so that the patient support base is at the same height + double patientSupportTranslationFromOrigin[3] = { 0, 0, patientSupportModelBounds[4]}; + patientSupportScalingTransform->Translate(patientSupportTranslationFromOrigin); - // Assemble transform and update node vtkMRMLLinearTransformNode* patientSupportToPatientSupportRotationTransformNode = this->IECLogic->GetTransformNodeBetween(vtkSlicerIECTransformLogic::PatientSupport, vtkSlicerIECTransformLogic::PatientSupportRotation); - vtkNew patientSupportToFixedReferenceTransform; - patientSupportToFixedReferenceTransform->PostMultiply(); - patientSupportToFixedReferenceTransform->Concatenate(patientSupportRotationToRasTransform); - patientSupportToFixedReferenceTransform->Concatenate(rasToScaledRasTransform); - patientSupportToFixedReferenceTransform->Concatenate(scaledRasToFixedReferenceTransform); - patientSupportToPatientSupportRotationTransformNode->SetAndObserveTransformToParent(patientSupportToFixedReferenceTransform); + patientSupportToPatientSupportRotationTransformNode->SetAndObserveTransformToParent(patientSupportScalingTransform); } //----------------------------------------------------------------------------- diff --git a/RoomsEyeView/Logic/vtkSlicerRoomsEyeViewModuleLogic.h b/RoomsEyeView/Logic/vtkSlicerRoomsEyeViewModuleLogic.h index bd1f7c9e..455755e3 100644 --- a/RoomsEyeView/Logic/vtkSlicerRoomsEyeViewModuleLogic.h +++ b/RoomsEyeView/Logic/vtkSlicerRoomsEyeViewModuleLogic.h @@ -78,6 +78,9 @@ 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 FixedReferenceToRAS transform, making sure the patient stays fixed on the table top + void UpdateFixedReferenceToRASTransform(vtkMRMLRoomsEyeViewNode* parameterNode); + /// Update GantryToFixedReference transform based on gantry angle from UI slider void UpdateGantryToFixedReferenceTransform(vtkMRMLRoomsEyeViewNode* parameterNode); diff --git a/RoomsEyeView/qSlicerRoomsEyeViewModuleWidget.cxx b/RoomsEyeView/qSlicerRoomsEyeViewModuleWidget.cxx index 79e2fffd..1f3507b8 100644 --- a/RoomsEyeView/qSlicerRoomsEyeViewModuleWidget.cxx +++ b/RoomsEyeView/qSlicerRoomsEyeViewModuleWidget.cxx @@ -589,6 +589,7 @@ void qSlicerRoomsEyeViewModuleWidget::onPatientSupportRotationSliderValueChanged // Update IEC transform d->logic()->UpdatePatientSupportRotationToFixedReferenceTransform(paramNode); + d->logic()->UpdateFixedReferenceToRASTransform(paramNode); // Update beam parameter vtkMRMLRTBeamNode* beamNode = vtkMRMLRTBeamNode::SafeDownCast(paramNode->GetBeamNode()); @@ -618,6 +619,7 @@ void qSlicerRoomsEyeViewModuleWidget::onVerticalTableTopDisplacementSliderValueC d->logic()->UpdatePatientSupportToPatientSupportRotationTransform(paramNode); d->logic()->UpdateTableTopToTableTopEccentricRotationTransform(paramNode); + d->logic()->UpdateFixedReferenceToRASTransform(paramNode); this->checkForCollisions(); this->updateTreatmentOrientationMarker(); @@ -639,6 +641,7 @@ void qSlicerRoomsEyeViewModuleWidget::onLongitudinalTableTopDisplacementSliderVa paramNode->DisableModifiedEventOff(); d->logic()->UpdateTableTopToTableTopEccentricRotationTransform(paramNode); + d->logic()->UpdateFixedReferenceToRASTransform(paramNode); this->checkForCollisions(); this->updateTreatmentOrientationMarker(); @@ -662,6 +665,7 @@ void qSlicerRoomsEyeViewModuleWidget::onLateralTableTopDisplacementSliderValueCh paramNode->DisableModifiedEventOff(); d->logic()->UpdateTableTopToTableTopEccentricRotationTransform(paramNode); + d->logic()->UpdateFixedReferenceToRASTransform(paramNode); this->checkForCollisions(); this->updateTreatmentOrientationMarker();