Files
glocalize/dicomUtils.cpp
T
Giovanni Fattori 1a726009ba dcmutils and cmake
2026-01-05 21:49:52 +01:00

577 lines
22 KiB
C++

#include "dicomUtils.h"
#include <sstream>
#include <algorithm>
#include <cctype>
using std::cout;
using std::endl;
namespace {
static inline std::string Trim(std::string s)
{
auto notSpace = [](unsigned char c) { return !std::isspace(c); };
s.erase(s.begin(), std::find_if(s.begin(), s.end(), notSpace));
s.erase(std::find_if(s.rbegin(), s.rend(), notSpace).base(), s.end());
return s;
}
static inline int ParseIntOr(const std::string& s, int fallback)
{
const std::string t = Trim(s);
if (t.empty()) return fallback;
try
{
size_t idx = 0;
int v = std::stoi(t, &idx);
(void)idx;
return v;
}
catch (...)
{
return fallback;
}
}
static inline double ParseDoubleOr(const std::string& s, double fallback)
{
const std::string t = Trim(s);
if (t.empty()) return fallback;
try
{
size_t idx = 0;
double v = std::stod(t, &idx);
(void)idx;
return v;
}
catch (...)
{
return fallback;
}
}
static inline int GetTagIntOr(const gdcm::Tag& t, const gdcm::DataSet& ds, int fallback)
{
return ParseIntOr(gGetStringValueFromTagStr(t, ds), fallback);
}
static inline double GetTagDoubleOr(const gdcm::Tag& t, const gdcm::DataSet& ds, double fallback)
{
return ParseDoubleOr(gGetStringValueFromTagStr(t, ds), fallback);
}
// Parse a DICOM "DS" multi-value string with 3 components separated by '\\'.
// Returns {BADVALUE,BADVALUE,BADVALUE} if parsing fails.
std::array<double, 3> Parse3Doubles(const std::string& s)
{
std::array<double, 3> out{BADVALUE, BADVALUE, BADVALUE};
if (s.empty()) return out;
std::stringstream ss(s);
std::string token;
for (int i = 0; i < 3; ++i)
{
if (!std::getline(ss, token, '\\')) return out;
try
{
out[static_cast<size_t>(i)] = std::stod(token);
}
catch (...)
{
return {BADVALUE, BADVALUE, BADVALUE};
}
}
return out;
}
} // namespace
//FUNCTION DECLARATION NECESSARY FOR COPY/PASTE FROM vtkGDCMImageReader
// const char *gGetStringValueFromTag(const gdcm::Tag& t, const gdcm::DataSet& ds);
const char *gGetStringValueFromTag(const gdcm::Tag& t, const gdcm::DataSet& ds)
{
// NOTE: legacy API returning pointer into a static buffer.
// Keep for older call sites.
static std::string buffer;
buffer = gGetStringValueFromTagStr(t, ds);
return buffer.c_str();
}
std::string gGetStringValueFromTagStr(const gdcm::Tag& t, const gdcm::DataSet& ds)
{
if (!ds.FindDataElement(t)) return {};
const gdcm::DataElement& de = ds.GetDataElement(t);
const gdcm::ByteValue* bv = de.GetByteValue();
if (!bv) return {};
std::string s(bv->GetPointer(), bv->GetLength());
// DICOM strings are often space/\0 padded.
while (!s.empty() && (s.back() == '\0' || s.back() == ' ' || s.back() == '\r' || s.back() == '\n' || s.back() == '\t'))
s.pop_back();
return s;
}
//GENERAL INDEPENDENT FUNCTION TO CHECK FOR FILE DICOM MODALITY (PLAN, STRUCT, IMAGE) REQUIRES CORRECT PATH
const char * gCheckDICOMModality(const char *filename)
{
gdcm::Reader gR;
gR.SetFileName(filename);//str.toAscii().data());
if(!gR.Read())
return("NODCMFILE");
const gdcm::File &file = gR.GetFile();
const gdcm::DataSet &ds = file.GetDataSet();
return(gGetStringValueFromTag( gdcm::Tag(0x0008,0x0060), ds) ); //RETURN MODALITY
};
int gCheckDICOMModalityToInt(const char *filename){
const char * tmp = gCheckDICOMModality(filename);
//cout<< tmp<<endl;
if( !strcmp(tmp,"RTSTRUCT") )
return RTSTRUCT;
else if ( !strcmp(tmp,"RTPLAN") )
return RTPLAN;
else if( !strcmp(tmp,"CT") )
return IMAGE;
else
return NODCMFILE;
};
IonBeamProperties::IonBeamProperties()
{
BeamNumber = BADVALUE;
BeamName.clear();
SupportId.clear();
SupportType.clear();
GantryAngle = BADVALUE;
IsocenterPosition = {BADVALUE, BADVALUE, BADVALUE};
TablePitchAngle = BADVALUE;
TableRollAngle = BADVALUE;
TableYawAngle = BADVALUE;
TableLatDispl = BADVALUE;
TableLongDispl = BADVALUE;
TableVertDispl = BADVALUE;
}
void IonBeamProperties::PrintSelf(){
cout<< "__ IonBeamProperties print self ** " <<endl;
cout<<" |_ BeamNumber = "<<BeamNumber<<endl;
cout<<" |_ BeamName = "<<BeamName<<endl;
cout<<" |_ SupportType = " <<SupportType<<endl;
cout<<" |_ SupportId = "<<SupportId<<endl;
cout<<" |_ GantryAngle = "<<GantryAngle<<endl;
cout<<" |_ IsocenterPosition = "<<IsocenterPosition[0]<<" "
<<IsocenterPosition[1]<<" "
<<IsocenterPosition[2]<<endl;
cout<<" |_ TableYawAngle = "<<TableYawAngle<<endl;
cout<<" |_ TablePitchAngle = "<<TablePitchAngle<<endl;
cout<<" |_ TableRollAngle = "<<TableRollAngle<<endl;
cout<<" |_ TableLatDispl = "<<TableLatDispl<<endl;
cout<<" |_ TableLongDispl = "<<TableLongDispl<<endl;
cout<<" |_ TableVertDispl = "<<TableVertDispl<<endl;
}
/*-----*/
//CLASS gIonBEAMProperties
RTPlan::RTPlan()
{
NumberOfBeams = 0;
PatientName.clear();
PatientID.clear();
PatientSex.clear();
PatientAge.clear();
PatientBirthDate.clear();
SeriesNumber.clear();
SeriesDescription.clear();
StudyID.clear();
StudyDescription.clear();
Modality.clear();
IsocenterPosition = {BADVALUE, BADVALUE, BADVALUE};
VolumeCenter = {BADVALUE, BADVALUE, BADVALUE};
BeamNumber = BADVALUE;
BeamName.clear();
TableRollAngle = BADVALUE;
TableYawAngle = BADVALUE;
TablePitchAngle = BADVALUE;
TableLatDispl = BADVALUE;
TableVertDispl = BADVALUE;
TableLongDispl = BADVALUE;
SupportType.clear();
SupportId.clear();
PatientOrientation.clear();
}
void RTPlan::PrintSelf(){
cout<< "** RTPlan print self ** " <<endl;
cout<<"PatientName = "<<PatientName<<endl;
cout<<"PatientID = "<<PatientID<<endl;
cout<<"PatientSex = "<<PatientSex<<endl;
cout<<"PatientAge = "<<PatientAge<<endl;
cout<<"PatientBirthDate = "<<PatientBirthDate<<endl;
cout<<"SeriesNumber = "<<SeriesNumber<<endl;
cout<<"SeriesDescription = "<<SeriesDescription<<endl;
cout<<"StudyID = "<<StudyID<<endl;
cout<<"StudyDescription = "<<StudyDescription<<endl;
cout<<"Modality = "<<Modality<<endl;
cout<<"IsocenterPosition = "<<IsocenterPosition[0]<<" "<<
IsocenterPosition[1]<<" "<<
IsocenterPosition[2]<<endl;
cout<<"VolumeCenter = "<<VolumeCenter[0]<<" "<<
VolumeCenter[1]<<" "<<
VolumeCenter[2]<<endl;
cout<<"BeamNumber = "<<BeamNumber<<endl;
cout<<"BeamName = "<<BeamName<<endl;
cout<<"TableRollAngle = "<<TableRollAngle<<endl;
cout<<"TableYawAngle = "<<TableYawAngle<<endl;
cout<<"TablePitchAngle = "<<TablePitchAngle<<endl;
cout<<"TableVertDispl = "<<TableVertDispl<<endl;
cout<<"TableLongDispl = "<<TableLongDispl<<endl;
cout<<"SupportType = "<<SupportType<<endl;
cout<<"SupportId = "<<SupportId<<endl;
cout<<"PatientOrientation = "<<PatientOrientation<<endl;
if(NumberOfBeams > 0)
for(const auto& b : Beams)
if(b) b->PrintSelf();
}
bool RTPlan::fillRTPlan(const char *filertionplan){
LastError.clear();
cout<<"reading filename: "<<filertionplan<<endl;
gdcm::Reader R;
R.SetFileName(filertionplan);
if(!R.Read())
{
LastError = "Cannot read RT Plan file";
cout<<"ERROR: cannot read filertionplan."<<endl;
return false;
}
const gdcm::File &file = R.GetFile();
const gdcm::DataSet &ds = file.GetDataSet();
//GENERAL PATIENT AND STUDY DATE
//PATIENT NAME
PatientName = gGetStringValueFromTagStr(gdcm::Tag(0x0010,0x0010), ds);
// // // PATIENT ID For ex: DICOM (0010,0020) = 1933197
PatientID = gGetStringValueFromTagStr(gdcm::Tag(0x0010,0x0020), ds);
// //
// // // PATIENT AGE For ex: DICOM (0010,1010) = 031Y
PatientAge = gGetStringValueFromTagStr(gdcm::Tag(0x0010,0x1010), ds);
// //
// // // PATIENT SEX For ex: DICOM (0010,0040) = M
PatientSex = gGetStringValueFromTagStr(gdcm::Tag(0x0010,0x0040), ds);
// //
// // // PATIENT BIRTHDATE For ex: DICOM (0010,0030) = 19680427
PatientBirthDate = gGetStringValueFromTagStr(gdcm::Tag(0x0010,0x0030), ds);
// //
// // // SERIES NUMBER For ex: DICOM (0020,0011) = 902
SeriesNumber = gGetStringValueFromTagStr(gdcm::Tag(0x0020,0x0011), ds);
// //
// // // SERIES DESCRIPTION For ex: DICOM (0008,103e) = SCOUT
SeriesDescription = gGetStringValueFromTagStr(gdcm::Tag(0x0008,0x103e), ds);
// //
// // // STUDY ID For ex: DICOM (0020,0010) = 37481
StudyID = gGetStringValueFromTagStr(gdcm::Tag(0x0020,0x0010), ds);
// //
// // // STUDY DESCRIPTION For ex: DICOM (0008,1030) = BRAIN/C-SP/FACIAL
StudyDescription = gGetStringValueFromTagStr(gdcm::Tag(0x0008,0x1030), ds);
// //
// // // STUDY MODALITY For ex: DICOM (0008,0060)= CT
Modality = gGetStringValueFromTagStr(gdcm::Tag(0x0008,0x0060), ds);
//TREATMENT GEOMETRY
//IONI 0x300a,0x00b0
// radiation 0x300ax00c6
//FIND TAG BEAMSequence
//v04Build27 HANDLE PROTON vs CARBONION
bool IsCarbon=false;
//v04Build27 CHECK EXISTANCE SETUP SEQUENCE (PRESENT IN PROTN ABSENT IN IONS)
if(!ds.FindDataElement(gdcm::Tag(0x300a,0x03a2)))
IsCarbon=true;
//QUI HFP vs HFS
//v04 Build29: LOOK FOR SETUP SEQUENCE TO SEARHC FOR PATINET ORIENTATION
// Patient orientation comes from Patient Setup Sequence, when present.
PatientOrientation.clear();
if (ds.FindDataElement(gdcm::Tag(0x300a,0x0180)))
{
const gdcm::DataElement &gSetupSequence=ds.GetDataElement(gdcm::Tag(0x300a,0x0180));
gdcm::SmartPointer<gdcm::SequenceOfItems> sqiPS = gSetupSequence.GetValueAsSQ();
if (sqiPS && sqiPS->GetNumberOfItems() >= 1)
{
const gdcm::DataSet& gPatSetupNest = sqiPS->GetItem(1).GetNestedDataSet();
PatientOrientation = gGetStringValueFromTagStr(gdcm::Tag(0x0018, 0x5100), gPatSetupNest);
}
}
const gdcm::DataElement &gBeamSequence=ds.GetDataElement(gdcm::Tag(0x300a,(IsCarbon==false ? 0x03a2 : 0x00b0)));
// (THESE ARE THE BEAMS)
gdcm::SmartPointer<gdcm::SequenceOfItems> sqi = gBeamSequence.GetValueAsSQ();
if(!sqi || !sqi->GetNumberOfItems())
{
LastError = "Beam sequence not found in RT Plan file";
cout<<"usteria" <<endl;
return false;
}
const int expectedBeams = static_cast<int>(sqi->GetNumberOfItems());
NumberOfBeams = expectedBeams;
// Own and manage beams with RAII.
Beams.clear();
Beams.reserve(static_cast<size_t>(expectedBeams));
//ITERATES ON BEAM NUMBER
for(int i = 0; i < expectedBeams; i++)
{
const gdcm::Item & item = sqi->GetItem(i+1); //ITEM START FROM 1
const gdcm::DataSet& gBeamNestedds = item.GetNestedDataSet(); //THIS IS BEAM DATASET
auto beam = std::make_unique<IonBeamProperties>();
IonBeamProperties* b = beam.get();
//READ BEAM DATA
b->BeamNumber = GetTagIntOr(gdcm::Tag(0x300a, 0x00c0), gBeamNestedds, 0);
b->BeamName = gGetStringValueFromTagStr(gdcm::Tag(0x300a, 0x00c2), gBeamNestedds);
if(IsCarbon == false)
b->SupportType = gGetStringValueFromTagStr(gdcm::Tag(0x300a, 0x0350), gBeamNestedds);
else
b->SupportType = "TABLE";
b->SupportId = gGetStringValueFromTagStr(gdcm::Tag(0x300a, 0x0352), gBeamNestedds);
//gBeams[i]->gSetSupportType("CHAIR");
//READ GEOMETRY
//READ SEQUENCE OF CONTROL POINTS
//v04Build27 DO DIFFERENTLY IF PROTONS OR CARBONS
const gdcm::Tag cpTag(0x300a, IsCarbon==false ? 0x03a8 : 0x0111);
if (!gBeamNestedds.FindDataElement(cpTag))
{
// Skip this beam if control points are missing.
continue;
}
const gdcm::DataElement &gCPSequence=gBeamNestedds.GetDataElement(cpTag);
gdcm::SmartPointer<gdcm::SequenceOfItems> sqicp = gCPSequence.GetValueAsSQ();
if (!sqicp || sqicp->GetNumberOfItems() < 1)
{
continue;
}
const gdcm::DataSet& gCPNestedds = sqicp->GetItem(1).GetNestedDataSet();
//GANTRY ANGLE (NEEDED FOR CHAIR ?)
b->GantryAngle = GetTagIntOr(gdcm::Tag(0x300a, 0x011e), gCPNestedds, 0);
// ISOCENTER POSITION (this is beam position, not patient/volume center).
// DICOM tag (300A,012C) is a multi-value DS string: "x\\y\\z".
{
const std::string isoStr = gGetStringValueFromTagStr(gdcm::Tag(0x300a, 0x012c), gCPNestedds);
const auto iso = Parse3Doubles(isoStr);
b->IsocenterPosition[0] = iso[0];
b->IsocenterPosition[1] = iso[1];
b->IsocenterPosition[2] = iso[2];
}
// Beams[i]->IsocenterPosition[](iso[0],iso[1],iso[2]);
//SUPPORT ANGLE (YAW ANGLE OF SUPPORT WITH RESPECT TO FIXED REFERENCE SYSTEM) AROUND Z AXIS (VERTICAL)
//POSITIVE COUNTER-CLOCKWISE WHEN VIEWED FROM TOP OF Z-Axis
b->TableYawAngle = static_cast<float>(GetTagDoubleOr(gdcm::Tag(0x300a, 0x0122), gCPNestedds, 0.0));
/*if(i==1)
gBeams[i]->gSetTableYawAngle(0);
*/
//TABLE TOP PITCH (ROTATION AROUND X AXIS, i.e. LATERAL AXIS OF TABLE AND BEAM DIRECTION IN FIXED REF SYS)
////v04Build27 IF PROTONS READ IT FROM PLAN; IF CARBONS SET DEFAULT VALUE (0.0) AND FORGET CHAIR
if(IsCarbon==false)
{ gdcm::Attribute <0x300a, 0x0140> pa;
pa.SetFromDataElement(gCPNestedds.GetDataElement( pa.GetTag()));
b->TablePitchAngle = pa.GetValue();
}
else
b->TablePitchAngle = 0.0;
//v04 Build23 SET SUPPORT TYPE CORRECTLY IF PITCH IS 90 (IT IS CHAIR); DO IT FOR ALL BEAMS
// if(i==0)
// { if(Beams[i]->TablePitchAngle()>45)
// strcpy (Beams[i]->SupportType,("CHAIR"));
// }
// else if (i>0)
// { if(Beams[0]->SupportChair()==true)
// Beams[i]->gSetSupportType("CHAIR");
// }
//TABLE TOP ROLL (ROTATION AROUND Y AXIS, i.e. LONGITUDINAL AXIS OF TABLE)
//v04Build27 IF PROTONS READ IT FROM PLAN; IF CARBONS SET DEFAULT VALUE (0.0)
if(IsCarbon==false)
{ gdcm::Attribute <0x300a, 0x0144> ra;
ra.SetFromDataElement(gCPNestedds.GetDataElement(ra.GetTag()));
b->TableRollAngle = ra.GetValue();
}
else
b->TableRollAngle = 0.0;
//WHAT ABOUT TABLE TOP VERTICAL POSITION, LOGITUDINAL AND LATERAL POSITION ? USEFUL SOMEHOW ?
// THEY ARE READ AND USED TO REALIZE VOLUME CENTER ALIGNMENT IN ROOM ISOCENTER
//FIND PATIENT SETUP SEQUENCE (IT IS UNIQUE FOR ALL BEAMS BUT I DO IT BEAM BY BEAM)
//v04Build27 DO NOT LOOK FOR PATIENT SETUPSEQUENCE
/* if(!ds.FindDataElement(gdcm::Tag(0x300a,0x0180)))
throw(gException("CRITICAL: PATIENT SETUP SEQUENCE NOT FOUND IN RT ION PLAN"));
const gdcm::DataElement &gPatientSetupSequence=ds.GetDataElement(gdcm::Tag(0x300a,0x0180));
gdcm::SmartPointer<gdcm::SequenceOfItems> sqpt = gPatientSetupSequence.GetValueAsSQ();
int psuseq=sqpt->GetNumberOfItems(); //CHECK NUMBER OF SETUPSEQUENCE VS BEAM NUMBER
//READ DATA FROM ITEM 1 OR BEAM IS MORE PATIENT SETUP SEQUENCE ARE FOUND (ATTENTION !!!!)
//v03 Build30: READ TABLE POSITIONS FROM SETUPSEQUENCE
const gdcm::DataSet& gPSNestedds = sqpt->GetItem((i+1)<=psuseq ? (i+1) : 1).GetNestedDataSet(); //IF PSTIENT SETUP SEQUENCE < THAN BEAMS USE FIRST
*/
//v04Build27 READ BEAMS ABSOLUTE GEOMETRY NOT FROM SETUP SEQUENCE BUT FROM CONTROL POINT SEQUENCE (NEW VERSION OIS FOR CARBONS)
/*float tablelat=atof(gGetStringValueFromTag(gdcm::Tag(0x300a, 0x01d6),gPSNestedds));
float tablelong=atof(gGetStringValueFromTag(gdcm::Tag(0x300a, 0x01d4),gPSNestedds));
float tablevert=atof(gGetStringValueFromTag(gdcm::Tag(0x300a, 0x01d2),gPSNestedds));*/
float deltatablelat = static_cast<float>(GetTagDoubleOr(gdcm::Tag(0x300a, 0x012a), gCPNestedds, 0.0));
float deltatablelong = static_cast<float>(GetTagDoubleOr(gdcm::Tag(0x300a, 0x0129), gCPNestedds, 0.0));
float deltatablevert = static_cast<float>(GetTagDoubleOr(gdcm::Tag(0x300a, 0x0128), gCPNestedds, 0.0));
//v03 (Build 30) NOW ADD TO TREATMENT BEMA EVENTUAL RELATIVE SHIFTS BETWEEN SETUP SEQUENCE AND STORE IN BEAM STRUCTURE ONLY TO TREATMETN BEAMS
// if(i==0) //THIS IS SETUP BEAM DO NOTHING
//v04Build26 TREAT ALL BEAMS THE SAME WAY (SET-UP+BEAMS)(NEW VERSION OF OIS)
//v04Build27 USE PPS GEOMETRY READ FROM POINT SEQUENCE
{ b->TableLatDispl = deltatablelat;
b->TableLongDispl = deltatablelong;
b->TableVertDispl = deltatablevert;
}
/* else
{ gBeams[i]->gSetTableLatDispl(tablelat);
gBeams[i]->gSetTableLongDispl(tablelong);
gBeams[i]->gSetTableVertDispl(tablevert);
}*/
//v04Build26 DO NOT ADD DELTASHIFTS (NEW VERSION OF OIS)
//v04Build1: CHECK FOR VIRTUAL ISO
//ADD DELTASHIFTS TO TABLE DISPLACEMENTS
//v04Build26 DO NOT ADD DELTASHIFTS (NEW VERSION OF OIS)
/*gBeams[i]->gSetTableLatDispl(tablelat+atof(gGetStringValueFromTag(gdcm::Tag(0x300a, 0x012a),gCPNestedds)));
gBeams[i]->gSetTableLongDispl(tablelong+atof(gGetStringValueFromTag(gdcm::Tag(0x300a, 0x0129),gCPNestedds)));
gBeams[i]->gSetTableVertDispl(tablevert+atof(gGetStringValueFromTag(gdcm::Tag(0x300a, 0x0128),gCPNestedds)));
//UPDATE ISOCENTER POSITION ON TREATMENT BEAM USING CT REFERENCE (LONG IS Z COORDINATE; Y IS VERTICAL; Z I)
//v04Build26 DO NOT ADD DELTASHIFTS (NEW VERSION OF OIS)
gBeams[i]->gSetIsocenterPosition(iso[0]-deltatablelat,iso[1]-deltatablevert,iso[2]-deltatablelong); //SIGNS AND COORDINATE TO BE CHECKED (OK for LONG)
}
*/
//v04Build28 CORRECT ISO POSITION IN CASE OF VIRTUAL ISO
/* if(i<0) //DO THIS ONLY FOR TREATMENT BEAMS
gBeams[i]->gSetIsocenterPosition(iso[0]-(gBeams[i]->gGetTableLatDispl()-gBeams[0]->gGetTableLatDispl()),iso[1]-(gBeams[i]->gGetTableVertDispl()-gBeams[0]->gGetTableVertDispl()),iso[2]-(gBeams[i]->gGetTableLongDispl()-gBeams[0]->gGetTableLongDispl())); //SIGNS AND COORDINATE TO BE CHECKED (OK for LONG)
*/
//UPDATE AT FIRST CNAO PATIENT SET TABLE POSITION FROM PLAN AT THE ISOCENTER POSITION FOR EVERY BEAM
/* gBeams[i]->gSetTableLatDispl(gBeams[i]->gGetIsocenterPosition()[0]);
gBeams[i]->gSetTableLongDispl(gBeams[i]->gGetIsocenterPosition()[1]);gui
gBeams[i]->gSetTableVertDispl(gBeams[i]->gGetIsocenterPosition()[2]);
*/
// FILL gPPS CLASS WITH CURRENT PPS CONFIGURATION UPDATING CONVENTIONS FROM PLAN TO PPS (-90 FOR CONVENTION CT-PPS)
// Beams[i]->gGetCurrentPPS()->gSetPPSXPlan(gBeams[i]->gGetTableLatDispl());
// Beams[i]->gGetCurrentPPS()->gSetPPSYPlan(gBeams[i]->gGetTableLongDispl());
// Beams[i]->gGetCurrentPPS()->gSetPPSYOrig(gBeams[i]->gGetTableLongDispl()); //SAVE ALSO AN ORIGINAL COPY FROM PLAN OF LONGITUFINAL POSITION (FOR INDEXING DIALOG)
// Beams[i]->gGetCurrentPPS()->gSetPPSZPlan(gBeams[i]->gGetTableVertDispl());
//
//v03 Build35: ADD TO TREATMENT BEAMS YAW THE YAW VALUE OF SETUP BEAM TO RECOVER ABSOLUTE VALUES FROM RELATIVE ONES AS SENT BY OIS
if(i>0) // THIS IS TREATMENT BEAM
{ //v04Build03 SET PROPER DELTA ANGLES FOR BEAM ROTATION (IF DELTA IS NEGATIVE SET IT NEGATIVE)
double yaw = b->TableYawAngle;
if(yaw>180)
yaw=yaw-360;
//V04Build26 NEW VERSION OF MOSAIQ OIS DO NOT USE RELATIVE ANGLES
//yaw=yaw+gBeams[0]->gGetTableYawAngle();
b->TableYawAngle = yaw;
double pitch = b->TablePitchAngle;
if(pitch>180)
pitch=pitch-360;
//V04Build26 NEW VERSION OF MOSAIQ OIS DO NOT USE RELATIVE ANGLES
//pitch=pitch+gBeams[0]->gGetTablePitchAngle();
//v04Build23 SET ALL PITCH AT 90 (NOT STABLE) IT IS FOR THE CHAIR
if(pitch>150) pitch=90;
b->TablePitchAngle = pitch;
double roll = b->TableRollAngle;
if(roll>180)
roll=roll-360;
//V04Build26 NEW VERSION OF MOSAIQ OIS DO NOT USE RELATIVE ANGLES
//roll=roll+gBeams[0]->gGetTableRollAngle();
b->TableRollAngle = roll;
}
//HERE 90 DEGREES IS SUMMED IN ORDER TO SOLVE TEH AMBIGUITY BETWEEN TPS REFERENCE AND ROOM/ODEVIS REFERENCE
//0 DEGREE YAW IN TPS BECOMES +90 YAW IN ROOM/PPS MEANING BEAM IS FROM LATERAL
//THIS WE DO IT JUST FOR THE USER INTERFACE DISPLAYING DEGREES OF YAW ROTATION EXPECTED OR TO BE INSERTED IN THE PPS
//v04Build24 ADJUST PPS YAW ANGLE FOR PPS AND CHAIR
// double yaw=gBeams[0]->gIsSupportChair()==false ? gBeams[i]->gGetTableYawAngle()+90.0 : gBeams[i]->gGetTableYawAngle()-90.0;
// //NOW SET +- 180 YAW ROTATION CONVENTION
// if(yaw>180)
// yaw=yaw-360;
// gBeams[i]->gGetCurrentPPS()->gSetPPSRotateDegreePlan(yaw); //CONVERSION FROM CT BASE PLAN TO PPS (ANGLE RANGES -180 +180)
//
// //v04Build23 ATTENZIONE !
// if(gBeams[0]->gIsSupportChair()==false) gBeams[i]->gGetCurrentPPS()->gSetPPSPitchDegreePlan(gBeams[i]->gGetTablePitchAngle());
// else gBeams[i]->gGetCurrentPPS()->gSetPPSPitchDegreePlan(gBeams[i]->gGetTablePitchAngle()-90); //NEED TO SUBTRACT 90 DEGRESS FOR PPS CONFIGURATION : THERE IS THE CHAIR
//
// gBeams[i]->gGetCurrentPPS()->gSetPPSRollDegreePlan(gBeams[i]->gGetTableRollAngle());
//
//
//
// //SET CURRENT PPS CONFIGURATION WITH PLAN CONFIGURATION (THERE I TAKE CARE OF THE CHAIR)
// gBeams[i]->gGetCurrentPPS()->gSetPlanConfig();
/* //UPDATE TABLE DISPLACEMENT ACCORDING TO SIEMENS TPS USING THE ISOCENTER CO-ORDINATESIF DEFINE W.R.T. INDEXING POINT
//MIGHT NEED TO SUBTRACT COORDINATES OF STRUCTURE VOLUMECENTER IF RELATIVE REFERENCE SYSTEM IS NOT DEFINED IN TPS
gBeams[i]->gSetTableLatDispl(gBeams[i]->gGetIsocenterPosition()[0]);
gBeams[i]->gSetTableLongDispl(gBeams[i]->gGetIsocenterPosition()[1]);
gBeams[i]->gSetTableVertDispl(gBeams[i]->gGetIsocenterPosition()[2]);*/
// gBeams[i]->gSetIsocenterPosition(iso[0]- gBeams[i]->gGetCurrentPPS()->getPPSXPlan(),iso[1]- gBeams[i]->gGetCurrentPPS()->getPPSYPlan(),iso[2]- gBeams[i]->gGetCurrentPPS()->getPPSZPlan());
// Store the parsed beam.
Beams.push_back(std::move(beam));
}
// Keep legacy field in sync with actual parsed beams.
NumberOfBeams = static_cast<int>(Beams.size());
return true;
};