From 95dd9f24ac2ea6a75be248e626840b02b4dc44f8 Mon Sep 17 00:00:00 2001 From: Michael Colonel Date: Tue, 7 Jan 2020 09:34:12 +0300 Subject: [PATCH 1/2] ENH: Pull request #124 cleanups and fixes --- Beams/MRML/vtkMRMLRTBeamNode.cxx | 441 ++++++++---------- Beams/MRML/vtkMRMLRTBeamNode.h | 17 + Beams/MRML/vtkMRMLRTIonBeamNode.cxx | 121 +++-- Beams/MRML/vtkMRMLRTIonBeamNode.h | 10 +- ...tkSlicerDicomRtImportExportModuleLogic.cxx | 18 +- .../Logic/vtkSlicerDicomRtReader.cxx | 88 ++-- .../Logic/vtkSlicerDicomRtReader.h | 20 +- 7 files changed, 335 insertions(+), 380 deletions(-) diff --git a/Beams/MRML/vtkMRMLRTBeamNode.cxx b/Beams/MRML/vtkMRMLRTBeamNode.cxx index 88bd35aba..9a086b022 100644 --- a/Beams/MRML/vtkMRMLRTBeamNode.cxx +++ b/Beams/MRML/vtkMRMLRTBeamNode.cxx @@ -62,149 +62,8 @@ static const char* CONTOUR_BEV_REFERENCE_ROLE = "contourBEVRef"; namespace { -typedef std::vector< std::pair< double, double > > PointVector; -typedef std::vector< std::array< double, 4 > > LeafDataVector; -typedef std::vector< std::pair< LeafDataVector::iterator, LeafDataVector::iterator > > SectionVector; -typedef std::array< double, 6 > JawsParameters; -typedef std::pair< bool, bool > MLCType; - bool(*AreEqual)( double, double) = vtkSlicerRtCommon::AreEqualWithTolerance; -void -CreateMLCPointsFromSectionBorders( const JawsParameters& jawsParameters, - const MLCType& mlcType, const SectionVector::value_type& sectionBorder, - PointVector& side12) -{ - const double& jawBegin = jawsParameters[0]; - const double& jawEnd = jawsParameters[1]; - const double& X1Jaw = jawsParameters[2]; - const double& X2Jaw = jawsParameters[3]; - const double& Y1Jaw = jawsParameters[4]; - const double& Y2Jaw = jawsParameters[5]; - - bool typeMLCX = mlcType.first; - bool typeMLCY = mlcType.second; - - PointVector side1, side2; // temporary vectors to save visible points - - LeafDataVector::iterator firstLeafIterator = sectionBorder.first; - LeafDataVector::iterator lastLeafIterator = sectionBorder.second; - LeafDataVector::iterator firstLeafIteratorJaws = firstLeafIterator; - LeafDataVector::iterator lastLeafIteratorJaws = lastLeafIterator; - - // find first and last visible leaves using Jaws data - for ( auto it = firstLeafIterator; it <= lastLeafIterator; ++it) - { - double& bound1 = (*it)[0]; // leaf begin boundary - double& bound2 = (*it)[1]; // leaf end boundary - if (bound1 <= jawBegin && bound2 > jawBegin) - { - firstLeafIteratorJaws = it; - } - else if (bound1 <= jawEnd && bound2 > jawEnd) - { - lastLeafIteratorJaws = it; - } - } - - // find opened MLC leaves into Jaws opening (logical AND) - if (firstLeafIteratorJaws != firstLeafIterator) - { - firstLeafIterator = std::max( firstLeafIteratorJaws, firstLeafIterator); - } - if (lastLeafIteratorJaws != lastLeafIterator) - { - lastLeafIterator = std::min( lastLeafIteratorJaws, lastLeafIterator); - } - - // add points for the visible leaves of side "1" and "2" - // into side1 and side2 points vectors - for ( auto it = firstLeafIterator; it <= lastLeafIterator; ++it) - { - double& bound1 = (*it)[0]; // leaf begin boundary - double& bound2 = (*it)[1]; // leaf end boundary - double& pos1 = (*it)[2]; // leaf position "1" - double& pos2 = (*it)[3]; // leaf position "2" - if (typeMLCX) - { - side1.push_back({ std::max( pos1, X1Jaw), bound1}); - side1.push_back({ std::max( pos1, X1Jaw), bound2}); - side2.push_back({ std::min( pos2, X2Jaw), bound1}); - side2.push_back({ std::min( pos2, X2Jaw), bound2}); - } - else if (typeMLCY) - { - side1.push_back({ bound1, std::max( pos1, Y1Jaw)}); - side1.push_back({ bound2, std::max( pos1, Y1Jaw)}); - side2.push_back({ bound1, std::min( pos2, Y2Jaw)}); - side2.push_back({ bound2, std::min( pos2, Y2Jaw)}); - } - } - - // intersection between Jaws and MLC boundary (logical AND) lambda - auto intersectJawsMLC = [ jawBegin, jawEnd, typeMLCX, typeMLCY](PointVector::value_type& point) - { - double& leafBoundary = point.second; - if (typeMLCX) // JawsY and MLCX - { - leafBoundary = point.second; - } - else if (typeMLCY) // JawsX and MLCY - { - leafBoundary = point.first; - } - - if (leafBoundary <= jawBegin) - { - leafBoundary = jawBegin; - } - else if (leafBoundary >= jawEnd) - { - leafBoundary = jawEnd; - } - }; - - // apply lambda to side "1" - std::for_each( side1.begin(), side1.end(), intersectJawsMLC); - // apply lambda to side "2" - std::for_each( side2.begin(), side2.end(), intersectJawsMLC); - - // reverse side "2" - std::reverse( side2.begin(), side2.end()); - - // fill real points vector side12 without excessive points from side1 vector - PointVector::value_type& p = side1.front(); // start point - double& px = p.first; // x coordinate of p point - double& py = p.second; // y coordinate of p point - side12.push_back(p); - for ( size_t i = 1; i < side1.size() - 1; ++i) - { - double& pxNext = side1[i + 1].first; // x coordinate of next point - double& pyNext = side1[i + 1].second; // y coordinate of next point - if (!AreEqual( px, pxNext) && !AreEqual( py, pyNext)) - { - p = side1[i]; - side12.push_back(p); - } - } - side12.push_back(side1.back()); // end point - - // same for the side2 vector - p = side2.front(); - side12.push_back(p); - for ( size_t i = 1; i < side2.size() - 1; ++i) - { - double& pxNext = side2[i + 1].first; - double& pyNext = side2[i + 1].second; - if (!AreEqual( px, pxNext) && !AreEqual( py, pyNext)) - { - p = side2[i]; - side12.push_back(p); - } - } - side12.push_back(side2.back()); -} - } // namespace //------------------------------------------------------------------------------ @@ -245,23 +104,22 @@ void vtkMRMLRTBeamNode::WriteXML(ostream& of, int nIndent) Superclass::WriteXML(of, nIndent); // Write all MRML node attributes into output stream - of << " BeamNumber=\"" << this->BeamNumber << "\""; - of << " BeamDescription=\"" << (this->BeamDescription ? BeamDescription : "") << "\""; - of << " BeamWeight=\"" << this->BeamWeight << "\""; - - of << " X1Jaw=\"" << this->X1Jaw << "\""; - of << " X2Jaw=\"" << this->X2Jaw << "\""; - of << " Y1Jaw=\"" << this->Y1Jaw << "\""; - of << " Y2Jaw=\"" << this->Y2Jaw << "\""; - of << " SAD=\"" << this->SAD << "\""; - - of << " SourceToJawsDistanceX=\"" << this->SourceToJawsDistanceX << "\""; - of << " SourceToJawsDistanceY=\"" << this->SourceToJawsDistanceY << "\""; - of << " SourceToMultiLeafCollimatorDistance=\"" << this->SourceToMultiLeafCollimatorDistance << "\""; - - of << " GantryAngle=\"" << this->GantryAngle << "\""; - of << " CollimatorAngle=\"" << this->CollimatorAngle << "\""; - of << " CouchAngle=\"" << this->CouchAngle << "\""; + vtkMRMLWriteXMLBeginMacro(of); + vtkMRMLWriteXMLIntMacro( BeamNumber, BeamNumber); + vtkMRMLWriteXMLStringMacro( BeamDescription, BeamDescription); + vtkMRMLWriteXMLFloatMacro( BeamWeight, BeamWeight); + vtkMRMLWriteXMLFloatMacro( X1Jaw, X1Jaw); + vtkMRMLWriteXMLFloatMacro( X2Jaw, X2Jaw); + vtkMRMLWriteXMLFloatMacro( Y1Jaw, Y1Jaw); + vtkMRMLWriteXMLFloatMacro( Y2Jaw, Y2Jaw); + vtkMRMLWriteXMLFloatMacro( SAD, SAD); + vtkMRMLWriteXMLFloatMacro( SourceToJawsDistanceX, SourceToJawsDistanceX); + vtkMRMLWriteXMLFloatMacro( SourceToJawsDistanceY, SourceToJawsDistanceY); + vtkMRMLWriteXMLFloatMacro( SourceToMultiLeafCollimatorDistance, SourceToMultiLeafCollimatorDistance); + vtkMRMLWriteXMLFloatMacro( GantryAngle, GantryAngle); + vtkMRMLWriteXMLFloatMacro( CollimatorAngle, CollimatorAngle); + vtkMRMLWriteXMLFloatMacro( CouchAngle, CouchAngle); + vtkMRMLWriteXMLEndMacro(); } //---------------------------------------------------------------------------- @@ -270,71 +128,22 @@ void vtkMRMLRTBeamNode::ReadXMLAttributes(const char** atts) vtkMRMLNode::ReadXMLAttributes(atts); // Read all MRML node attributes from two arrays of names and values - const char* attName = nullptr; - const char* attValue = nullptr; - - while (*atts != nullptr) - { - attName = *(atts++); - attValue = *(atts++); - - if (!strcmp(attName, "BeamNumber")) - { - this->BeamNumber = vtkVariant(attValue).ToInt(); - } - else if (!strcmp(attName, "BeamDescription")) - { - this->SetBeamDescription(attValue); - } - else if (!strcmp(attName, "BeamWeight")) - { - this->BeamWeight = vtkVariant(attValue).ToDouble(); - } - else if (!strcmp(attName, "X1Jaw")) - { - this->X1Jaw = vtkVariant(attValue).ToDouble(); - } - else if (!strcmp(attName, "X2Jaw")) - { - this->X2Jaw = vtkVariant(attValue).ToDouble(); - } - else if (!strcmp(attName, "Y1Jaw")) - { - this->Y1Jaw = vtkVariant(attValue).ToDouble(); - } - else if (!strcmp(attName, "Y2Jaw")) - { - this->Y2Jaw = vtkVariant(attValue).ToDouble(); - } - else if (!strcmp(attName, "SAD")) - { - this->SAD = vtkVariant(attValue).ToDouble(); - } - else if (!strcmp(attName, "SourceToJawsDistanceX")) - { - this->SourceToJawsDistanceX = vtkVariant(attValue).ToDouble(); - } - else if (!strcmp(attName, "SourceToJawsDistanceY")) - { - this->SourceToJawsDistanceY = vtkVariant(attValue).ToDouble(); - } - else if (!strcmp(attName, "SourceToMultiLeafCollimatorDistance")) - { - this->SourceToMultiLeafCollimatorDistance = vtkVariant(attValue).ToDouble(); - } - else if (!strcmp(attName, "GantryAngle")) - { - this->GantryAngle = vtkVariant(attValue).ToDouble(); - } - else if (!strcmp(attName, "CollimatorAngle")) - { - this->CollimatorAngle = vtkVariant(attValue).ToDouble(); - } - else if (!strcmp(attName, "CouchAngle")) - { - this->CouchAngle = vtkVariant(attValue).ToDouble(); - } - } + vtkMRMLReadXMLBeginMacro(atts); + vtkMRMLReadXMLIntMacro( BeamNumber, BeamNumber); + vtkMRMLReadXMLStringMacro( BeamDescription, BeamDescription); + vtkMRMLReadXMLFloatMacro( BeamWeight, BeamWeight); + vtkMRMLReadXMLFloatMacro( X1Jaw, X1Jaw); + vtkMRMLReadXMLFloatMacro( X2Jaw, X2Jaw); + vtkMRMLReadXMLFloatMacro( Y1Jaw, Y1Jaw); + vtkMRMLReadXMLFloatMacro( Y2Jaw, Y2Jaw); + vtkMRMLReadXMLFloatMacro( SAD, SAD); + vtkMRMLReadXMLFloatMacro( SourceToJawsDistanceX, SourceToJawsDistanceX); + vtkMRMLReadXMLFloatMacro( SourceToJawsDistanceY, SourceToJawsDistanceY); + vtkMRMLReadXMLFloatMacro( SourceToMultiLeafCollimatorDistance, SourceToMultiLeafCollimatorDistance); + vtkMRMLReadXMLFloatMacro( GantryAngle, GantryAngle); + vtkMRMLReadXMLFloatMacro( CollimatorAngle, CollimatorAngle); + vtkMRMLReadXMLFloatMacro( CouchAngle, CouchAngle); + vtkMRMLReadXMLEndMacro(); } //---------------------------------------------------------------------------- @@ -342,6 +151,7 @@ void vtkMRMLRTBeamNode::ReadXMLAttributes(const char** atts) // Does NOT copy: ID, FilePrefix, Name, VolumeID void vtkMRMLRTBeamNode::Copy(vtkMRMLNode *anode) { + int disabledModify = this->StartModify(); // Do not call Copy function of the direct model base class, as it copies the poly data too, // which is undesired for beams, as they generate their own poly data from their properties. vtkMRMLDisplayableNode::Copy(anode); @@ -382,7 +192,8 @@ void vtkMRMLRTBeamNode::Copy(vtkMRMLNode *anode) this->SetCollimatorAngle(node->GetCollimatorAngle()); this->SetCouchAngle(node->GetCouchAngle()); - this->DisableModifiedEventOff(); + this->EndModify(disabledModify); + this->InvokePendingModifiedEvent(); } @@ -411,23 +222,22 @@ void vtkMRMLRTBeamNode::PrintSelf(ostream& os, vtkIndent indent) { Superclass::PrintSelf(os,indent); - os << indent << " BeamNumber: " << this->BeamNumber << "\n"; - os << indent << " BeamDescription: " << (this->BeamDescription ? BeamDescription : "nullptr") << "\n"; - os << indent << " BeamWeight: " << this->BeamWeight << "\n"; - - os << indent << " X1Jaw: " << this->X1Jaw << "\n"; - os << indent << " X2Jaw: " << this->X2Jaw << "\n"; - os << indent << " Y1Jaw: " << this->Y1Jaw << "\n"; - os << indent << " Y2Jaw: " << this->Y2Jaw << "\n"; - os << indent << " SAD: " << this->SAD << "\n"; - - os << indent << " SourceToJawsDistanceX: " << this->SourceToJawsDistanceX << "\n"; - os << indent << " SourceToJawsDistanceY: " << this->SourceToJawsDistanceY << "\n"; - os << indent << " SourceToMultiLeafCollimatorDistance: " << this->SourceToMultiLeafCollimatorDistance << "\n"; - - os << indent << " GantryAngle: " << this->GantryAngle << "\n"; - os << indent << " CollimatorAngle: " << this->CollimatorAngle << "\n"; - os << indent << " CouchAngle: " << this->CouchAngle << "\n"; + vtkMRMLPrintBeginMacro(os, indent); + vtkMRMLPrintIntMacro(BeamNumber); + vtkMRMLPrintStringMacro(BeamDescription); + vtkMRMLPrintFloatMacro(BeamWeight); + vtkMRMLPrintFloatMacro(X1Jaw); + vtkMRMLPrintFloatMacro(X2Jaw); + vtkMRMLPrintFloatMacro(Y1Jaw); + vtkMRMLPrintFloatMacro(Y2Jaw); + vtkMRMLPrintFloatMacro(SAD); + vtkMRMLPrintFloatMacro(SourceToJawsDistanceX); + vtkMRMLPrintFloatMacro(SourceToJawsDistanceY); + vtkMRMLPrintFloatMacro(SourceToMultiLeafCollimatorDistance); + vtkMRMLPrintFloatMacro(GantryAngle); + vtkMRMLPrintFloatMacro(CollimatorAngle); + vtkMRMLPrintFloatMacro(CouchAngle); + vtkMRMLPrintEndMacro(); } //---------------------------------------------------------------------------- @@ -741,7 +551,7 @@ void vtkMRMLRTBeamNode::CreateBeamPolyData(vtkPolyData* beamModelPolyData/*=null // Check that we have MLC with Jaws opening if (mlcTableNode && xOpened && yOpened) { - LeafDataVector mlc; // temporary MLC vector (Boundary and Position) + MLCBoundaryPositionVector mlc; const char* mlcName = mlcTableNode->GetName(); bool typeMLCX = !strncmp( "MLCX", mlcName, strlen("MLCX")); @@ -776,7 +586,7 @@ void vtkMRMLRTBeamNode::CreateBeamPolyData(vtkPolyData* beamModelPolyData/*=null // find first and last opened leaves visible within jaws // and fill sections vector for further processing - SectionVector sections; // sections (first & last leaf iterator) of opened MLC + MLCSectionVector sections; // sections (first & last leaf iterator) of opened MLC for ( auto it = mlc.begin(); it != mlc.end(); ++it) { double& pos1 = (*it)[2]; // leaf position "1" @@ -824,7 +634,7 @@ void vtkMRMLRTBeamNode::CreateBeamPolyData(vtkPolyData* beamModelPolyData/*=null // append one or more visible sections to beam model poly data auto append = vtkSmartPointer::New(); - for (const SectionVector::value_type& section : sections) + for (const MLCSectionVector::value_type& section : sections) { if (section.first != mlc.end() && section.second != mlc.end()) { @@ -832,19 +642,16 @@ void vtkMRMLRTBeamNode::CreateBeamPolyData(vtkPolyData* beamModelPolyData/*=null vtkSmartPointer points = vtkSmartPointer::New(); vtkSmartPointer cellArray = vtkSmartPointer::New(); - PointVector side12; // real points for side "1" and "2" - JawsParameters jawsParameters{ jawBegin, jawEnd, this->X1Jaw, this->X2Jaw, this->Y1Jaw, this->Y2Jaw }; - MLCType mlcType{ typeMLCX, typeMLCY }; - - CreateMLCPointsFromSectionBorders( jawsParameters, mlcType, - section, side12); + MLCVisiblePointVector side12; // real points for side "1" and "2" + CreateMLCPointsFromSectionBorder( jawBegin, jawEnd, typeMLCX, + typeMLCY, section, side12); // fill vtk points points->InsertPoint( 0, 0, 0, this->SAD); // source // side "1" and "2" points vector vtkIdType pointIds = 0; - for ( const PointVector::value_type& point : side12) + for ( const MLCVisiblePointVector::value_type& point : side12) { const double& x = point.first; const double& y = point.second; @@ -929,6 +736,13 @@ void vtkMRMLRTBeamNode::CreateBeamPolyData(vtkPolyData* beamModelPolyData/*=null } } +//--------------------------------------------------------------------------- +vtkPolyData* vtkMRMLRTBeamNode::CreateMultiLeafCollimatorPolyData() +{ + //TODO: Create beam limiting device poly data here + return nullptr; +} + //--------------------------------------------------------------------------- void vtkMRMLRTBeamNode::RequestCloning() { @@ -940,3 +754,128 @@ void vtkMRMLRTBeamNode::RequestCloning() this->GetDisplayNode()->Modified(); } } + +//---------------------------------------------------------------------------- +void vtkMRMLRTBeamNode::CreateMLCPointsFromSectionBorder( double jawBegin, + double jawEnd, bool typeMLCX, bool typeMLCY, const MLCSectionVector::value_type& sectionBorder, + MLCVisiblePointVector& side12) +{ + MLCVisiblePointVector side1, side2; // temporary vectors to save visible points + + MLCBoundaryPositionVector::iterator firstLeafIterator = sectionBorder.first; + MLCBoundaryPositionVector::iterator lastLeafIterator = sectionBorder.second; + MLCBoundaryPositionVector::iterator firstLeafIteratorJaws = firstLeafIterator; + MLCBoundaryPositionVector::iterator lastLeafIteratorJaws = lastLeafIterator; + + // find first and last visible leaves using Jaws data + for ( auto it = firstLeafIterator; it <= lastLeafIterator; ++it) + { + double& bound1 = (*it)[0]; // leaf begin boundary + double& bound2 = (*it)[1]; // leaf end boundary + if (bound1 <= jawBegin && bound2 > jawBegin) + { + firstLeafIteratorJaws = it; + } + else if (bound1 <= jawEnd && bound2 > jawEnd) + { + lastLeafIteratorJaws = it; + } + } + + // find opened MLC leaves into Jaws opening (logical AND) + if (firstLeafIteratorJaws != firstLeafIterator) + { + firstLeafIterator = std::max( firstLeafIteratorJaws, firstLeafIterator); + } + if (lastLeafIteratorJaws != lastLeafIterator) + { + lastLeafIterator = std::min( lastLeafIteratorJaws, lastLeafIterator); + } + + // add points for the visible leaves of side "1" and "2" + // into side1 and side2 points vectors + for ( auto it = firstLeafIterator; it <= lastLeafIterator; ++it) + { + double& bound1 = (*it)[0]; // leaf begin boundary + double& bound2 = (*it)[1]; // leaf end boundary + double& pos1 = (*it)[2]; // leaf position "1" + double& pos2 = (*it)[3]; // leaf position "2" + if (typeMLCX) + { + side1.push_back({ std::max( pos1, this->X1Jaw), bound1}); + side1.push_back({ std::max( pos1, this->X1Jaw), bound2}); + side2.push_back({ std::min( pos2, this->X2Jaw), bound1}); + side2.push_back({ std::min( pos2, this->X2Jaw), bound2}); + } + else if (typeMLCY) + { + side1.push_back({ bound1, std::max( pos1, this->Y1Jaw)}); + side1.push_back({ bound2, std::max( pos1, this->Y1Jaw)}); + side2.push_back({ bound1, std::min( pos2, this->Y2Jaw)}); + side2.push_back({ bound2, std::min( pos2, this->Y2Jaw)}); + } + } + + // intersection between Jaws and MLC boundary (logical AND) lambda + auto intersectJawsMLC = [ jawBegin, jawEnd, typeMLCX, typeMLCY](MLCVisiblePointVector::value_type& point) + { + double& leafBoundary = point.second; + if (typeMLCX) // JawsY and MLCX + { + leafBoundary = point.second; + } + else if (typeMLCY) // JawsX and MLCY + { + leafBoundary = point.first; + } + + if (leafBoundary <= jawBegin) + { + leafBoundary = jawBegin; + } + else if (leafBoundary >= jawEnd) + { + leafBoundary = jawEnd; + } + }; + + // apply lambda to side "1" + std::for_each( side1.begin(), side1.end(), intersectJawsMLC); + // apply lambda to side "2" + std::for_each( side2.begin(), side2.end(), intersectJawsMLC); + + // reverse side "2" + std::reverse( side2.begin(), side2.end()); + + // fill real points vector side12 without excessive points from side1 vector + MLCVisiblePointVector::value_type& p = side1.front(); // start point + double& px = p.first; // x coordinate of p point + double& py = p.second; // y coordinate of p point + side12.push_back(p); + for ( size_t i = 1; i < side1.size() - 1; ++i) + { + double& pxNext = side1[i + 1].first; // x coordinate of next point + double& pyNext = side1[i + 1].second; // y coordinate of next point + if (!AreEqual( px, pxNext) && !AreEqual( py, pyNext)) + { + p = side1[i]; + side12.push_back(p); + } + } + side12.push_back(side1.back()); // end point + + // same for the side2 vector + p = side2.front(); + side12.push_back(p); + for ( size_t i = 1; i < side2.size() - 1; ++i) + { + double& pxNext = side2[i + 1].first; + double& pyNext = side2[i + 1].second; + if (!AreEqual( px, pxNext) && !AreEqual( py, pyNext)) + { + p = side2[i]; + side12.push_back(p); + } + } + side12.push_back(side2.back()); +} diff --git a/Beams/MRML/vtkMRMLRTBeamNode.h b/Beams/MRML/vtkMRMLRTBeamNode.h index fde13fd49..edec76d7c 100644 --- a/Beams/MRML/vtkMRMLRTBeamNode.h +++ b/Beams/MRML/vtkMRMLRTBeamNode.h @@ -84,6 +84,9 @@ class VTK_SLICER_BEAMS_MODULE_MRML_EXPORT vtkMRMLRTBeamNode : public vtkMRMLMode /// Only creates it if missing virtual void CreateDefaultTransformNode(); + /// Create beam limiting device (MLC and jaws) polydata for model node + virtual vtkPolyData* CreateMultiLeafCollimatorPolyData(); + /// Create transform node that places the beam poly data in the right position based on geometry. /// Always creates a new transform node. virtual void CreateNewBeamTransformNode(); @@ -245,6 +248,20 @@ class VTK_SLICER_BEAMS_MODULE_MRML_EXPORT vtkMRMLRTBeamNode : public vtkMRMLMode double CollimatorAngle; /// Couch angle double CouchAngle; + +private: + /// Visible multi-leaf collimator points + typedef std::vector< std::pair< double, double > > MLCVisiblePointVector; + /// Multi-leaf collimator boundary position parameters + typedef std::vector< std::array< double, 4 > > MLCBoundaryPositionVector; + /// Start and stop border of multi-leaf collimator opened section + typedef std::vector< std::pair< MLCBoundaryPositionVector::iterator, MLCBoundaryPositionVector::iterator > > MLCSectionVector; + + /// \brief Create visible points of MLC enclosure (perimeter) + /// in IEC BEAM LIMITING DEVICE coordinate axis (isocenter plane) + void CreateMLCPointsFromSectionBorder( double jawBegin, double jawEnd, + bool typeMLCX, bool typeMLCY, const MLCSectionVector::value_type& sectionBorder, + MLCVisiblePointVector& side12); }; #endif // __vtkMRMLRTBeamNode_h diff --git a/Beams/MRML/vtkMRMLRTIonBeamNode.cxx b/Beams/MRML/vtkMRMLRTIonBeamNode.cxx index ab00053ca..ae919028a 100644 --- a/Beams/MRML/vtkMRMLRTIonBeamNode.cxx +++ b/Beams/MRML/vtkMRMLRTIonBeamNode.cxx @@ -63,10 +63,10 @@ vtkMRMLNodeNewMacro(vtkMRMLRTIonBeamNode); vtkMRMLRTIonBeamNode::vtkMRMLRTIonBeamNode() : Superclass(), - VSADx(Superclass::SAD), - IsocenterToJawsDistanceX(Superclass::SourceToJawsDistanceX), - IsocenterToJawsDistanceY(Superclass::SourceToJawsDistanceY), - IsocenterToMultiLeafCollimatorDistance(Superclass::SourceToMultiLeafCollimatorDistance), + VSADx(vtkMRMLRTBeamNode::SAD), + IsocenterToJawsDistanceX(vtkMRMLRTBeamNode::SourceToJawsDistanceX), + IsocenterToJawsDistanceY(vtkMRMLRTBeamNode::SourceToJawsDistanceY), + IsocenterToMultiLeafCollimatorDistance(vtkMRMLRTBeamNode::SourceToMultiLeafCollimatorDistance), IsocenterToRangeShifterDistance(4000.), ScanningSpotSize({ 15.0, 15.0 }) { @@ -88,64 +88,32 @@ void vtkMRMLRTIonBeamNode::WriteXML(ostream& of, int nIndent) { Superclass::WriteXML(of, nIndent); + vtkMRMLWriteXMLBeginMacro(of); // Write all MRML node attributes into output stream - of << " VSADx=\"" << this->VSADx << "\""; - of << " VSADy=\"" << this->VSADy << "\""; - of << " IsocenterToJawsDistanceX=\"" << this->IsocenterToJawsDistanceX << "\""; - of << " IsocenterToJawsDistanceY=\"" << this->IsocenterToJawsDistanceY << "\""; - of << " IsocenterToMultiLeafCollimatorDistance=\"" << this->IsocenterToMultiLeafCollimatorDistance << "\""; - of << " IsocenterToRangeShifterDistance=\"" << this->IsocenterToRangeShifterDistance << "\""; - of << " ScanningSpotSizeX=\"" << this->ScanningSpotSize[0] << "\""; - of << " ScanningSpotSizeY=\"" << this->ScanningSpotSize[1] << "\""; + vtkMRMLWriteXMLFloatMacro( VSADx, VSADx); + vtkMRMLWriteXMLFloatMacro( VSADy, VSADy); + vtkMRMLWriteXMLFloatMacro( IsocenterToJawsDistanceX, IsocenterToJawsDistanceX); + vtkMRMLWriteXMLFloatMacro( IsocenterToJawsDistanceY, IsocenterToJawsDistanceY); + vtkMRMLWriteXMLFloatMacro( IsocenterToMultiLeafCollimatorDistance, IsocenterToMultiLeafCollimatorDistance); + vtkMRMLWriteXMLFloatMacro( IsocenterToRangeShifterDistance, IsocenterToRangeShifterDistance); + vtkMRMLWriteXMLVectorMacro( ScanningSpotSize, ScanningSpotSize, float, 2); + vtkMRMLWriteXMLEndMacro(); } //---------------------------------------------------------------------------- void vtkMRMLRTIonBeamNode::ReadXMLAttributes(const char** atts) { Superclass::ReadXMLAttributes(atts); - - // Read all MRML node attributes from two arrays of names and values - const char* attName = nullptr; - const char* attValue = nullptr; - - while (*atts != nullptr) - { - attName = *(atts++); - attValue = *(atts++); - - if (!strcmp( attName, "VSADx")) - { - this->VSADx = vtkVariant(attValue).ToDouble(); - } - else if (!strcmp( attName, "VSADy")) - { - this->VSADy = vtkVariant(attValue).ToDouble(); - } - else if (!strcmp( attName, "IsocenterToJawsDistanceX")) - { - this->IsocenterToJawsDistanceX = vtkVariant(attValue).ToDouble(); - } - else if (!strcmp( attName, "IsocenterToJawsDistanceY")) - { - this->IsocenterToJawsDistanceY = vtkVariant(attValue).ToDouble(); - } - else if (!strcmp( attName, "IsocenterToMultiLeafCollimatorDistance")) - { - this->IsocenterToMultiLeafCollimatorDistance = vtkVariant(attValue).ToDouble(); - } - else if (!strcmp( attName, "IsocenterToRangeShifterDistance")) - { - this->IsocenterToRangeShifterDistance = vtkVariant(attValue).ToDouble(); - } - else if (!strcmp( attName, "ScanningSpotSizeX")) - { - this->ScanningSpotSize[0] = vtkVariant(attValue).ToFloat(); - } - else if (!strcmp( attName, "ScanningSpotSizeY")) - { - this->ScanningSpotSize[1] = vtkVariant(attValue).ToFloat(); - } - } + + vtkMRMLReadXMLBeginMacro(atts); + vtkMRMLReadXMLFloatMacro( VSADx, VSADx); + vtkMRMLReadXMLFloatMacro( VSADy, VSADy); + vtkMRMLReadXMLFloatMacro( IsocenterToJawsDistanceX, IsocenterToJawsDistanceX); + vtkMRMLReadXMLFloatMacro( IsocenterToJawsDistanceY, IsocenterToJawsDistanceY); + vtkMRMLReadXMLFloatMacro( IsocenterToMultiLeafCollimatorDistance, IsocenterToMultiLeafCollimatorDistance); + vtkMRMLReadXMLFloatMacro( IsocenterToRangeShifterDistance, IsocenterToRangeShifterDistance); + vtkMRMLReadXMLVectorMacro( ScanningSpotSize, ScanningSpotSize, float, 2); + vtkMRMLReadXMLEndMacro(); } //---------------------------------------------------------------------------- @@ -153,6 +121,8 @@ void vtkMRMLRTIonBeamNode::ReadXMLAttributes(const char** atts) // Does NOT copy: ID, FilePrefix, Name, VolumeID void vtkMRMLRTIonBeamNode::Copy(vtkMRMLNode *anode) { + int disabledModify = this->StartModify(); + // Do not call Copy function of the direct model base class, as it copies the poly data too, // which is undesired for beams, as they generate their own poly data from their properties. vtkMRMLDisplayableNode::Copy(anode); @@ -190,14 +160,14 @@ void vtkMRMLRTIonBeamNode::Copy(vtkMRMLNode *anode) this->SetIsocenterToMultiLeafCollimatorDistance(node->GetIsocenterToMultiLeafCollimatorDistance()); this->SetIsocenterToRangeShifterDistance(node->GetIsocenterToRangeShifterDistance()); - float* scanSpotSize = node->GetScanningSpotSize(); - this->SetScanningSpotSize({ scanSpotSize[0], scanSpotSize[1] }); + this->SetScanningSpotSize(node->GetScanningSpotSize()); this->SetGantryAngle(node->GetGantryAngle()); this->SetCollimatorAngle(node->GetCollimatorAngle()); this->SetCouchAngle(node->GetCouchAngle()); - this->DisableModifiedEventOff(); + this->EndModify(disabledModify); + this->InvokePendingModifiedEvent(); } @@ -212,14 +182,15 @@ void vtkMRMLRTIonBeamNode::PrintSelf(ostream& os, vtkIndent indent) { Superclass::PrintSelf(os,indent); - os << indent << " VSADx: " << this->VSADx << "\n"; - os << indent << " VSADy: " << this->VSADy << "\n"; - os << indent << " IsocenterToJawsDistanceX: " << this->IsocenterToJawsDistanceX << "\n"; - os << indent << " IsocenterToJawsDistanceY: " << this->IsocenterToJawsDistanceY << "\n"; - os << indent << " IsocenterToMultiLeafCollimatorDistance: " << this->IsocenterToMultiLeafCollimatorDistance << "\n"; - os << indent << " IsocenterToRangeShifterDistance: " << this->IsocenterToRangeShifterDistance << "\n"; - os << indent << " ScanningSpotSizeX: " << this->ScanningSpotSize[0] << "\n"; - os << indent << " ScanningSpotSizeY: " << this->ScanningSpotSize[1] << "\n"; + vtkMRMLPrintBeginMacro(os, indent); + vtkMRMLPrintFloatMacro(VSADx); + vtkMRMLPrintFloatMacro(VSADy); + vtkMRMLPrintFloatMacro(IsocenterToJawsDistanceX); + vtkMRMLPrintFloatMacro(IsocenterToJawsDistanceY); + vtkMRMLPrintFloatMacro(IsocenterToMultiLeafCollimatorDistance); + vtkMRMLPrintFloatMacro(IsocenterToRangeShifterDistance); + vtkMRMLPrintVectorMacro( ScanningSpotSize, float, 2); + vtkMRMLPrintEndMacro(); } //---------------------------------------------------------------------------- @@ -233,7 +204,7 @@ void vtkMRMLRTIonBeamNode::SetAndObserveScanSpotTableNode(vtkMRMLTableNode* node this->SetNodeReferenceID( SCANSPOT_REFERENCE_ROLE, (node ? node->GetID() : nullptr)); - this->InvokeCustomModifiedEvent(Superclass::BeamGeometryModified); + this->InvokeCustomModifiedEvent(vtkMRMLRTBeamNode::BeamGeometryModified); } //---------------------------------------------------------------------------- @@ -262,6 +233,13 @@ void vtkMRMLRTIonBeamNode::CreateNewBeamTransformNode() Superclass::CreateNewBeamTransformNode(); } +//--------------------------------------------------------------------------- +vtkPolyData* vtkMRMLRTIonBeamNode::CreateMultiLeafCollimatorPolyData() +{ + //TODO: Create beam limiting device poly data here + return nullptr; +} + //---------------------------------------------------------------------------- void vtkMRMLRTIonBeamNode::SetVSAD( double xComponent, double yComponent) { @@ -283,6 +261,15 @@ void vtkMRMLRTIonBeamNode::SetVSAD(const std::array< double, 2 >& VSAD) this->SetVSAD( VSAD[0], VSAD[1]); } +//---------------------------------------------------------------------------- +void vtkMRMLRTIonBeamNode::SetScanningSpotSize(float size[2]) +{ + this->ScanningSpotSize[0] = size[0]; + this->ScanningSpotSize[1] = size[1]; + this->Modified(); + this->InvokeCustomModifiedEvent(vtkMRMLRTBeamNode::BeamGeometryModified); +} + //---------------------------------------------------------------------------- void vtkMRMLRTIonBeamNode::SetScanningSpotSize(const std::array< float, 2 >& size) { diff --git a/Beams/MRML/vtkMRMLRTIonBeamNode.h b/Beams/MRML/vtkMRMLRTIonBeamNode.h index 62f5d4d70..5589dab11 100644 --- a/Beams/MRML/vtkMRMLRTIonBeamNode.h +++ b/Beams/MRML/vtkMRMLRTIonBeamNode.h @@ -62,6 +62,9 @@ class VTK_SLICER_BEAMS_MODULE_MRML_EXPORT vtkMRMLRTIonBeamNode : public vtkMRMLR /// Only creates it if missing void CreateDefaultTransformNode() override; + /// Create beam limiting device (MLC and jaws) polydata for model node + vtkPolyData* CreateMultiLeafCollimatorPolyData() override; + /// Create transform node that places the beam poly data in the right position based on geometry. /// Always creates a new transform node. void CreateNewBeamTransformNode() override; @@ -69,11 +72,15 @@ class VTK_SLICER_BEAMS_MODULE_MRML_EXPORT vtkMRMLRTIonBeamNode : public vtkMRMLR public: /// Get VSAD distance x component vtkGetMacro( VSADx, double); + /// Set VSAD distance x component + vtkSetMacro( VSADx, double); /// Get VSAD distance y component vtkGetMacro( VSADy, double); + /// Set VSAD distance y component + vtkSetMacro( VSADy, double); /// Set VSAD distance. Triggers \sa BeamGeometryModified event and re-generation of beam model - void SetVSAD( double VSADx, double VSADy); + void SetVSAD( double VSADx, double VSADy); void SetVSAD(double VSAD[2]); void SetVSAD(const std::array< double, 2 >& VSAD); @@ -81,6 +88,7 @@ class VTK_SLICER_BEAMS_MODULE_MRML_EXPORT vtkMRMLRTIonBeamNode : public vtkMRMLR /// Set scanning spot size for modulated beam /// Triggers \sa BeamGeometryModified event and re-generation of beam model + void SetScanningSpotSize(float ScanSpotSize[2]); void SetScanningSpotSize(const std::array< float, 2 >& ScanSpotSize); /// Get isocenter to jaws X distance diff --git a/DicomRtImportExport/Logic/vtkSlicerDicomRtImportExportModuleLogic.cxx b/DicomRtImportExport/Logic/vtkSlicerDicomRtImportExportModuleLogic.cxx index de254a996..95be884ed 100644 --- a/DicomRtImportExport/Logic/vtkSlicerDicomRtImportExportModuleLogic.cxx +++ b/DicomRtImportExport/Logic/vtkSlicerDicomRtImportExportModuleLogic.cxx @@ -830,7 +830,7 @@ bool vtkSlicerDicomRtImportExportModuleLogic::vtkInternal::LoadExternalBeamPlan( { unsigned int dicomBeamNumber = rtReader->GetBeamNumberForIndex(beamIndex); const char* beamName = rtReader->GetBeamName(dicomBeamNumber); - unsigned int nofCointrolPoints = rtReader->GetNumberOfControlPoints(dicomBeamNumber); + unsigned int nofCointrolPoints = rtReader->GetBeamNumberOfControlPoints(dicomBeamNumber); for ( unsigned int cointrolPointIndex = 0; cointrolPointIndex < nofCointrolPoints; ++cointrolPointIndex) { @@ -863,7 +863,7 @@ bool vtkSlicerDicomRtImportExportModuleLogic::vtkInternal::LoadExternalBeamPlan( // Set beam geometry parameters from DICOM double jawPositions[2][2] = {{0.0, 0.0},{0.0, 0.0}}; - if (rtReader->GetControlPointJawPositions( dicomBeamNumber, cointrolPointIndex, jawPositions)) + if (rtReader->GetBeamControlPointJawPositions( dicomBeamNumber, cointrolPointIndex, jawPositions)) { beamNode->SetX1Jaw(jawPositions[0][0]); beamNode->SetX2Jaw(jawPositions[0][1]); @@ -871,9 +871,9 @@ bool vtkSlicerDicomRtImportExportModuleLogic::vtkInternal::LoadExternalBeamPlan( beamNode->SetY2Jaw(jawPositions[1][1]); } - beamNode->SetGantryAngle(rtReader->GetControlPointGantryAngle( dicomBeamNumber, cointrolPointIndex)); - beamNode->SetCollimatorAngle(rtReader->GetControlPointBeamLimitingDeviceAngle( dicomBeamNumber, cointrolPointIndex)); - beamNode->SetCouchAngle(rtReader->GetControlPointPatientSupportAngle( dicomBeamNumber, cointrolPointIndex)); + beamNode->SetGantryAngle(rtReader->GetBeamControlPointGantryAngle( dicomBeamNumber, cointrolPointIndex)); + beamNode->SetCollimatorAngle(rtReader->GetBeamControlPointBeamLimitingDeviceAngle( dicomBeamNumber, cointrolPointIndex)); + beamNode->SetCouchAngle(rtReader->GetBeamControlPointPatientSupportAngle( dicomBeamNumber, cointrolPointIndex)); // SAD for RTPlan, source to beam limiting devices (Jaws, MLC) if (beamNode && !ionBeamNode) @@ -892,7 +892,7 @@ bool vtkSlicerDicomRtImportExportModuleLogic::vtkInternal::LoadExternalBeamPlan( ionBeamNode->SetIsocenterToJawsDistanceX(rtReader->GetBeamIsocenterToJawsDistanceX(dicomBeamNumber)); ionBeamNode->SetIsocenterToJawsDistanceY(rtReader->GetBeamIsocenterToJawsDistanceY(dicomBeamNumber)); ionBeamNode->SetIsocenterToMultiLeafCollimatorDistance(rtReader->GetBeamIsocenterToMultiLeafCollimatorDistance(dicomBeamNumber)); - bool res = rtReader->GetControlPointScanningSpotSize( dicomBeamNumber, + bool res = rtReader->GetBeamControlPointScanningSpotSize( dicomBeamNumber, cointrolPointIndex, ScanSpotSize); if (res) { @@ -901,7 +901,7 @@ bool vtkSlicerDicomRtImportExportModuleLogic::vtkInternal::LoadExternalBeamPlan( } // Set isocenter to parent plan - double* isocenter = rtReader->GetControlPointIsocenterPositionRas( dicomBeamNumber, + double* isocenter = rtReader->GetBeamControlPointIsocenterPositionRas( dicomBeamNumber, cointrolPointIndex); planNode->SetIsocenterSpecification(vtkMRMLRTPlanNode::ArbitraryPoint); if (beamIndex == 0) @@ -934,7 +934,7 @@ bool vtkSlicerDicomRtImportExportModuleLogic::vtkInternal::LoadExternalBeamPlan( vtkMRMLTableNode* mlcTableNode = nullptr; vtkMRMLDoubleArrayNode* mlcArrayNode = nullptr; // Check MLC - const char* mlcName = rtReader->GetControlPointMultiLeafCollimatorPositions( dicomBeamNumber, + const char* mlcName = rtReader->GetBeamControlPointMultiLeafCollimatorPositions( dicomBeamNumber, cointrolPointIndex, boundaries, positions); if (mlcName) { @@ -957,7 +957,7 @@ bool vtkSlicerDicomRtImportExportModuleLogic::vtkInternal::LoadExternalBeamPlan( if (ionBeamNode) { std::vector positions, weights; - bool result = rtReader->GetControlPointScanSpotParameters( dicomBeamNumber, + bool result = rtReader->GetBeamControlPointScanSpotParameters( dicomBeamNumber, cointrolPointIndex, positions, weights); if (result) { diff --git a/DicomRtImportExport/Logic/vtkSlicerDicomRtReader.cxx b/DicomRtImportExport/Logic/vtkSlicerDicomRtReader.cxx index 60d63a36f..2c5a4058b 100644 --- a/DicomRtImportExport/Logic/vtkSlicerDicomRtReader.cxx +++ b/DicomRtImportExport/Logic/vtkSlicerDicomRtReader.cxx @@ -78,13 +78,14 @@ class vtkSlicerDicomRtReader::vtkInternal BeamLimitingDeviceEntry() : SourceIsoDistance(400.), - NumberOfPairs(0) + NumberOfLeafJawPairs(0) { } - /// Source to BeamLimitingDevice (MLC) distance for RTPlan (first element) + /// Source to BeamLimitingDevice (MLC) distance for RTPlan /// or Isocenter to BeamLimitingDevice (MLC) distance for RTIonPlan double SourceIsoDistance; - unsigned int NumberOfPairs; + /// Number of leaf pairs for MLC, or 1 for symmetric or asymmetric jaws + unsigned int NumberOfLeafJawPairs; /// MLC position boundaries: Raw DICOM values std::vector LeafPositionBoundary; }; @@ -95,7 +96,7 @@ class vtkSlicerDicomRtReader::vtkInternal public: RangeShifterEntry() : - Number(0), + Number(-1), Type("BINARY") { } @@ -106,7 +107,8 @@ class vtkSlicerDicomRtReader::vtkInternal std::string Description; }; - /// Structure storing a treatment compensator parameters associated with beam + //TODO: Add support of compensators + //Structure storing a treatment compensator parameters associated with beam class CompensatorEntry { public: @@ -115,7 +117,8 @@ class vtkSlicerDicomRtReader::vtkInternal } }; - /// Structure storing a shielding block parameters associated with beam + //TODO: Add support of shielding blocks + //Structure storing a shielding block parameters associated with beam class BlockEntry { public: @@ -148,12 +151,13 @@ class vtkSlicerDicomRtReader::vtkInternal /// List of loaded contour ROIs from structure set std::vector RoiSequenceVector; + //TODO: Use referenced beams to load beams in correct order class ReferencedBeamEntry { public: ReferencedBeamEntry() : - Number(0) + Number(-1) { } unsigned int Number; /// Referenced Beam Number (300C,0006) @@ -162,13 +166,14 @@ class vtkSlicerDicomRtReader::vtkInternal std::vector< std::pair< double, unsigned int > > BeamDoseVerificationControlPointSequenceVector; }; + //TODO: Use fraction entry to check that RTPlan data is valid /// Structure storing Fraction information of RT plan or RT Ion plan class FractionEntry { public: FractionEntry() : - Number(0), + Number(-1), NumberOfFractionsPlanned(0), NumberOfBeams(0), NumberOfBrachyApplicationSetups(0) @@ -250,7 +255,7 @@ class vtkSlicerDicomRtReader::vtkInternal public: BeamEntry() : - Number(0), + Number(-1), Type("STATIC"), RadiationIon({ 0, 0, 0 }), SourceAxisDistance({ 2000., 2000. }), @@ -322,7 +327,7 @@ class vtkSlicerDicomRtReader::vtkInternal public: ChannelEntry() { - Number = 0; + Number = -1; NumberOfControlPoints = 0; Length = 0.0; TotalTime = 0.0; @@ -985,12 +990,12 @@ void vtkSlicerDicomRtReader::vtkInternal::LoadRTPlan(DcmDataset* dataset) } else if (!deviceType.compare("MLCX") || !deviceType.compare("MLCY")) { - OFCondition getNumberOfPairsCondition = collimatorItem.getNumberOfLeafJawPairs(nofPairs); - if (getNumberOfPairsCondition.good()) + OFCondition getNumberOfLeafJawPairsCondition = collimatorItem.getNumberOfLeafJawPairs(nofPairs); + if (getNumberOfLeafJawPairsCondition.good()) { std::string& type = beamEntry.MultiLeafCollimatorType; double& mlcDistance = beamEntry.MultiLeafCollimator.SourceIsoDistance; - unsigned int& pairs = beamEntry.MultiLeafCollimator.NumberOfPairs; + unsigned int& pairs = beamEntry.MultiLeafCollimator.NumberOfLeafJawPairs; std::vector& bounds = beamEntry.MultiLeafCollimator.LeafPositionBoundary; OFVector leafPositionBoundaries; OFCondition getBoundariesCondition = collimatorItem.getLeafPositionBoundaries(leafPositionBoundaries); @@ -1553,12 +1558,12 @@ void vtkSlicerDicomRtReader::vtkInternal::LoadRTIonPlan(DcmDataset* dataset) } else if (!deviceType.compare("MLCX") || !deviceType.compare("MLCY")) { - OFCondition getNumberOfPairsCondition = collimatorItem.getNumberOfLeafJawPairs(nofPairs); - if (getNumberOfPairsCondition.good()) + OFCondition getNumberOfLeafJawPairsCondition = collimatorItem.getNumberOfLeafJawPairs(nofPairs); + if (getNumberOfLeafJawPairsCondition.good()) { std::string& type = beamEntry.MultiLeafCollimatorType; double& mlcDistance = beamEntry.MultiLeafCollimator.SourceIsoDistance; - unsigned int& pairs = beamEntry.MultiLeafCollimator.NumberOfPairs; + unsigned int& pairs = beamEntry.MultiLeafCollimator.NumberOfLeafJawPairs; std::vector& bounds = beamEntry.MultiLeafCollimator.LeafPositionBoundary; OFVector leafPositionBoundaries; OFCondition getBoundariesCondition = collimatorItem.getLeafPositionBoundaries(leafPositionBoundaries); @@ -1696,7 +1701,7 @@ void vtkSlicerDicomRtReader::vtkInternal::LoadRTIonPlan(DcmDataset* dataset) controlPoint.CumulativeMetersetWeight = cumulativeMetersetWeight; } - if (beamEntry.ScanMode == "MODULATED") + if (!beamEntry.ScanMode.compare("MODULATED") || !beamEntry.ScanMode.compare("MODULATED_SPEC")) { OFString scanSpotTuneId(""); dataCondition = controlPointItem.getScanSpotTuneID(scanSpotTuneId); @@ -1746,7 +1751,7 @@ void vtkSlicerDicomRtReader::vtkInternal::LoadRTIonPlan(DcmDataset* dataset) dataCondition = controlPointItem.getScanSpotPositionMap( y, 2 * i + 1); if (dataCondition.good()) { - controlPoint.ScanSpotPositionMap.at(2 * i + 1) = float(x); + controlPoint.ScanSpotPositionMap.at(2 * i + 1) = float(y); } dataCondition = controlPointItem.getScanSpotMetersetWeights( w, i); @@ -2622,10 +2627,9 @@ int vtkSlicerDicomRtReader::GetNumberOfBeams() } //---------------------------------------------------------------------------- -unsigned int vtkSlicerDicomRtReader::GetNumberOfControlPoints(unsigned int beamNumber) +unsigned int vtkSlicerDicomRtReader::GetBeamNumberOfControlPoints(unsigned int beamNumber) { vtkInternal::BeamEntry* beam = this->Internal->FindBeamByNumber(beamNumber); - if (beam) { return (beam->ControlPointSequenceVector.size() > 1) ? beam->ControlPointSequenceVector.size() : 0; @@ -2650,10 +2654,10 @@ const char* vtkSlicerDicomRtReader::GetBeamName(unsigned int beamNumber) return (beam->Name.empty() ? vtkSlicerRtCommon::DICOMRTIMPORT_NO_NAME : beam->Name).c_str(); } -//---------------------------------------------------------------------------- +//---------------------------------------------------------------------------- DEPRICATED double* vtkSlicerDicomRtReader::GetBeamIsocenterPositionRas(unsigned int beamNumber) { - return GetControlPointIsocenterPositionRas( beamNumber, 0); + return GetBeamControlPointIsocenterPositionRas( beamNumber, 0); } //---------------------------------------------------------------------------- @@ -2679,7 +2683,7 @@ const char* vtkSlicerDicomRtReader::GetBeamRadiationType(unsigned int beamNumber } //---------------------------------------------------------------------------- -double vtkSlicerDicomRtReader::GetControlPointNominalBeamEnergy( unsigned int beamNumber, +double vtkSlicerDicomRtReader::GetBeamControlPointNominalBeamEnergy( unsigned int beamNumber, unsigned int controlPointIndex) { vtkInternal::BeamEntry* beam=this->Internal->FindBeamByNumber(beamNumber); @@ -2692,7 +2696,7 @@ double vtkSlicerDicomRtReader::GetControlPointNominalBeamEnergy( unsigned int be } //---------------------------------------------------------------------------- -double* vtkSlicerDicomRtReader::GetControlPointIsocenterPositionRas( unsigned int beamNumber, +double* vtkSlicerDicomRtReader::GetBeamControlPointIsocenterPositionRas( unsigned int beamNumber, unsigned int controlPointIndex) { vtkInternal::BeamEntry* beam = this->Internal->FindBeamByNumber(beamNumber); @@ -2728,14 +2732,14 @@ double* vtkSlicerDicomRtReader::GetBeamVirtualSourceAxisDistance(unsigned int be return beam->SourceAxisDistance.data(); } -//---------------------------------------------------------------------------- +//---------------------------------------------------------------------------- DEPRICATED double vtkSlicerDicomRtReader::GetBeamGantryAngle(unsigned int beamNumber) { - return GetControlPointGantryAngle( beamNumber, 0); + return GetBeamControlPointGantryAngle( beamNumber, 0); } //---------------------------------------------------------------------------- -double vtkSlicerDicomRtReader::GetControlPointGantryAngle( unsigned int beamNumber, +double vtkSlicerDicomRtReader::GetBeamControlPointGantryAngle( unsigned int beamNumber, unsigned int controlPointIndex) { vtkInternal::BeamEntry* beam = this->Internal->FindBeamByNumber(beamNumber); @@ -2757,14 +2761,14 @@ double vtkSlicerDicomRtReader::GetControlPointGantryAngle( unsigned int beamNumb return 0.0; } -//---------------------------------------------------------------------------- +//---------------------------------------------------------------------------- DEPRICATED double vtkSlicerDicomRtReader::GetBeamPatientSupportAngle(unsigned int beamNumber) { - return GetControlPointPatientSupportAngle( beamNumber, 0); + return GetBeamControlPointPatientSupportAngle( beamNumber, 0); } //---------------------------------------------------------------------------- -double vtkSlicerDicomRtReader::GetControlPointPatientSupportAngle( unsigned int beamNumber, +double vtkSlicerDicomRtReader::GetBeamControlPointPatientSupportAngle( unsigned int beamNumber, unsigned int controlPointIndex) { vtkInternal::BeamEntry* beam = this->Internal->FindBeamByNumber(beamNumber); @@ -2786,14 +2790,14 @@ double vtkSlicerDicomRtReader::GetControlPointPatientSupportAngle( unsigned int return 0.0; } -//---------------------------------------------------------------------------- +//---------------------------------------------------------------------------- DEPRICATED double vtkSlicerDicomRtReader::GetBeamBeamLimitingDeviceAngle(unsigned int beamNumber) { - return GetControlPointBeamLimitingDeviceAngle( beamNumber, 0); + return GetBeamControlPointBeamLimitingDeviceAngle( beamNumber, 0); } //---------------------------------------------------------------------------- -double vtkSlicerDicomRtReader::GetControlPointBeamLimitingDeviceAngle( unsigned int beamNumber, +double vtkSlicerDicomRtReader::GetBeamControlPointBeamLimitingDeviceAngle( unsigned int beamNumber, unsigned int controlPointIndex) { vtkInternal::BeamEntry* beam = this->Internal->FindBeamByNumber(beamNumber); @@ -2815,14 +2819,14 @@ double vtkSlicerDicomRtReader::GetControlPointBeamLimitingDeviceAngle( unsigned return 0.0; } -//---------------------------------------------------------------------------- +//---------------------------------------------------------------------------- DEPRICATED void vtkSlicerDicomRtReader::GetBeamLeafJawPositions(unsigned int beamNumber, double jawPositions[2][2]) { - GetControlPointJawPositions( beamNumber, 0, jawPositions); + GetBeamControlPointJawPositions( beamNumber, 0, jawPositions); } //---------------------------------------------------------------------------- -bool vtkSlicerDicomRtReader::GetControlPointJawPositions( unsigned int beamNumber, +bool vtkSlicerDicomRtReader::GetBeamControlPointJawPositions( unsigned int beamNumber, unsigned int controlPointIndex, double jawPositions[2][2]) { vtkInternal::BeamEntry* beam = this->Internal->FindBeamByNumber(beamNumber); @@ -2848,23 +2852,23 @@ bool vtkSlicerDicomRtReader::GetControlPointJawPositions( unsigned int beamNumbe return false; } -//---------------------------------------------------------------------------- +//---------------------------------------------------------------------------- DEPRICATED const char* vtkSlicerDicomRtReader::GetBeamMultiLeafCollimatorPositions( unsigned int beamNumber, std::vector& pairBoundaries, std::vector& leafPositions) { - return GetControlPointMultiLeafCollimatorPositions( beamNumber, 0, + return GetBeamControlPointMultiLeafCollimatorPositions( beamNumber, 0, pairBoundaries, leafPositions); } //---------------------------------------------------------------------------- -const char* vtkSlicerDicomRtReader::GetControlPointMultiLeafCollimatorPositions( unsigned int beamNumber, +const char* vtkSlicerDicomRtReader::GetBeamControlPointMultiLeafCollimatorPositions( unsigned int beamNumber, unsigned int controlPointIndex, std::vector& pairBoundaries, std::vector& leafPositions) { vtkInternal::BeamEntry* beam = this->Internal->FindBeamByNumber(beamNumber); if (beam) { - const unsigned int& pairs = beam->MultiLeafCollimator.NumberOfPairs; + const unsigned int& pairs = beam->MultiLeafCollimator.NumberOfLeafJawPairs; const std::string& mlcType = beam->MultiLeafCollimatorType; if (mlcType.empty()) { @@ -2907,7 +2911,7 @@ const char* vtkSlicerDicomRtReader::GetControlPointMultiLeafCollimatorPositions( } //---------------------------------------------------------------------------- -bool vtkSlicerDicomRtReader::GetControlPointScanSpotParameters( unsigned int beamNumber, +bool vtkSlicerDicomRtReader::GetBeamControlPointScanSpotParameters( unsigned int beamNumber, unsigned int controlPointIndex, std::vector& positionMap, std::vector& metersetWeights) { @@ -2953,7 +2957,7 @@ bool vtkSlicerDicomRtReader::GetControlPointScanSpotParameters( unsigned int bea } //---------------------------------------------------------------------------- -bool vtkSlicerDicomRtReader::GetControlPointScanningSpotSize( unsigned int beamNumber, +bool vtkSlicerDicomRtReader::GetBeamControlPointScanningSpotSize( unsigned int beamNumber, unsigned int controlPointIndex, std::array< float, 2 >& ScanSpotSize) { vtkInternal::BeamEntry* beam = this->Internal->FindBeamByNumber(beamNumber); diff --git a/DicomRtImportExport/Logic/vtkSlicerDicomRtReader.h b/DicomRtImportExport/Logic/vtkSlicerDicomRtReader.h index ee945e53f..009cc41dd 100644 --- a/DicomRtImportExport/Logic/vtkSlicerDicomRtReader.h +++ b/DicomRtImportExport/Logic/vtkSlicerDicomRtReader.h @@ -80,7 +80,7 @@ class VTK_SLICER_DICOMRTIMPORTEXPORT_LOGIC_EXPORT vtkSlicerDicomRtReader : publi /// Get number of control points for a given beams /// \result >= 2 if number of control points are valid, 0 otherwise - unsigned int GetNumberOfControlPoints(unsigned int beamNumber); + unsigned int GetBeamNumberOfControlPoints(unsigned int beamNumber); /// Get beam number (as defined in DICOM) for a beam index (that is between 0 and numberOfBeams-1) unsigned int GetBeamNumberForIndex(unsigned int index); @@ -98,11 +98,11 @@ class VTK_SLICER_DICOMRTIMPORTEXPORT_LOGIC_EXPORT vtkSlicerDicomRtReader : publi const char* GetBeamRadiationType(unsigned int beamNumber); /// Get isocenter for a given control point of a beam - double* GetControlPointIsocenterPositionRas( unsigned int beamNumber, + double* GetBeamControlPointIsocenterPositionRas( unsigned int beamNumber, unsigned int controlPointIndex); /// Get nominal beam energy for a given control point of a beam - double GetControlPointNominalBeamEnergy( unsigned int beamNumber, + double GetBeamControlPointNominalBeamEnergy( unsigned int beamNumber, unsigned int controlPoint); /// Get source axis distance for a given beam @@ -116,21 +116,21 @@ class VTK_SLICER_DICOMRTIMPORTEXPORT_LOGIC_EXPORT vtkSlicerDicomRtReader : publi double GetBeamGantryAngle(unsigned int beamNumber); // DEPRICATED /// Get gantry angle for a given control point of a beam - double GetControlPointGantryAngle( unsigned int beamNumber, + double GetBeamControlPointGantryAngle( unsigned int beamNumber, unsigned int controlPoint); /// Get beam patient support (couch) angle for a given beam double GetBeamPatientSupportAngle(unsigned int beamNumber); // DEPRICATED /// Get patient support (couch) angle for a given control point of a beam - double GetControlPointPatientSupportAngle( unsigned int beamNumber, + double GetBeamControlPointPatientSupportAngle( unsigned int beamNumber, unsigned int controlPoint); /// Get beam beam limiting device (collimator) angle for a given beam double GetBeamBeamLimitingDeviceAngle(unsigned int beamNumber); // DEPRICATED /// Get beam limiting device (collimator) angle for a given control point of a beam - double GetControlPointBeamLimitingDeviceAngle( unsigned int beamNumber, + double GetBeamControlPointBeamLimitingDeviceAngle( unsigned int beamNumber, unsigned int controlPoint); /// Get beam leaf jaw positions for a given beam @@ -140,7 +140,7 @@ class VTK_SLICER_DICOMRTIMPORTEXPORT_LOGIC_EXPORT vtkSlicerDicomRtReader : publi /// Get jaw positions for a given control point of a beam /// \param jawPositions Array in which the jaw positions are copied /// \return true if jaw positions are valid, false otherwise - bool GetControlPointJawPositions( unsigned int beamNumber, + bool GetBeamControlPointJawPositions( unsigned int beamNumber, unsigned int controlPoint, double jawPositions[2][2]); /// Get MLC leaves boundaries & leaves positions opening for a given beam @@ -155,14 +155,14 @@ class VTK_SLICER_DICOMRTIMPORTEXPORT_LOGIC_EXPORT vtkSlicerDicomRtReader : publi /// \param positionMap Array in which the raw position map are copied /// \param metersetWeights Array in which the raw meterset weights are copied /// \return true if data is valid, false otherwise - bool GetControlPointScanSpotParameters( unsigned int beamNumber, + bool GetBeamControlPointScanSpotParameters( unsigned int beamNumber, unsigned int controlPointIndex, std::vector& positionMap, std::vector& metersetWeights); /// Get Scan spot size for a given control point of a modulated ion beam /// \param ScanSpotSize Array in which the raw scanning spot size is copied /// \return true if data is valid, false otherwise - bool GetControlPointScanningSpotSize( unsigned int beamNumber, + bool GetBeamControlPointScanningSpotSize( unsigned int beamNumber, unsigned int controlPointIndex, std::array< float, 2 >& ScanSpotSize); /// Get source to beam limiting device distance (MLC) for a given beam of RTPlan @@ -190,7 +190,7 @@ class VTK_SLICER_DICOMRTIMPORTEXPORT_LOGIC_EXPORT vtkSlicerDicomRtReader : publi /// \param pairBoundaries Array in which the raw leaves boundaries are copied /// \param leafPositions Array in which the raw leaf positions are copied /// \return "MLCX" or "MLCY" if data is valid, nullptr otherwise - const char* GetControlPointMultiLeafCollimatorPositions( unsigned int beamNumber, + const char* GetBeamControlPointMultiLeafCollimatorPositions( unsigned int beamNumber, unsigned int controlPoint, std::vector& pairBoundaries, std::vector& leafPositions); From c778013489cc0d01e2cd04944a9841d7b0ffa06f Mon Sep 17 00:00:00 2001 From: MichaelColonel Date: Mon, 13 Jan 2020 16:53:05 +0300 Subject: [PATCH 2/2] ENH: Pull request #124 fixes --- Beams/MRML/vtkMRMLRTBeamNode.cxx | 97 ++++++++++++++++++++++++----- Beams/MRML/vtkMRMLRTBeamNode.h | 2 +- Beams/MRML/vtkMRMLRTIonBeamNode.cxx | 2 +- Beams/MRML/vtkMRMLRTIonBeamNode.h | 2 +- 4 files changed, 86 insertions(+), 17 deletions(-) diff --git a/Beams/MRML/vtkMRMLRTBeamNode.cxx b/Beams/MRML/vtkMRMLRTBeamNode.cxx index 9a086b022..1c02e2ae7 100644 --- a/Beams/MRML/vtkMRMLRTBeamNode.cxx +++ b/Beams/MRML/vtkMRMLRTBeamNode.cxx @@ -47,6 +47,7 @@ #include #include #include +#include //------------------------------------------------------------------------------ const char* vtkMRMLRTBeamNode::NEW_BEAM_NODE_NAME_PREFIX = "NewBeam_"; @@ -515,10 +516,10 @@ void vtkMRMLRTBeamNode::CreateBeamPolyData(vtkPolyData* beamModelPolyData/*=null vtkErrorMacro("CreateBeamPolyData: Invalid beam node"); return; } - + vtkMRMLTableNode* mlcTableNode = nullptr; - vtkIdType nofLeaves = 0; + vtkIdType nofLeafPairs = 0; // MLC boundary data vtkMRMLDoubleArrayNode* arrayNode = this->GetMLCBoundaryDoubleArrayNode(); @@ -526,16 +527,16 @@ void vtkMRMLRTBeamNode::CreateBeamPolyData(vtkPolyData* beamModelPolyData/*=null if (arrayNode) { mlcBoundArray = arrayNode->GetArray(); - nofLeaves = mlcBoundArray ? (mlcBoundArray->GetNumberOfTuples() - 1) : 0; + nofLeafPairs = mlcBoundArray ? (mlcBoundArray->GetNumberOfTuples() - 1) : 0; } // MLC position data - if (nofLeaves) + if (nofLeafPairs) { mlcTableNode = this->GetMLCPositionTableNode(); - if (mlcTableNode && (mlcTableNode->GetNumberOfRows() == nofLeaves)) + if (mlcTableNode && (mlcTableNode->GetNumberOfRows() == nofLeafPairs)) { - vtkDebugMacro("CreateBeamPolyData: Valid MLC nodes, number of leaves: " << nofLeaves); + vtkDebugMacro("CreateBeamPolyData: Valid MLC nodes, number of leaf pairs: " << nofLeafPairs); } else { @@ -558,13 +559,13 @@ void vtkMRMLRTBeamNode::CreateBeamPolyData(vtkPolyData* beamModelPolyData/*=null bool typeMLCY = !strncmp( "MLCY", mlcName, strlen("MLCY")); // copy MLC data for easier processing - for ( vtkIdType leaf = 0; leaf < nofLeaves; leaf++) + for ( vtkIdType leafPair = 0; leafPair < nofLeafPairs; leafPair++) { vtkTable* table = mlcTableNode->GetTable(); - double boundBegin = mlcBoundArray->GetTuple1(leaf); - double boundEnd = mlcBoundArray->GetTuple1(leaf + 1); - double pos1 = table->GetValue( leaf, 0).ToDouble(); - double pos2 = table->GetValue( leaf, 1).ToDouble(); + double boundBegin = mlcBoundArray->GetTuple1(leafPair); + double boundEnd = mlcBoundArray->GetTuple1(leafPair + 1); + double pos1 = table->GetValue( leafPair, 0).ToDouble(); + double pos2 = table->GetValue( leafPair, 1).ToDouble(); mlc.push_back({ boundBegin, boundEnd, pos1, pos2 }); } @@ -737,10 +738,78 @@ void vtkMRMLRTBeamNode::CreateBeamPolyData(vtkPolyData* beamModelPolyData/*=null } //--------------------------------------------------------------------------- -vtkPolyData* vtkMRMLRTBeamNode::CreateMultiLeafCollimatorPolyData() +vtkPolyData* vtkMRMLRTBeamNode::CreateMultiLeafCollimatorModelPolyData() { - //TODO: Create beam limiting device poly data here - return nullptr; + vtkIdType nofLeafPairs = 0; + // MLC boundary data + vtkMRMLDoubleArrayNode* arrayNode = this->GetMLCBoundaryDoubleArrayNode(); + vtkDoubleArray* mlcBoundArray = nullptr; + if (arrayNode) + { + mlcBoundArray = arrayNode->GetArray(); + nofLeafPairs = mlcBoundArray ? (mlcBoundArray->GetNumberOfTuples() - 1) : 0; + } + + // MLC position data + vtkMRMLTableNode* mlcTableNode = nullptr; + if (nofLeafPairs) + { + mlcTableNode = this->GetMLCPositionTableNode(); + if (mlcTableNode && (mlcTableNode->GetNumberOfRows() == nofLeafPairs)) + { + vtkDebugMacro("CreateMultiLeafCollimatorPolyData: Valid MLC nodes, number of leaf pairs: " << nofLeafPairs); + } + else + { + vtkErrorMacro("CreateMultiLeafCollimatorPolyData: Invalid MLC nodes, or " \ + "number of MLC boundaries and positions are different"); + mlcTableNode = nullptr; + } + } + + // Check that we have MLC + // append a leaf pair to form MLC polydata + double isoCenterToMLCDistance = this->SAD - SourceToMultiLeafCollimatorDistance; + auto append = vtkSmartPointer::New(); + if (mlcTableNode) + { + const char* mlcName = mlcTableNode->GetName(); + bool typeMLCX = !strncmp( "MLCX", mlcName, strlen("MLCX")); + bool typeMLCY = !strncmp( "MLCY", mlcName, strlen("MLCY")); + + // create polydata for a leaf pair + for ( vtkIdType leafPair = 0; leafPair < nofLeafPairs; leafPair++) + { + vtkTable* table = mlcTableNode->GetTable(); + double boundBegin = mlcBoundArray->GetTuple1(leafPair); + double boundEnd = mlcBoundArray->GetTuple1(leafPair + 1); + double pos1 = table->GetValue( leafPair, 0).ToDouble(); + double pos2 = table->GetValue( leafPair, 1).ToDouble(); + + auto leaf1 = vtkSmartPointer::New(); + auto leaf2 = vtkSmartPointer::New(); + if (typeMLCX) + { + leaf1->SetBounds( pos1 - 20., pos1, boundBegin, BoundEnd, -isoCenterToMLCDistance, isoCenterToMLCDistance); + leaf2->SetBounds( pos2, pos2 + 20., boundBegin, BoundEnd, -isoCenterToMLCDistance, isoCenterToMLCDistance); + } + else if (typeMLCY) + { + leaf1->SetBounds( boundBegin, BoundEnd, pos1 - 20., pos1, -isoCenterToMLCDistance, isoCenterToMLCDistance); + leaf2->SetBounds( boundBegin, BoundEnd, pos2, pos2 + 20., -isoCenterToMLCDistance, isoCenterToMLCDistance); + } + leaf1->Update(); + leaf2->Update(); + append->AddInputData(leaf1->GetOutput()); + append->AddInputData(leaf2->GetOutput()); + } + } + append->Update(); + + vtkPolyData* mlcModelPolyData = vtkPolyData::New(); + mlcModelPolyData->ShallowCopy(append->GetOutput()); + + return mlcModelPolyData; } //--------------------------------------------------------------------------- diff --git a/Beams/MRML/vtkMRMLRTBeamNode.h b/Beams/MRML/vtkMRMLRTBeamNode.h index edec76d7c..80aca8948 100644 --- a/Beams/MRML/vtkMRMLRTBeamNode.h +++ b/Beams/MRML/vtkMRMLRTBeamNode.h @@ -85,7 +85,7 @@ class VTK_SLICER_BEAMS_MODULE_MRML_EXPORT vtkMRMLRTBeamNode : public vtkMRMLMode virtual void CreateDefaultTransformNode(); /// Create beam limiting device (MLC and jaws) polydata for model node - virtual vtkPolyData* CreateMultiLeafCollimatorPolyData(); + virtual vtkPolyData* CreateMultiLeafCollimatorModelPolyData(); /// Create transform node that places the beam poly data in the right position based on geometry. /// Always creates a new transform node. diff --git a/Beams/MRML/vtkMRMLRTIonBeamNode.cxx b/Beams/MRML/vtkMRMLRTIonBeamNode.cxx index ae919028a..a2edff435 100644 --- a/Beams/MRML/vtkMRMLRTIonBeamNode.cxx +++ b/Beams/MRML/vtkMRMLRTIonBeamNode.cxx @@ -234,7 +234,7 @@ void vtkMRMLRTIonBeamNode::CreateNewBeamTransformNode() } //--------------------------------------------------------------------------- -vtkPolyData* vtkMRMLRTIonBeamNode::CreateMultiLeafCollimatorPolyData() +vtkPolyData* vtkMRMLRTIonBeamNode::CreateMultiLeafCollimatorModelPolyData() { //TODO: Create beam limiting device poly data here return nullptr; diff --git a/Beams/MRML/vtkMRMLRTIonBeamNode.h b/Beams/MRML/vtkMRMLRTIonBeamNode.h index 5589dab11..0fbd5d1e7 100644 --- a/Beams/MRML/vtkMRMLRTIonBeamNode.h +++ b/Beams/MRML/vtkMRMLRTIonBeamNode.h @@ -63,7 +63,7 @@ class VTK_SLICER_BEAMS_MODULE_MRML_EXPORT vtkMRMLRTIonBeamNode : public vtkMRMLR void CreateDefaultTransformNode() override; /// Create beam limiting device (MLC and jaws) polydata for model node - vtkPolyData* CreateMultiLeafCollimatorPolyData() override; + vtkPolyData* CreateMultiLeafCollimatorModelPolyData() override; /// Create transform node that places the beam poly data in the right position based on geometry. /// Always creates a new transform node.