Skip to content

Commit 0a3415c

Browse files
author
Mikhail Polkovnikov
committed
ENH: Dynamic arc beam sequence GUI and logic upgrades
1. Logic debug information was removed 2. ExternalBeamPlanning module description file was created
1 parent 0294eae commit 0a3415c

File tree

9 files changed

+200
-142
lines changed

9 files changed

+200
-142
lines changed

Beams/Logic/vtkSlicerBeamsModuleLogic.cxx

Lines changed: 3 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -340,9 +340,6 @@ bool vtkSlicerBeamsModuleLogic::CreateArcBeamDynamicSequence(
340340
{
341341
angles.push_back(finalAngle);
342342
}
343-
std::cout << "Angles " << angles.size() << '\n';
344-
std::for_each( angles.begin(), angles.end(), [](double v){ std::cout << v << ' '; });
345-
std::cout << std::endl;
346343
}
347344
else if (!direction && initialAngle > finalAngle) // CW, ini > fin
348345
{
@@ -368,9 +365,6 @@ bool vtkSlicerBeamsModuleLogic::CreateArcBeamDynamicSequence(
368365
{
369366
angles.push_back(finalAngle);
370367
}
371-
std::cout << "Angles " << angles.size() << '\n';
372-
std::for_each( angles.begin(), angles.end(), [](double v){ std::cout << v << ' '; });
373-
std::cout << std::endl;
374368
}
375369
else if (direction && initialAngle < finalAngle) // CCW, ini < fin
376370
{
@@ -395,9 +389,6 @@ bool vtkSlicerBeamsModuleLogic::CreateArcBeamDynamicSequence(
395389
{
396390
angles.push_back(finalAngle);
397391
}
398-
std::cout << "Angles " << angles.size() << '\n';
399-
std::for_each( angles.begin(), angles.end(), [](double v){ std::cout << v << ' '; });
400-
std::cout << std::endl;
401392
}
402393
else if (direction && initialAngle > finalAngle) // CCW, ini > fin
403394
{
@@ -409,53 +400,7 @@ bool vtkSlicerBeamsModuleLogic::CreateArcBeamDynamicSequence(
409400
{
410401
angles.push_back(finalAngle);
411402
}
412-
std::cout << "Angles " << angles.size() << '\n';
413-
std::for_each( angles.begin(), angles.end(), [](double v){ std::cout << v << ' '; });
414-
std::cout << std::endl;
415403
}
416-
/*
417-
if (finalAngle < 0. && finalAngle > 360. && initialAngle < 0. && initialAngle > 360.)
418-
{
419-
return false;
420-
}
421-
422-
if (!direction && initialAngle < finalAngle) // CW, ini < fin
423-
{
424-
for (double angle = initialAngle; angle <= finalAngle; angle += 1.)
425-
{
426-
angles.push_back(angle);
427-
}
428-
}
429-
else if (!direction && initialAngle > finalAngle) // CW, ini > fin
430-
{
431-
for (double angle = initialAngle; angle <= 360.; angle += 1.)
432-
{
433-
angles.push_back(angle);
434-
}
435-
for (double angle = 1.; angle <= finalAngle; angle += 1.)
436-
{
437-
angles.push_back(angle);
438-
}
439-
}
440-
else if (direction && initialAngle < finalAngle) // CCW, ini < fin
441-
{
442-
for (double angle = initialAngle; angle >= 0.0; angle -= 1.)
443-
{
444-
angles.push_back(angle);
445-
}
446-
for (double angle = 359.; angle >= finalAngle; angle -= 1.)
447-
{
448-
angles.push_back(angle);
449-
}
450-
}
451-
else if (direction && initialAngle > finalAngle) // CCW, ini > fin
452-
{
453-
for (double angle = initialAngle; angle >= finalAngle; angle -= 1.)
454-
{
455-
angles.push_back(angle);
456-
}
457-
}
458-
*/
459404
if (angles.size() < 2)
460405
{
461406
vtkErrorMacro("CreateArcBeamDynamicSequence: Number of angle elements is less than 2");
@@ -464,7 +409,7 @@ bool vtkSlicerBeamsModuleLogic::CreateArcBeamDynamicSequence(
464409

465410
vtkMRMLScene* scene = planNode->GetScene();
466411

467-
vtkMRMLSubjectHierarchyNode* shNode = vtkMRMLSubjectHierarchyNode::GetSubjectHierarchyNode(scene);
412+
vtkMRMLSubjectHierarchyNode* shNode = scene->GetSubjectHierarchyNode();
468413
if (!shNode)
469414
{
470415
vtkErrorMacro("CreateArcBeamDynamicSequence: Failed to access subject hierarchy node");
@@ -521,10 +466,7 @@ bool vtkSlicerBeamsModuleLogic::CreateArcBeamDynamicSequence(
521466
// SAD for RTPlan, source to beam limiting devices (Jaws, MLC)
522467
if (beamNode)
523468
{
524-
// beamNode->SetSAD(rtReader->GetBeamSourceAxisDistance(dicomBeamNumber));
525-
// beamNode->SetSourceToJawsDistanceX(rtReader->GetBeamSourceToJawsDistanceX(dicomBeamNumber));
526-
// beamNode->SetSourceToJawsDistanceY(rtReader->GetBeamSourceToJawsDistanceY(dicomBeamNumber));
527-
// beamNode->SetSourceToMultiLeafCollimatorDistance(rtReader->GetBeamSourceToMultiLeafCollimatorDistance(dicomBeamNumber));
469+
// TODO: Add beam parameters for each angle step
528470
}
529471

530472
// Add beam to beam sequence node
@@ -545,7 +487,7 @@ bool vtkSlicerBeamsModuleLogic::CreateArcBeamDynamicSequence(
545487
this->UpdateTransformForBeam( beamSequenceNode->GetSequenceScene(), beamNode, transformNode, isocenter);
546488

547489
vtkTransform* transform = vtkTransform::SafeDownCast(transformNode->GetTransformToParent());
548-
if (isocenter)
490+
if (transform)
549491
{
550492
// Actual translation to isocenter
551493
transform->Translate( isocenter[0], isocenter[1], isocenter[2]);

Beams/Logic/vtkSlicerBeamsModuleLogic.h

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -54,24 +54,24 @@ class VTK_SLICER_BEAMS_LOGIC_EXPORT vtkSlicerBeamsModuleLogic :
5454

5555
/// Update parent transform of a given beam using its parameters and the IEC logic
5656
/// without using plan node (only isocenter position)
57-
/// @param beamSequenceScene - inner scene of the beam sequence node
58-
/// @param beamNode - a current beam node (must be added to the beam sequence node beforehand)
59-
/// @param beamTransformNode - parent transform of the beam according to the beam parameters and isocenter
60-
/// @param isocenter - isocenter position
57+
/// \param beamSequenceScene inner scene of the beam sequence node
58+
/// \param beamNode a current beam node (must be added to the beam sequence node beforehand)
59+
/// \param beamTransformNode parent transform of the beam according to the beam parameters and isocenter
60+
/// \param isocenter isocenter position
6161
/// \warning This method is used only in vtkSlicerDicomRtImportExportModuleLogic::vtkInternal::LoadDynamicBeamSequence
62-
void UpdateTransformForBeam( vtkMRMLScene* beamSequenceScene, vtkMRMLRTBeamNode* beamNode,
62+
void UpdateTransformForBeam(vtkMRMLScene* beamSequenceScene, vtkMRMLRTBeamNode* beamNode,
6363
vtkMRMLLinearTransformNode* beamTransformNode, double isocenter[3]);
6464

6565
/// Create arc delivery beam sequence
66-
/// @param initialAngle - initial angle in degrees
67-
/// @param finalAngle - final angle in degrees
68-
/// @param angleStep - single angle step within arc
69-
/// @param direction - 0 - clockwise, 1 - counter-clockwise
70-
/// @param planNode - input plan node, which contains reference volume node and isocenter position
71-
/// @param sequenceBrowserNode - output sequence browser node
72-
/// @param sequenceBeamNode - output beam node
73-
/// @param sequenceTransformNode - output transform node
74-
bool CreateArcBeamDynamicSequence( double initialAngle, double finalAngle,
66+
/// \param initialAngle initial angle in degrees
67+
/// \param finalAngle final angle in degrees
68+
/// \param angleStep single angle step within arc
69+
/// \param direction 0 - clockwise, 1 - counter-clockwise
70+
/// \param planNode input plan node, which contains reference volume node and isocenter position
71+
/// \param sequenceBrowserNode output sequence browser node
72+
/// \param sequenceBeamNode output beam node
73+
/// \param sequenceTransformNode output transform node
74+
bool CreateArcBeamDynamicSequence(double initialAngle, double finalAngle,
7575
double angleStep, bool direction, vtkMRMLRTPlanNode* planNode,
7676
vtkMRMLSequenceBrowserNode* beamSequenceBrowserNode,
7777
vtkMRMLSequenceNode* beamSequenceNode, vtkMRMLSequenceNode* transformSequenceNode);
@@ -95,7 +95,7 @@ class VTK_SLICER_BEAMS_LOGIC_EXPORT vtkSlicerBeamsModuleLogic :
9595
vtkSlicerBeamsModuleLogic(const vtkSlicerBeamsModuleLogic&) = delete;
9696
void operator=(const vtkSlicerBeamsModuleLogic&) = delete;
9797

98-
vtkSlicerMLCPositionLogic* MLCPositionLogic;
98+
vtkSlicerMLCPositionLogic* MLCPositionLogic{ nullptr };
9999
};
100100

101101
#endif

DicomRtImportExport/Logic/vtkSlicerDicomRtReader.cxx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,7 @@ class vtkSlicerDicomRtReader::vtkInternal
319319
std::string InstitutionalDepartmentName;
320320
std::string ManufacturerModelName;
321321
};
322-
322+
323323
/// List of loaded beams from external beam plan
324324
std::vector<BeamEntry> BeamSequenceVector;
325325

@@ -589,9 +589,8 @@ bool vtkSlicerDicomRtReader::vtkInternal::BeamEntry::IsArcDeliverySequence(
589589
(cp1.GantryAngle >= 0. && cp1.GantryAngle <= 360.);
590590

591591
res = point0 && point1;
592-
if (point0 && point1)
592+
if (res)
593593
{
594-
res = true;
595594
initialAngle = cp0.GantryAngle;
596595
finalAngle = cp1.GantryAngle;
597596
// rotation: 0 - clockwise, 1 - counter clockwise

DicomRtImportExport/Logic/vtkSlicerDicomRtReader.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737

3838
// STD includes
3939
#include <vector>
40+
#include <array>
4041

4142
class vtkPolyData;
4243

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
## "External Beam Planning" module description
2+
3+
The ExternalBeamPlanning module is a generic, extensible module for forward
4+
planning of external beam radiation therapy treatments.
5+
6+
The "External Beam Planning" module is responsible for creating a beam plan or ion beam plan
7+
according to DICOM RT standard. Only a few basic features are supported.
8+
Each plan consists of a number of beams: static or dynamic. Each plan supports
9+
only one isocenter point, multiple isocenters per a single plan are not allowed.
10+
11+
This work was in part funded by An Applied Cancer Research Unit of Cancer Care Ontario
12+
with funds provided by the Ministry of Health and Long-Term Care and the Ontario Consortium
13+
for Adaptive Interventions in Radiation Oncology (OCAIRO) to provide free, open-source toolset
14+
for radiotherapy and related image-guided interventions.
15+
16+
Author: Csaba Pinter (PerkLab, Queen's University), Greg Sharp (Massachusetts General Hospital)
17+
Contact: Csaba Pinter, <email>[email protected]</email>
18+
19+
![image](https://www.slicer.org/w/img_auth.php/3/3f/LogoCco.png)
20+
![image](https://www.slicer.org/w/img_auth.php/2/27/LogoOCAIRO.jpg)
21+
22+
### Use Cases
23+
- Proton dose calculation
24+
- Any dose engine can be integrated (C++, Python, Matlab)
25+
26+
### Tutorials
27+
[Orthovoltage RT treatment planning tutorial](https://github.com/SlicerRt/SlicerRtDoc/blob/master/tutorials/SlicerRT_Tutorial_OrthovoltageDoseEngine.pptx) (uses EGSnrc)
28+
29+
### Graphical User Interface loadable module (GUI). Panels and their use
30+
31+
![image](https://user-images.githubusercontent.com/3785912/183899734-d4795313-fd39-4c40-8df6-e84f0469c3b0.png)
32+
33+
Loadable GUI module "External Beam Planning" is used to setup beam parameters
34+
for beams or ion beams, or modify already created beams.
35+
Static RT beam is setup by: Isocenter position, SAD, SID, Jaws borders, MLC positions. Static beam is fixed.
36+
37+
Dynamic RT and Ion RT beams are setup by: initial and final angles of rotation around gantry axis,
38+
direction of rotation, isocenter position, SAD, SID, Jaws borders, MLC positions, scanning spot map, etc.
39+
Dynamic beam changes it position over time by means of rotation or (and) changing the position of phase space of the beam.
40+
41+
#### Active RT plan
42+
43+
![image](https://user-images.githubusercontent.com/3785912/183899795-762b8b13-4ad5-4112-bcb6-e3f5fbd21fe2.png)
44+
45+
1. **Active RT plan**: Selected RT Plan
46+
2. **Ion plan**: flag to generate RTIonPlan instead of RTPlan
47+
48+
#### Plan parameters
49+
50+
![image](https://user-images.githubusercontent.com/3785912/183899832-fb178c71-900f-48d8-97a5-35c4d532bfb3.png)
51+
52+
1. **Reference volume**: Reference volume node data (usually CT)
53+
2. **Structure set**: Segmentation node data
54+
3. **Points of interest**: Markups fiducial to be used as isocenter
55+
4. **Isocenter**: Isocenter position in RAS
56+
5. **Center views**: Center slice views on isocenter
57+
6. **Target volume**: Select targer ROI
58+
7. **Isocenter at target center**: Set isocenter in the center of the targer ROI
59+
8. **Dose engine**: Select a dose engine calculator **Not implemented**
60+
9. **Rx Dose (Gy)**: Setup an amount of dose in Gy
61+
62+
#### Output
63+
64+
![image](https://user-images.githubusercontent.com/3785912/183899869-580d00a9-aa86-405d-b22c-d11a35f2f6df.png)
65+
66+
1. **Output dose volume**: Setup output dose volume
67+
2. **Clear dose**: Clear dose volume
68+
3. **Calculate WED**: Calculate water-equivalent distance **Not implemented**
69+
4. **Calculate Dose**: Calculate dose according to the engine parameters **Not implemented**
70+
71+
#### Beams
72+
73+
![image](https://user-images.githubusercontent.com/3785912/183899904-6f8e76fd-1574-462e-bf31-21f2cf5fa740.png)
74+
75+
1. **Arc beam sequence**: Activate dynamic beam arc movement around gantry rotation axis
76+
2. **Initial angle**: Initial angle position in degrees
77+
3. **Final angle**: Final angle position in degrees
78+
4. **Angle step**: Angle step
79+
5. **Rotation direction**: Clockwise (CW) of CounterClockwise (CCW) rotation
80+
5. **Add beam**: Add static or dynamic arc beam to the plan
81+
6. **Remove beam**: Remove selected beam from the plan
82+
83+
### Arc beam
84+
85+
The beam movement around rotation axis could be used to calculate a filtered back-projection reconstruction for cone-beam geometries.
86+
For fast forward and back projection reconstruction the [RTK](https://www.openrtk.org) library can be used.
87+
88+
### References
89+
90+
1. Sharp, G., Pinter, C., Unkelbach, J., Fichtinger, G. (2017). Open Source Proton Treatment Planning in 3D Slicer: Status Update. Proceedings to the 56 Annual Meeting of the Particle Therapy Cooperative Group (PTCOG), 8-13 May 2017. International Journal of Particle Therapy: Summer 2017, Vol. 4, No. 1, pp. 14-83.
91+
92+
### Information for Developers
93+
94+
- Sample C++ dose engine: [https://github.com/SlicerRt/SlicerRT/blob/master/ExternalBeamPlanning/Widgets/qSlicerMockDoseEngine.h](https://github.com/SlicerRt/SlicerRT/blob/master/ExternalBeamPlanning/Widgets/qSlicerMockDoseEngine.h)
95+
- Sample Python dose engine: [https://github.com/SlicerRt/SlicerRT/blob/master/ExternalBeamPlanning/Widgets/Python/MockPythonDoseEngine.py](https://github.com/SlicerRt/SlicerRT/blob/master/ExternalBeamPlanning/Widgets/Python/MockPythonDoseEngine.py)
96+
- Future plans
97+
- Inverse planning capabilities
98+
- Matlab plugin adapter
99+
- Ports module (apertures, MLC, target volume)
100+
- Beam groups (common parameters for a group of beams)
101+

0 commit comments

Comments
 (0)