From 9df83163e2283201a4575acf4b7ba1a555ae799c Mon Sep 17 00:00:00 2001 From: Csaba Pinter Date: Sat, 11 Feb 2023 21:37:17 +0000 Subject: [PATCH] ENH: Apply treatment machine part color from JSON descriptor file Re #218 --- .../vtkSlicerRoomsEyeViewModuleLogic.cxx | 220 ++++++++---------- .../Logic/vtkSlicerRoomsEyeViewModuleLogic.h | 9 +- .../VarianTrueBeamSTx/VarianTrueBeamSTx.json | 28 +-- 3 files changed, 120 insertions(+), 137 deletions(-) diff --git a/RoomsEyeView/Logic/vtkSlicerRoomsEyeViewModuleLogic.cxx b/RoomsEyeView/Logic/vtkSlicerRoomsEyeViewModuleLogic.cxx index 1a738fcd..ebbb85d7 100644 --- a/RoomsEyeView/Logic/vtkSlicerRoomsEyeViewModuleLogic.cxx +++ b/RoomsEyeView/Logic/vtkSlicerRoomsEyeViewModuleLogic.cxx @@ -53,6 +53,7 @@ #include #include #include +#include // VTKSYS includes #include @@ -140,8 +141,7 @@ rapidjson::Value& vtkSlicerRoomsEyeViewModuleLogic::vtkInternal::GetTreatmentMac rapidjson::Value& partsArray = partsIt->value; // Traverse parts and try to find the element with the given part type - rapidjson::SizeType index = 0; - while (index < partsArray.Size()) + for (rapidjson::SizeType index=0; index < partsArray.Size(); ++index) { rapidjson::Value& currentObject = partsArray[index]; if (currentObject.IsObject()) @@ -152,7 +152,6 @@ rapidjson::Value& vtkSlicerRoomsEyeViewModuleLogic::vtkInternal::GetTreatmentMac return currentObject; } } - ++index; } // Not found @@ -488,7 +487,6 @@ void vtkSlicerRoomsEyeViewModuleLogic::LoadTreatmentMachine(vtkMRMLRoomsEyeViewN std::string subjectHierarchyFolderName = machineType + std::string("_Components"); vtkIdType rootFolderItem = shNode->CreateFolderItem(shNode->GetSceneItemID(), subjectHierarchyFolderName); - // // Load treatment machine models // Collimator - mandatory @@ -522,125 +520,93 @@ void vtkSlicerRoomsEyeViewModuleLogic::SetupTreatmentMachineModels(vtkMRMLRoomsE return; } - //TODO: Store treatment machine component color and other properties in JSON - - // Display all pieces of the treatment room and sets each piece a color to provide realistic representation - - // Collimator - mandatory - vtkMRMLModelNode* collimatorModel = this->Internal->GetTreatmentMachinePartModelNode(parameterNode, Collimator); - if (!collimatorModel) - { - vtkErrorMacro("SetupTreatmentMachineModels: Unable to access collimator model"); - return; - } - vtkMRMLLinearTransformNode* collimatorToGantryTransformNode = - this->IECLogic->GetTransformNodeBetween(vtkSlicerIECTransformLogic::Collimator, vtkSlicerIECTransformLogic::Gantry); - collimatorModel->SetAndObserveTransformNodeID(collimatorToGantryTransformNode->GetID()); - collimatorModel->CreateDefaultDisplayNodes(); - collimatorModel->GetDisplayNode()->SetColor(0.7, 0.7, 0.95); - - // Gantry - mandatory - vtkMRMLModelNode* gantryModel = this->Internal->GetTreatmentMachinePartModelNode(parameterNode, Gantry); - if (!gantryModel) - { - vtkErrorMacro("SetupTreatmentMachineModels: Unable to access gantry model"); - return; - } - vtkMRMLLinearTransformNode* gantryToFixedReferenceTransformNode = - this->IECLogic->GetTransformNodeBetween(vtkSlicerIECTransformLogic::Gantry, vtkSlicerIECTransformLogic::FixedReference); - gantryModel->SetAndObserveTransformNodeID(gantryToFixedReferenceTransformNode->GetID()); - gantryModel->CreateDefaultDisplayNodes(); - gantryModel->GetDisplayNode()->SetColor(0.95, 0.95, 0.95); - - // Patient support - mandatory - vtkMRMLModelNode* patientSupportModel = this->Internal->GetTreatmentMachinePartModelNode(parameterNode, PatientSupport); - if (!patientSupportModel) - { - vtkErrorMacro("SetupTreatmentMachineModels: Unable to access patient support model"); - return; - } - vtkMRMLLinearTransformNode* patientSupportToPatientSupportRotationTransformNode = - this->IECLogic->GetTransformNodeBetween(vtkSlicerIECTransformLogic::PatientSupport, vtkSlicerIECTransformLogic::PatientSupportRotation); - patientSupportModel->SetAndObserveTransformNodeID(patientSupportToPatientSupportRotationTransformNode->GetID()); - patientSupportModel->CreateDefaultDisplayNodes(); - patientSupportModel->GetDisplayNode()->SetColor(0.85, 0.85, 0.85); - - // Table top - mandatory - vtkMRMLModelNode* tableTopModel = this->Internal->GetTreatmentMachinePartModelNode(parameterNode, TableTop); - if (!tableTopModel) - { - vtkErrorMacro("SetupTreatmentMachineModels: Unable to access table top model"); - return; - } - vtkMRMLLinearTransformNode* tableTopToTableTopEccentricRotationTransformNode = - this->IECLogic->GetTransformNodeBetween(vtkSlicerIECTransformLogic::TableTop, vtkSlicerIECTransformLogic::TableTopEccentricRotation); - tableTopModel->SetAndObserveTransformNodeID(tableTopToTableTopEccentricRotationTransformNode->GetID()); - tableTopModel->CreateDefaultDisplayNodes(); - tableTopModel->GetDisplayNode()->SetColor(0, 0, 0); - - // Linac body - optional - vtkMRMLModelNode* linacBodyModel = this->Internal->GetTreatmentMachinePartModelNode(parameterNode, Body); - if (linacBodyModel) + for (int partIdx=0; partIdxIECLogic->GetTransformNodeBetween(vtkSlicerIECTransformLogic::FixedReference, vtkSlicerIECTransformLogic::RAS); - linacBodyModel->SetAndObserveTransformNodeID(fixedReferenceToRasTransformNode->GetID()); - linacBodyModel->CreateDefaultDisplayNodes(); - linacBodyModel->GetDisplayNode()->SetColor(0.9, 0.9, 0.9); - } + std::string partType = this->GetTreatmentMachinePartTypeAsString((TreatmentMachinePartType)partIdx); + vtkMRMLModelNode* partModel = this->Internal->GetTreatmentMachinePartModelNode(parameterNode, (TreatmentMachinePartType)partIdx); + if (!partModel) + { + switch (partIdx) + { + case Collimator: case Gantry: case PatientSupport: case TableTop: + vtkErrorMacro("SetupTreatmentMachineModels: Unable to access " << partType << " model"); + } + continue; + } - // Imaging panel left - optional - vtkMRMLModelNode* leftImagingPanelModel = this->Internal->GetTreatmentMachinePartModelNode(parameterNode, ImagingPanelLeft); - if (leftImagingPanelModel) - { - vtkMRMLLinearTransformNode* leftImagingPanelToGantryTransformNode = - this->IECLogic->GetTransformNodeBetween(vtkSlicerIECTransformLogic::LeftImagingPanel, vtkSlicerIECTransformLogic::Gantry); - leftImagingPanelModel->SetAndObserveTransformNodeID(leftImagingPanelToGantryTransformNode->GetID()); - leftImagingPanelModel->CreateDefaultDisplayNodes(); - leftImagingPanelModel->GetDisplayNode()->SetColor(0.95, 0.95, 0.95); - } + // Set color + vtkVector3d partColor(this->GetColorForPartType(partType)); + partModel->CreateDefaultDisplayNodes(); + partModel->GetDisplayNode()->SetColor((double)partColor[0] / 255.0, (double)partColor[1] / 255.0, (double)partColor[2] / 255.0); - // Imaging panel right - optional - vtkMRMLModelNode* rightImagingPanelModel = this->Internal->GetTreatmentMachinePartModelNode(parameterNode, ImagingPanelRight); - if (rightImagingPanelModel) - { - vtkMRMLLinearTransformNode* rightImagingPanelToGantryTransformNode = - this->IECLogic->GetTransformNodeBetween(vtkSlicerIECTransformLogic::RightImagingPanel, vtkSlicerIECTransformLogic::Gantry); - rightImagingPanelModel->SetAndObserveTransformNodeID(rightImagingPanelToGantryTransformNode->GetID()); - rightImagingPanelModel->CreateDefaultDisplayNodes(); - rightImagingPanelModel->GetDisplayNode()->SetColor(0.95, 0.95, 0.95); - } - - // Flat panel - optional - vtkMRMLModelNode* flatPanelModel = this->Internal->GetTreatmentMachinePartModelNode(parameterNode, FlatPanel); - if (flatPanelModel) - { - vtkMRMLLinearTransformNode* flatPanelToGantryTransformNode = - this->IECLogic->GetTransformNodeBetween(vtkSlicerIECTransformLogic::FlatPanel, vtkSlicerIECTransformLogic::Gantry); - flatPanelModel->SetAndObserveTransformNodeID(flatPanelToGantryTransformNode->GetID()); - flatPanelModel->CreateDefaultDisplayNodes(); - flatPanelModel->GetDisplayNode()->SetColor(0.95, 0.95, 0.95); + // Setup transforms and collision detection + if (partIdx == Collimator) + { + vtkMRMLLinearTransformNode* collimatorToGantryTransformNode = + this->IECLogic->GetTransformNodeBetween(vtkSlicerIECTransformLogic::Collimator, vtkSlicerIECTransformLogic::Gantry); + partModel->SetAndObserveTransformNodeID(collimatorToGantryTransformNode->GetID()); + this->CollimatorTableTopCollisionDetection->SetInputData(0, partModel->GetPolyData()); + // Patient model is set when calculating collisions, as it can be changed dynamically + this->CollimatorPatientCollisionDetection->SetInputData(0, partModel->GetPolyData()); + } + else if (partIdx == Gantry) + { + vtkMRMLLinearTransformNode* gantryToFixedReferenceTransformNode = + this->IECLogic->GetTransformNodeBetween(vtkSlicerIECTransformLogic::Gantry, vtkSlicerIECTransformLogic::FixedReference); + partModel->SetAndObserveTransformNodeID(gantryToFixedReferenceTransformNode->GetID()); + this->GantryTableTopCollisionDetection->SetInputData(0, partModel->GetPolyData()); + this->GantryPatientSupportCollisionDetection->SetInputData(0, partModel->GetPolyData()); + // Patient model is set when calculating collisions, as it can be changed dynamically + this->GantryPatientCollisionDetection->SetInputData(0, partModel->GetPolyData()); + } + else if (partIdx == PatientSupport) + { + vtkMRMLLinearTransformNode* patientSupportToPatientSupportRotationTransformNode = + this->IECLogic->GetTransformNodeBetween(vtkSlicerIECTransformLogic::PatientSupport, vtkSlicerIECTransformLogic::PatientSupportRotation); + partModel->SetAndObserveTransformNodeID(patientSupportToPatientSupportRotationTransformNode->GetID()); + this->GantryPatientSupportCollisionDetection->SetInputData(1, partModel->GetPolyData()); + } + else if (partIdx == TableTop) + { + vtkMRMLLinearTransformNode* tableTopToTableTopEccentricRotationTransformNode = + this->IECLogic->GetTransformNodeBetween(vtkSlicerIECTransformLogic::TableTop, vtkSlicerIECTransformLogic::TableTopEccentricRotation); + partModel->SetAndObserveTransformNodeID(tableTopToTableTopEccentricRotationTransformNode->GetID()); + this->GantryTableTopCollisionDetection->SetInputData(1, partModel->GetPolyData()); + this->CollimatorTableTopCollisionDetection->SetInputData(1, partModel->GetPolyData()); + } + else if (partIdx == Body) + { + vtkMRMLLinearTransformNode* fixedReferenceToRasTransformNode = + this->IECLogic->GetTransformNodeBetween(vtkSlicerIECTransformLogic::FixedReference, vtkSlicerIECTransformLogic::RAS); + partModel->SetAndObserveTransformNodeID(fixedReferenceToRasTransformNode->GetID()); + } + else if (partIdx == ImagingPanelLeft) + { + vtkMRMLLinearTransformNode* leftImagingPanelToGantryTransformNode = + this->IECLogic->GetTransformNodeBetween(vtkSlicerIECTransformLogic::LeftImagingPanel, vtkSlicerIECTransformLogic::Gantry); + partModel->SetAndObserveTransformNodeID(leftImagingPanelToGantryTransformNode->GetID()); + } + else if (partIdx == ImagingPanelRight) + { + vtkMRMLLinearTransformNode* rightImagingPanelToGantryTransformNode = + this->IECLogic->GetTransformNodeBetween(vtkSlicerIECTransformLogic::RightImagingPanel, vtkSlicerIECTransformLogic::Gantry); + partModel->SetAndObserveTransformNodeID(rightImagingPanelToGantryTransformNode->GetID()); + } + else if (partIdx == FlatPanel) + { + vtkMRMLLinearTransformNode* flatPanelToGantryTransformNode = + this->IECLogic->GetTransformNodeBetween(vtkSlicerIECTransformLogic::FlatPanel, vtkSlicerIECTransformLogic::Gantry); + partModel->SetAndObserveTransformNodeID(flatPanelToGantryTransformNode->GetID()); + } + //TODO: ApplicatorHolder, ElectronApplicator? } - // - // Set up collision detection between components - this->GantryTableTopCollisionDetection->SetInputData(0, gantryModel->GetPolyData()); - this->GantryTableTopCollisionDetection->SetInputData(1, tableTopModel->GetPolyData()); - - this->GantryPatientSupportCollisionDetection->SetInputData(0, gantryModel->GetPolyData()); - this->GantryPatientSupportCollisionDetection->SetInputData(1, patientSupportModel->GetPolyData()); - - this->CollimatorTableTopCollisionDetection->SetInputData(0, collimatorModel->GetPolyData()); - this->CollimatorTableTopCollisionDetection->SetInputData(1, tableTopModel->GetPolyData()); - //TODO: Whole patient (segmentation, CT) will need to be transformed when the table top is transformed //vtkMRMLLinearTransformNode* patientModelTransforms = vtkMRMLLinearTransformNode::SafeDownCast( // this->GetMRMLScene()->GetFirstNodeByName("TableTopEccentricRotationToPatientSupportTransform")); //patientModel->SetAndObserveTransformNodeID(patientModelTransforms->GetID()); + //TODO: Instead of this make the tableTop the fixed part in RAS - // Patient model is set when calculating collisions, as it can be changed dynamically - this->GantryPatientCollisionDetection->SetInputData(0, gantryModel->GetPolyData()); - this->CollimatorPatientCollisionDetection->SetInputData(0, collimatorModel->GetPolyData()); // Set identity transform for patient (parent transform is taken into account when getting poly data from segmentation) vtkNew identityTransform; identityTransform->Identity(); @@ -657,6 +623,9 @@ void vtkSlicerRoomsEyeViewModuleLogic::LoadBasicCollimatorMountedDevices() return; } //TODO: + // + // Create a JSON file for this just as if it would be a treatment machine and use the same functions as for those. + // /* std::string moduleShareDirectory = this->GetModuleShareDirectory(); std::string additionalDevicesDirectory = moduleShareDirectory + "/" + "AdditionalTreatmentModels"; @@ -1468,7 +1437,7 @@ std::string vtkSlicerRoomsEyeViewModuleLogic::CheckForCollisions(vtkMRMLRoomsEye const char* vtkSlicerRoomsEyeViewModuleLogic::GetTreatmentMachinePartTypeAsString(TreatmentMachinePartType type) { switch (type) - { + { case Collimator: return "Collimator"; case Gantry: return "Gantry"; case PatientSupport: return "PatientSupport"; @@ -1482,7 +1451,7 @@ const char* vtkSlicerRoomsEyeViewModuleLogic::GetTreatmentMachinePartTypeAsStrin default: // invalid type return ""; - } + } } //--------------------------------------------------------------------------- @@ -1533,10 +1502,25 @@ bool vtkSlicerRoomsEyeViewModuleLogic::GetFileToPartTransformMatrixPartType(std: } //--------------------------------------------------------------------------- -int* vtkSlicerRoomsEyeViewModuleLogic::GetColorForPartType(std::string partType) +vtkVector3d vtkSlicerRoomsEyeViewModuleLogic::GetColorForPartType(std::string partType) { - //TODO: - return nullptr; + rapidjson::Value& partObject = this->Internal->GetTreatmentMachinePart(partType); + if (partObject.IsNull()) + { + // The part may not have been included in the description + return vtkVector3d(255, 255, 255); + } + + rapidjson::Value& colorArray = partObject["Color"]; + if (!colorArray.IsArray() || colorArray.Size() != 3 || !colorArray[0].IsInt()) + { + vtkErrorMacro("GetFilePathForPartType: Invalid treatment machine color for part " << partType); + return vtkVector3d(255, 255, 255); + } + + return vtkVector3d( (unsigned char)colorArray[0].GetInt(), + (unsigned char)colorArray[1].GetInt(), + (unsigned char)colorArray[2].GetInt() ); } //--------------------------------------------------------------------------- diff --git a/RoomsEyeView/Logic/vtkSlicerRoomsEyeViewModuleLogic.h b/RoomsEyeView/Logic/vtkSlicerRoomsEyeViewModuleLogic.h index 846c557d..e9df246d 100644 --- a/RoomsEyeView/Logic/vtkSlicerRoomsEyeViewModuleLogic.h +++ b/RoomsEyeView/Logic/vtkSlicerRoomsEyeViewModuleLogic.h @@ -34,6 +34,7 @@ Ontario with funds provided by the Ontario Ministry of Health and Long-Term Care class vtkCollisionDetectionFilter; class vtkMatrix4x4; class vtkPolyData; +class vtkVector3d; class vtkMRMLRoomsEyeViewNode; class vtkMRMLModelNode; @@ -45,11 +46,10 @@ class VTK_SLICER_ROOMSEYEVIEW_LOGIC_EXPORT vtkSlicerRoomsEyeViewModuleLogic : { public: /// Treatment machine part types - /// \sa LastComputationResult, GetLastComputationResult(), - /// GetLastComputationResultAsString() + /// \sa GetTreatmentMachinePartTypeAsString() enum TreatmentMachinePartType { - Collimator, + Collimator = 0, Gantry, PatientSupport, TableTop, @@ -133,7 +133,7 @@ class VTK_SLICER_ROOMSEYEVIEW_LOGIC_EXPORT vtkSlicerRoomsEyeViewModuleLogic : /// \return Success flag bool GetFileToPartTransformMatrixPartType(std::string partType, vtkMatrix4x4* fileToPartTransformMatrix); /// Get part name for part type in the currently loaded treatment machine description - int* GetColorForPartType(std::string partType); + vtkVector3d GetColorForPartType(std::string partType); /// Get part name for part type in the currently loaded treatment machine description bool GetEnabledStateForPartType(std::string partType); @@ -188,4 +188,3 @@ class VTK_SLICER_ROOMSEYEVIEW_LOGIC_EXPORT vtkSlicerRoomsEyeViewModuleLogic : }; #endif - diff --git a/RoomsEyeView/TreatmentMachineModels/VarianTrueBeamSTx/VarianTrueBeamSTx.json b/RoomsEyeView/TreatmentMachineModels/VarianTrueBeamSTx/VarianTrueBeamSTx.json index 2bd1f85e..59011947 100644 --- a/RoomsEyeView/TreatmentMachineModels/VarianTrueBeamSTx/VarianTrueBeamSTx.json +++ b/RoomsEyeView/TreatmentMachineModels/VarianTrueBeamSTx/VarianTrueBeamSTx.json @@ -9,56 +9,56 @@ "Name": "Collimator", "FilePath": "Collimator.stl", "FileToPartTransformMatrix": [ [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1] ], - "Color": [255, 255, 255], - "Enabled": true + "Color": [179, 179, 244], + "State": "Active" }, { "Type": "Gantry", "Name": "Gantry", "FilePath": "Gantry.stl", "FileToPartTransformMatrix": [ [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1] ], - "Color": [255, 255, 255], - "Enabled": true + "Color": [244, 244, 244], + "State": "Active" }, { "Type": "ImagingPanelLeft", "Name": "Left imaging panel", "FilePath": "ImagingPanelLeft.stl", "FileToPartTransformMatrix": [ [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1] ], - "Color": [255, 255, 255], - "Enabled": true + "Color": [244, 244, 244], + "State": "Active" }, { "Type": "ImagingPanelRight", "Name": "Right imaging panel", "FilePath": "ImagingPanelRight.stl", "FileToPartTransformMatrix": [ [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1] ], - "Color": [255, 255, 255], - "Enabled": true + "Color": [244, 244, 244], + "State": "Active" }, { "Type": "Body", "Name": "Linac body", "FilePath": "LinacBody.stl", "FileToPartTransformMatrix": [ [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1] ], - "Color": [255, 255, 255], - "Enabled": true + "Color": [230, 230, 230], + "State": "Active" }, { "Type": "PatientSupport", "Name": "Patient support", "FilePath": "PatientSupport.stl", "FileToPartTransformMatrix": [ [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1] ], - "Color": [255, 255, 255], - "Enabled": true + "Color": [217, 217, 217], + "State": "Active" }, { "Type": "TableTop", "Name": "Table top", "FilePath": "TableTop.stl", "FileToPartTransformMatrix": [ [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1] ], - "Color": [255, 255, 255], - "Enabled": true + "Color": [0, 0, 0], + "State": "Active" } ] }