CASToR  1.0
Tomographic Reconstruction (PET/SPECT)
iScannerPET.cc
Go to the documentation of this file.
00001 
00002 /*
00003   Implementation of class iScannerPET
00004 
00005   - separators: X
00006   - doxygen: X
00007   - default initialization: X
00008   - CASTOR_DEBUG: none
00009   - CASTOR_VERBOSE: X
00010 */
00011 
00018 #include "iScannerPET.hh"
00019 #include "sOutputManager.hh"
00020 #include "sScannerManager.hh"
00021 
00022 
00023 // =====================================================================
00024 // ---------------------------------------------------------------------
00025 // ---------------------------------------------------------------------
00026 // =====================================================================
00027 /*
00028   \brief iScannerPET constructor. 
00029          Initialize the member variables to their default values.
00030 */
00031 iScannerPET::iScannerPET() : vScanner() 
00032 {
00033   m_scannerType = 0;
00034   m_nbLayers = -1;
00035   m_nbCrystals = -1;
00036   m_minAngleDifference = -1.;
00037   mp_nbCrystalsInLayer = NULL;
00038   mp_nbRings = NULL;
00039   
00040   mp_crystalCentralPositionX = NULL;
00041   mp_crystalCentralPositionY = NULL;
00042   mp_crystalCentralPositionZ = NULL;
00043 
00044   mp_crystalOrientationX = NULL;
00045   mp_crystalOrientationY = NULL;
00046   mp_crystalOrientationZ = NULL;
00047 
00048   mp_sizeCrystalTrans = NULL;
00049   mp_sizeCrystalAxial = NULL;
00050   mp_sizeCrystalDepth = NULL;
00051   mp_meanDepthOfInteraction = NULL;
00052     
00053   mp_positionMatrix_ref = NULL;
00054   mp_positionMatrix_out = NULL;
00055   mp_rotationMatrix = NULL;
00056 
00057   // Variables depending on the acquisition
00058   m_maxRingDiff = -1;
00059 }
00060 
00061 
00062 
00063 // =====================================================================
00064 // ---------------------------------------------------------------------
00065 // ---------------------------------------------------------------------
00066 // =====================================================================
00067 /*
00068   \brief iScannerPET destructor. 
00069 */
00070 iScannerPET::~iScannerPET() 
00071 {
00072   if (mp_sizeCrystalTrans != NULL) delete mp_sizeCrystalTrans; 
00073   if (mp_sizeCrystalAxial != NULL) delete mp_sizeCrystalAxial; 
00074   if (mp_sizeCrystalDepth != NULL) delete mp_sizeCrystalDepth; 
00075   if (mp_meanDepthOfInteraction != NULL)  delete mp_meanDepthOfInteraction;
00076   if (mp_crystalCentralPositionX != NULL) delete mp_crystalCentralPositionX; 
00077   if (mp_crystalCentralPositionY != NULL) delete mp_crystalCentralPositionY; 
00078   if (mp_crystalCentralPositionZ != NULL) delete mp_crystalCentralPositionZ; 
00079   if (mp_crystalOrientationX != NULL) delete mp_crystalOrientationX; 
00080   if (mp_crystalOrientationY != NULL) delete mp_crystalOrientationY; 
00081   if (mp_crystalOrientationZ != NULL) delete mp_crystalOrientationZ; 
00082   if (mp_nbCrystalsInLayer != NULL) delete mp_nbCrystalsInLayer;
00083   if (mp_positionMatrix_ref != NULL) delete mp_positionMatrix_ref;
00084   if (mp_positionMatrix_out != NULL) delete mp_positionMatrix_out;
00085   if (mp_rotationMatrix != NULL) delete mp_rotationMatrix;
00086 }
00087 
00088 
00089 
00090 
00091 // =====================================================================
00092 // ---------------------------------------------------------------------
00093 // ---------------------------------------------------------------------
00094 // =====================================================================
00095 
00096 int iScannerPET::Instantiate(bool a_scannerFileIsLUT)
00097 {
00098   if(m_verbose>=2) Cout("iScannerPET::Instantiate ..."<< endl); 
00099     
00100   sScannerManager* p_scannerManager; 
00101   p_scannerManager = sScannerManager::GetInstance();  
00102   
00103   // Get the number of layers in the scanner
00104   if(ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "number of layers", &m_nbLayers, 1, KEYWORD_MANDATORY))
00105   {
00106     Cerr("***** iScannerPET::Instantiate() -> An error occurred while trying to read a parameter in the scanner header file !" << endl);
00107     return 1;
00108   }
00109 
00110   // Check the correct initialization of the number of layers
00111   if(m_nbLayers<=0)
00112   {
00113     Cerr("***** iScannerPET::Instantiate() -> Incorrect value for the number of layer (should be >0) !" << endl);
00114     return 1;
00115   }
00116 
00117   // Get the number of elements in the system
00118   if(ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "number of elements", &m_nbCrystals, 1, KEYWORD_MANDATORY))
00119   {
00120     Cerr("***** iScannerPET::Instantiate() -> An error occurred while trying to read a parameter in the scanner header file !" << endl);
00121     return 1;
00122   }
00123 
00124   // Instanciation of layer dependent member variables
00125   mp_nbCrystalsInLayer = new int[m_nbLayers];
00126   mp_nbRings = new int[m_nbLayers];
00127   mp_sizeCrystalTrans = new FLTNB[m_nbLayers]; 
00128   mp_sizeCrystalAxial = new FLTNB[m_nbLayers]; 
00129   mp_sizeCrystalDepth = new FLTNB[m_nbLayers];
00130   mp_meanDepthOfInteraction = new FLTNB[m_nbLayers];
00131   
00132   // Instanciation of number of crystals dependent member variables
00133   mp_crystalCentralPositionX = new FLTNB[m_nbCrystals];
00134   mp_crystalCentralPositionY = new FLTNB[m_nbCrystals];
00135   mp_crystalCentralPositionZ = new FLTNB[m_nbCrystals];
00136 
00137   mp_crystalOrientationX = new FLTNB[m_nbCrystals];
00138   mp_crystalOrientationY = new FLTNB[m_nbCrystals];
00139   mp_crystalOrientationZ = new FLTNB[m_nbCrystals];
00140 
00141   // Initialize layer size with default value (=0);
00142   for(int l=0 ; l<m_nbLayers ; l++)
00143   {
00144     mp_sizeCrystalTrans[l] = 0;
00145     mp_sizeCrystalAxial[l] = 0;
00146     mp_sizeCrystalDepth[l] = 0;
00147   }
00148   
00149   // Size of crystals
00150   if (a_scannerFileIsLUT)
00151   {
00152     // For the moment, only the depth is mandatory as the others are not yet implemented
00153     if(ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "crystals size trans", mp_sizeCrystalTrans, m_nbLayers, KEYWORD_OPTIONAL) == 1 ||
00154        ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "crystals size axial", mp_sizeCrystalAxial, m_nbLayers, KEYWORD_OPTIONAL) == 1 ||
00155        ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "crystals size depth", mp_sizeCrystalDepth, m_nbLayers, KEYWORD_MANDATORY) )
00156     {
00157       Cerr("***** iScannerPET::Instantiate() -> An error occurred while trying to read a parameter in the scanner header file !" << endl);
00158       return 1;
00159     }
00160   }
00161   else
00162   {
00163     // Mandatory parameter
00164     if(ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "crystals size trans", mp_sizeCrystalTrans, m_nbLayers, KEYWORD_MANDATORY) ||
00165        ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "crystals size axial", mp_sizeCrystalAxial, m_nbLayers, KEYWORD_MANDATORY) ||
00166        ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "crystals size depth", mp_sizeCrystalDepth, m_nbLayers, KEYWORD_MANDATORY) )
00167     {
00168       Cerr("***** iScannerPET::Instantiate() -> An error occurred while trying to read a parameter in the scanner header file !" << endl);
00169       return 1;
00170     }
00171   }
00172 
00173   // Optional parameters
00174   
00175   // Initialize with default values
00176   for(int l=0 ; l<m_nbLayers ; l++)
00177     mp_meanDepthOfInteraction[l] = -1;
00178   
00179   m_minAngleDifference = 0.;
00180   
00181   // Check if value is provided in the scanner conf file
00182   if(ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "mean depth of interaction", mp_meanDepthOfInteraction, m_nbLayers, KEYWORD_OPTIONAL) == 1||
00183      ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "min angle difference", &m_minAngleDifference, 1, KEYWORD_OPTIONAL) == 1)
00184   {
00185     Cerr("***** iScannerPET::Instantiate() -> An error occurred while trying to read an optional parameter in the scanner header file !" << endl);
00186     return 1;
00187   }
00188 
00189   // Instanciation of objects for matrix calculation during reconstruction
00190   mp_positionMatrix_ref = new oMatrix(3,1);
00191   mp_positionMatrix_out = new oMatrix(3,1);
00192   mp_rotationMatrix = new oMatrix(3,3); 
00193   
00194   return 0;
00195 }
00196 
00197 
00198 
00199 
00200 // =====================================================================
00201 // ---------------------------------------------------------------------
00202 // ---------------------------------------------------------------------
00203 // =====================================================================
00204 /*
00205   \fn BuildLUT
00206   \param a_scannerFileIsLUT : boolean indicating if the file describing 
00207                               the SPECT camera is a generic file (0) or custom Look-up-table (1)
00208   \brief Call the functions to generate the LUT or read the user-made LUT depending on the user choice
00209   \return 0 if success, positive value otherwise
00210   \todo default values to max ring diff
00211 */
00212 int iScannerPET::BuildLUT(bool a_scannerFileIsLUT)
00213 {  
00214   if (m_verbose>=2) Cout("iScannerPET::BuildLUT ..."<< endl); 
00215     
00216   // Either generate the LUT from a generic file, or load the user precomputed LUT
00217   if (!a_scannerFileIsLUT)
00218   {
00219     if (ComputeLUT())
00220     {
00221      Cerr("***** iScannerPET::BuildLUT() -> A problem occured while generating scanner LUT !" << endl);
00222      return 1;
00223     }
00224   }
00225   else 
00226   {
00227     if (LoadLUT())
00228     {
00229       Cerr("***** iScannerPET::BuildLUT() -> A problem occured while loading scanner LUT !" << endl);
00230       return 1;
00231     }
00232   }
00233   
00234   // Set default values to parameters
00235   if(m_maxRingDiff < 0) // Not initialized
00236     m_maxRingDiff = mp_nbRings[0]; //TODO   // How to do with scanner with several layers ? For now setmaximal ring difference number to the number of rings in the first layer by default
00237   
00238   return 0;
00239 }
00240 
00241 
00242 
00243 
00244 // =====================================================================
00245 // ---------------------------------------------------------------------
00246 // ---------------------------------------------------------------------
00247 // =====================================================================
00248 /*
00249   \fn CheckParameters
00250   \brief Check if all parameters have been correctly initialized
00251   \return 0 if success, positive value otherwise
00252 */
00253 int iScannerPET::CheckParameters()
00254 {  
00255   if(m_verbose>=2) Cout("iScannerPET::CheckParameters ..."<< endl); 
00256   
00257   if(m_scannerType == -1)
00258   {
00259     Cerr("***** iScannerPET::CheckParameters() -> Scanner type not initialized !" << endl);
00260     return 1;
00261   }
00262   
00263   if(m_verbose == -1)
00264   {
00265     Cerr("***** iScannerPET::CheckParameters() -> Verbosity not initialized !" << endl);
00266     return 1;
00267   }
00268 
00269   if(m_nbLayers <= 0)
00270   {
00271     Cerr("***** iScannerPET::CheckParameters() -> Incorrect value for the number of layer (should be >0) !" << endl);
00272     return 1;
00273   }
00274 
00275   if(m_nbCrystals <0)
00276   {
00277     Cerr("***** iScannerPET::CheckParameters() -> Number of crystals not initialized !" << endl);
00278     return 1;
00279   }
00280 
00281   if(mp_nbRings == NULL)
00282   {
00283     Cerr("***** iScannerPET::CheckParameters() -> Number of rings not initialized !" << endl);
00284     return 1;
00285   }
00286   
00287   if(mp_nbCrystalsInLayer == NULL)
00288   {
00289     Cerr("***** iScannerPET::CheckParameters() -> Number of crystals in layer(s) not initialized !" << endl);
00290     return 1;
00291   }
00292 
00293   if(mp_crystalCentralPositionX==NULL || mp_crystalCentralPositionY==NULL || mp_crystalCentralPositionZ==NULL)
00294   {
00295     Cerr("***** iScannerPET::CheckParameters() -> Crystals central positions not initialized !" << endl);
00296     return 1;
00297   }
00298   
00299   if(mp_crystalOrientationX==NULL || mp_crystalOrientationY==NULL || mp_crystalOrientationZ==NULL)
00300   {
00301     Cerr("***** iScannerPET::CheckParameters() -> Crystals orientations not initialized !" << endl);
00302     return 1;
00303   }
00304 
00305   if(mp_sizeCrystalTrans==NULL || mp_sizeCrystalAxial==NULL || mp_sizeCrystalDepth==NULL)
00306   {
00307     Cerr("***** iScannerPET::CheckParameters() -> Crystals dimensions not initialized !" << endl);
00308     return 1;
00309   }
00310   
00311   if(mp_meanDepthOfInteraction == NULL)
00312   {
00313     Cout("***** iScannerPET::CheckParameters() -> Mean depth of interaction not initialized !" << endl);
00314     return 1;
00315   }
00316   
00317   if(m_minAngleDifference < 0)
00318   {
00319     Cerr("***** iScannerPET::CheckParameters() -> Minimum angle difference not initialized !" << endl);
00320     return 1;
00321   }
00322 
00323   if(m_maxRingDiff < 0)
00324   {
00325     Cerr("***** iScannerPET::CheckParameters() -> Maximal ring difference not initialized !" << endl);
00326     return 1;
00327   }
00328   
00329   if(m_scannerType == -1)
00330   {
00331     Cerr("***** iScannerPET::CheckParameters() -> Scanner type not initialized !" << endl);
00332     return 1;
00333   }
00334   
00335   if(m_scannerType == -1)
00336   {
00337     Cerr("***** iScannerPET::CheckParameters() -> Scanner type not initialized !" << endl);
00338     return 1;
00339   }
00340 
00341 
00342   m_allParametersChecked = true;
00343   
00344   return 0;
00345 }
00346 
00347 
00348 
00349 
00350 // =====================================================================
00351 // ---------------------------------------------------------------------
00352 // ---------------------------------------------------------------------
00353 // =====================================================================
00354 /*
00355   \fn Initialize
00356   \brief Check general initialization and set several parameters to their default value
00357   \return 0 if success, positive value otherwise
00358 */
00359 int iScannerPET::Initialize()
00360 {
00361   if(m_verbose>=2) Cout("iScannerPET::Initialize ..."<< endl); 
00362   
00363   if(m_allParametersChecked == false)
00364   {
00365     Cerr("***** iScannerPET::Initialize() -> Parameters have not been checked !" << endl);
00366     return 1;
00367   }
00368   
00369   // Set mean depth of interaction to the center of crystal if not initialized by default.
00370   for(int l=0 ; l<m_nbLayers ; l++)
00371     if (mp_meanDepthOfInteraction[l]<0) mp_meanDepthOfInteraction[l] = mp_sizeCrystalDepth[l]/2;
00372     
00373   // Conversion of the minimal angle difference into radians
00374   m_minAngleDifference *= M_PI/180;
00375   
00376   return 0;
00377 }
00378 
00379 
00380 
00381 
00382 // =====================================================================
00383 // ---------------------------------------------------------------------
00384 // ---------------------------------------------------------------------
00385 // =====================================================================
00386 /*
00387   \fn LoadLUT
00388   \brief Load a precomputed scanner LUT
00389   \details Read mandatory data from the header of the LUT. 
00390            Then load the LUT elements for each crystal
00391   \return 0 if success, positive value otherwise
00392 */
00393 int iScannerPET::LoadLUT()
00394 {
00395   if(m_verbose >= 2) Cout("iScannerPET::LoadLUT() -> Start loading LUT from user-provided file... " <<  endl);
00396 
00397   sScannerManager* p_scannerManager; 
00398   p_scannerManager = sScannerManager::GetInstance(); 
00399 
00400   if(ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "number of crystals in layer", mp_nbCrystalsInLayer, m_nbLayers, 1) ||
00401      ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "number of rings in scanner", mp_nbRings, m_nbLayers, 1))
00402   {
00403     Cerr("***** iScannerPET::LoadLUT() -> An error occurred while trying to read a mandatory parameter in the scanner header file  !" << endl);
00404     return 1;
00405   }
00406 
00407   // Open the scanner LUT file
00408   string scanner_lut_file = p_scannerManager->GetPathToScannerFile();
00409   //todo : maybe more practical to directly read the path from the header.
00410   //       Do not put restrictions on the file extension (.lut)
00411   scanner_lut_file = scanner_lut_file.substr(0, scanner_lut_file.find_last_of(".")).append(".lut");
00412 
00413   // Open file
00414   FILE* LUT_file = fopen(scanner_lut_file.c_str(), "rb");
00415   if (LUT_file==NULL)
00416   {
00417     Cerr("***** iScannerPET::LoadLUT() -> Input LUT file '" << scanner_lut_file << "' is missing or corrupted !" << endl);
00418     return 1;
00419   }
00420 
00421   // Read data for each index
00422   int nb_data_read = 0;
00423   for (int i=0; i<m_nbCrystals; i++)
00424   {
00425     FLTNBLUT buffer;
00426     // Read central crystal position X, then Y and Z
00427     nb_data_read += fread(&buffer,sizeof(FLTNBLUT),1,LUT_file);
00428     mp_crystalCentralPositionX[i] = ((FLTNB)buffer);
00429     nb_data_read += fread(&buffer,sizeof(FLTNBLUT),1,LUT_file);
00430     mp_crystalCentralPositionY[i] = ((FLTNB)buffer);
00431     nb_data_read += fread(&buffer,sizeof(FLTNBLUT),1,LUT_file);
00432     mp_crystalCentralPositionZ[i] = ((FLTNB)buffer);
00433     // Read crystal orientation X, then Y and Z
00434     nb_data_read += fread(&buffer,sizeof(FLTNBLUT),1,LUT_file);
00435     mp_crystalOrientationX[i] = ((FLTNB)buffer);
00436     nb_data_read += fread(&buffer,sizeof(FLTNBLUT),1,LUT_file);
00437     mp_crystalOrientationY[i] = ((FLTNB)buffer);
00438     nb_data_read += fread(&buffer,sizeof(FLTNBLUT),1,LUT_file);
00439     mp_crystalOrientationZ[i] = ((FLTNB)buffer);
00440   }
00441 
00442   // Close file
00443   fclose(LUT_file);
00444 
00445   // Check reading
00446   if (nb_data_read!=m_nbCrystals*6)
00447   {
00448     Cerr("***** iScannerPET::LoadLUT() -> Failed to read all data in input LUT file '" << scanner_lut_file << "' !" << endl);
00449     return 1;
00450   }
00451 
00452   if(m_verbose >= 2) Cout("iScannerPET::LoadLUT() -> LUT successfully loaded" << endl);
00453   
00454   return 0;
00455 }
00456 
00457 
00458 
00459 
00460 // =====================================================================
00461 // ---------------------------------------------------------------------
00462 // ---------------------------------------------------------------------
00463 // =====================================================================
00464 /*
00465   \fn ComputeLUT
00466   \brief Compute the LUT of the scanner from a generic (.geom) file
00467   \details Read mandatory data from the geom file. Then compute the LUT elements for each crystal from the geometry described in the file
00468   \details Compute the look-up-tables of the system containing the locations of the scanner elements center in space and their orientations
00469   \todo Add option to inverse rsector if transaxial orientation is counter-clockwise.?
00470   \todo rotation for non-perfectly cylindric scanner (angles along the Z axis)
00471   \return 0 if success, positive value otherwise
00472 */
00473 int iScannerPET::ComputeLUT()
00474 {
00475   if(m_verbose >= 2) Cout("iScannerPET::ComputeLUT() -> Start LUT generation ..." << endl);
00476   
00477   // ============================================================================================================
00478   // Init local variables to recover data from scanner file
00479   // ============================================================================================================
00480   
00481   // Layer dependent variables
00482   int *nb_rsectors_lyr,
00483       *nb_trans_mod_lyr,
00484       *nb_axial_mod_lyr,
00485       *nb_trans_submod_lyr,
00486       *nb_axial_submod_lyr,
00487       *nb_trans_crystal_lyr,
00488       *nb_axial_crystal_lyr;
00489   
00490   FLTNBLUT  *radius_lyr,
00491             *rsector_first_angle_lyr,
00492             *rsector_angular_span_lyr,
00493             *gap_trans_mod_lyr,
00494             *gap_axial_mod_lyr,
00495             *gap_trans_submod_lyr,
00496             *gap_axial_submod_lyr,
00497             *gap_trans_crystal_lyr,
00498             *gap_axial_crystal_lyr;
00499 
00500   nb_rsectors_lyr = new int[m_nbLayers];
00501   nb_trans_mod_lyr = new int[m_nbLayers];
00502   nb_axial_mod_lyr = new int[m_nbLayers];
00503   nb_trans_submod_lyr = new int[m_nbLayers];
00504   nb_axial_submod_lyr = new int[m_nbLayers];
00505   nb_trans_crystal_lyr = new int[m_nbLayers];
00506   nb_axial_crystal_lyr = new int[m_nbLayers];
00507 
00508   radius_lyr = new FLTNBLUT[m_nbLayers];
00509   gap_trans_mod_lyr = new FLTNBLUT[m_nbLayers];
00510   gap_axial_mod_lyr = new FLTNBLUT[m_nbLayers];
00511   gap_trans_submod_lyr = new FLTNBLUT[m_nbLayers];
00512   gap_axial_submod_lyr = new FLTNBLUT[m_nbLayers];
00513   gap_trans_crystal_lyr = new FLTNBLUT[m_nbLayers];
00514   gap_axial_crystal_lyr = new FLTNBLUT[m_nbLayers];
00515   rsector_first_angle_lyr = new FLTNBLUT[m_nbLayers];
00516   rsector_angular_span_lyr = new FLTNBLUT[m_nbLayers];
00517 
00518 
00519   // -------------------------------------------------------------------
00520   // Recover mandatory data from scanner file
00521   sScannerManager* p_scannerManager; 
00522   p_scannerManager = sScannerManager::GetInstance();
00523 
00524   if( ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "number of rsectors",                  nb_rsectors_lyr, m_nbLayers, KEYWORD_MANDATORY) ||
00525       ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "number of crystals transaxial",  nb_trans_crystal_lyr, m_nbLayers, KEYWORD_MANDATORY) ||
00526       ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "number of crystals axial",       nb_axial_crystal_lyr, m_nbLayers, KEYWORD_MANDATORY) ||
00527       ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "scanner radius",                           radius_lyr, m_nbLayers, KEYWORD_MANDATORY) )
00528   {
00529     Cerr("***** iScannerPET::ComputeLUT() -> An error occurred while trying to read a mandatory parameter for scanner LUT generation in the scanner header file  !" << endl);
00530     return 1;
00531   }
00532 
00533   // -------------------------------------------------------------------
00534   // Recover optionnal data from scanner file
00535   
00536   // Initialize defaulf values
00537   for(int l=0 ; l<m_nbLayers ; l++)
00538   {
00539     nb_trans_mod_lyr[ l ] =1;
00540     nb_axial_mod_lyr[ l ] =1;
00541     nb_trans_submod_lyr[ l ] = 1;
00542     nb_axial_submod_lyr[ l ] = 1;
00543     gap_trans_mod_lyr[ l ] = 0.;
00544     gap_axial_mod_lyr[ l ] = 0.;
00545     gap_trans_submod_lyr[ l ] = 0.;
00546     gap_axial_submod_lyr[ l ] = 0.;
00547     gap_trans_crystal_lyr[ l ] = 0.;
00548     gap_axial_crystal_lyr[ l ] = 0.;
00549     rsector_first_angle_lyr[ l ] = 0;
00550     rsector_angular_span_lyr[ l ] = 360.;
00551   }
00552   
00553   if( ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "number of modules transaxial",       nb_trans_mod_lyr, m_nbLayers, KEYWORD_OPTIONAL) == 1 ||
00554       ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "number of modules axial",            nb_axial_mod_lyr, m_nbLayers, KEYWORD_OPTIONAL) == 1 ||
00555       ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "number of submodules transaxial", nb_trans_submod_lyr, m_nbLayers, KEYWORD_OPTIONAL) == 1 ||
00556       ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "number of submodules axial",      nb_axial_submod_lyr, m_nbLayers, KEYWORD_OPTIONAL) == 1 ||
00557       ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "module gap transaxial",             gap_trans_mod_lyr, m_nbLayers, KEYWORD_OPTIONAL) == 1 ||
00558       ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "module gap axial",                  gap_axial_mod_lyr, m_nbLayers, KEYWORD_OPTIONAL) == 1 ||
00559       ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "submodule gap transaxial",       gap_trans_submod_lyr, m_nbLayers, KEYWORD_OPTIONAL) == 1 ||
00560       ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "submodule gap axial",            gap_axial_submod_lyr, m_nbLayers, KEYWORD_OPTIONAL) == 1 ||
00561       ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "crystal gap transaxial",        gap_trans_crystal_lyr, m_nbLayers, KEYWORD_OPTIONAL) == 1 ||
00562       ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "crystal gap axial",             gap_axial_crystal_lyr, m_nbLayers, KEYWORD_OPTIONAL) == 1 ||
00563       ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "rsectors first angle",        rsector_first_angle_lyr, m_nbLayers, KEYWORD_OPTIONAL) == 1 ||
00564       ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "rsectors angular span",              rsector_angular_span_lyr, m_nbLayers, KEYWORD_OPTIONAL) == 1 )
00565   {
00566     Cerr("***** iScannerPET::ComputeLUT() -> An error occurred while trying to read an optionnal parameter for scanner LUT generation in the scanner header file  !" << endl);
00567     return 1;
00568   }
00569   
00570 
00571   for(int lyr=0 ; lyr<m_nbLayers ; lyr++)
00572     mp_nbRings[lyr] = nb_axial_mod_lyr[ lyr ]
00573                     * nb_axial_submod_lyr[ lyr ]
00574                     * nb_axial_crystal_lyr[ lyr ];
00575 
00576 
00577   // Z-SHIFT MANAGEMENT
00578 
00579   // Variables
00580   int rsector_nb_zshift;
00581   FLTNBLUT *zshift;
00582   
00583   int rvalue = ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "rsectors nbZShift", &rsector_nb_zshift, 1, KEYWORD_OPTIONAL);
00584 
00585   // reading error
00586   if( rvalue == 1 ) 
00587   {
00588     Cerr("***** iScannerPET::ComputeLUT() -> Error occurred when trying to read z-shift nb !" << endl);
00589     return 1;
00590   }
00591   // z-shift not found, or == 0
00592   else if( rvalue > 1 || rsector_nb_zshift==0)
00593   {
00594     rsector_nb_zshift = 1; // set to default value (=1)
00595     zshift = new FLTNBLUT[rsector_nb_zshift];
00596     zshift[0] = 0;
00597   }
00598   // (==0, zhift value provided)
00599   else
00600   {
00601     zshift = new FLTNBLUT[rsector_nb_zshift];
00602 
00603     if(ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "rsectors ZShift", zshift, rsector_nb_zshift, KEYWORD_MANDATORY) )
00604     {
00605       Cerr("***** iScannerPET::ComputeLUT() -> No data found about modules z-shift in the scanner header file, whereas z-shift is enabled !" << endl);
00606       return 1;
00607     }
00608   }
00609   
00610   // keep track of the number of crystals already indexed between layer 
00611   int nb_crystals_cur = 0;
00612 
00613   // loop on layer, crystals indexed according to the layer they belong to
00614   for(int lyr=0 ; lyr<m_nbLayers ; lyr++)
00615   {
00616     if (lyr>0)
00617       nb_crystals_cur+=mp_nbCrystalsInLayer[lyr-1];
00618 
00619     int nb_rsectors = nb_rsectors_lyr[lyr],
00620         nb_trans_mod = nb_trans_mod_lyr[lyr],
00621         nb_axial_mod = nb_axial_mod_lyr[lyr],
00622         nb_trans_submod = nb_trans_submod_lyr[lyr],
00623         nb_axial_submod = nb_axial_submod_lyr[lyr],
00624         nb_trans_crystal = nb_trans_crystal_lyr[lyr],
00625         nb_axial_crystal = nb_axial_crystal_lyr[lyr];
00626 
00627         FLTNBLUT radius              = radius_lyr[lyr],
00628                  rsector_first_angle = rsector_first_angle_lyr[lyr],
00629                  angular_span        = rsector_angular_span_lyr[lyr],
00630                  gap_trans_mod       = gap_trans_mod_lyr[lyr],
00631                  gap_axial_mod       = gap_axial_mod_lyr[lyr],
00632                  gap_trans_submod    = gap_trans_submod_lyr[lyr],
00633                  gap_axial_submod    = gap_axial_submod_lyr[lyr],
00634                  gap_trans_crystal   = gap_trans_crystal_lyr[lyr],
00635                  gap_axial_crystal   = gap_axial_crystal_lyr[lyr]; 
00636 
00637     FLTNBLUT size_trans_submod = nb_trans_crystal*mp_sizeCrystalTrans[lyr] + (nb_trans_crystal-1)*gap_trans_crystal;
00638     FLTNBLUT size_axial_submod = nb_axial_crystal*mp_sizeCrystalAxial[lyr] + (nb_axial_crystal-1)*gap_axial_crystal;
00639     FLTNBLUT size_trans_mod = nb_trans_submod*size_trans_submod + (nb_trans_submod-1)*gap_trans_submod;
00640     FLTNBLUT size_axial_mod = nb_axial_submod*size_axial_submod + (nb_axial_submod-1)*gap_axial_submod;
00641   
00642     int nb_mod = nb_axial_mod * nb_trans_mod;
00643     int nb_submod = nb_axial_submod * nb_trans_submod;
00644     int nb_crystal = nb_axial_crystal * nb_trans_crystal;
00645 
00646     // Check the number of crystals < nb elements provided by the scanner configuration file
00647     int nb_crystals = nb_rsectors*nb_mod*nb_submod*nb_crystal + nb_crystals_cur;
00648     if(m_nbCrystals<nb_crystals)
00649     {
00650       Cerr("***** iScannerPET::ComputeLUT() -> Computed number of crystals computed from the geom file ("<< nb_crystals
00651          <<") > not consistent with the total number of crystals (including potential layers) provided in the geom file ("<< m_nbCrystals <<") !" << endl);
00652       return 1;
00653     }
00654     
00655     mp_nbCrystalsInLayer[lyr] = nb_rsectors*nb_mod*nb_submod*nb_crystal;
00656     int number_crystals_in_ring = mp_nbCrystalsInLayer[lyr]/mp_nbRings[lyr];
00657     
00658     // ============================================================================================================
00659     // Main part of the function: Generate the LUT
00660     // ============================================================================================================
00661   
00662     if(m_verbose>=2) Cout("iScannerPET::ComputeLUT : Generate positions for each crystals ..."<< endl); 
00663     
00664     // loop to nb_rsectors+1. crystal_center[0] will be used to gather position of the reference rsector (directly above isocenter)
00665     oMatrix *****crystal_center = new oMatrix ****[nb_rsectors+1]; 
00666   
00667     for(int i = 0; i < nb_rsectors+1 ; i++) 
00668     {
00669       crystal_center[i] = new oMatrix ***[nb_axial_mod * nb_trans_mod];
00670   
00671       for (int j = 0; j<nb_axial_mod*nb_trans_mod ; j++)
00672       {
00673         crystal_center[i][j] = new oMatrix **[nb_axial_submod * nb_trans_submod];
00674   
00675         for (int k = 0; k<nb_axial_submod*nb_trans_submod; k++)
00676         {
00677           crystal_center[i][j][k] = new oMatrix*[nb_axial_crystal * nb_trans_crystal];
00678   
00679           for (int l = 0; l<nb_axial_crystal*nb_trans_crystal; l++)
00680           {
00681             crystal_center[i][j][k][l]  = new oMatrix(3,1);
00682           }
00683         }
00684       }
00685     }
00686 
00687     // Generation of the rotation matrix allowing to compute the position of all the rsectors. 
00688     oMatrix** rotation_mtx = new oMatrix*[nb_rsectors];
00689   
00690     for(int i=0; i<nb_rsectors; i++)
00691       rotation_mtx[i] = new oMatrix(3,3);
00692     
00693     // convert first angle and angular span in radians
00694     FLTNBLUT rsector_first_angle_rad = rsector_first_angle*M_PI/180.;  
00695     FLTNBLUT angular_span_rad = angular_span*M_PI/180.;
00696     
00697     for (int i = 0; i<nb_rsectors; i++)
00698     { 
00699       FLTNBLUT angle = remainderf(rsector_first_angle_rad + ((FLTNBLUT)i)*angular_span_rad/((FLTNBLUT)nb_rsectors), 2.*M_PI);
00700   
00701       rotation_mtx[i]->SetMatriceElt(0,0,cos(angle) );
00702       rotation_mtx[i]->SetMatriceElt(1,0,-sin(angle) );
00703       rotation_mtx[i]->SetMatriceElt(2,0,0);
00704       rotation_mtx[i]->SetMatriceElt(0,1,sin(angle) );
00705       rotation_mtx[i]->SetMatriceElt(1,1,cos(angle) );
00706       rotation_mtx[i]->SetMatriceElt(2,1,0);
00707       rotation_mtx[i]->SetMatriceElt(0,2,0);
00708       rotation_mtx[i]->SetMatriceElt(1,2,0);
00709       rotation_mtx[i]->SetMatriceElt(2,2,1);
00710     }
00711 
00712     // Compute scanner elements positions for the first rsector
00713     for (int i=0; i < nb_mod ; i++)
00714     {
00715       // Define the transaxial and axial edge start positions for the rsector
00716       FLTNBLUT x_start_m = -(nb_trans_mod*size_trans_mod + (nb_trans_mod-1)*gap_trans_mod) / 2.;
00717       FLTNBLUT z_start_m = -(nb_axial_mod*size_axial_mod + (nb_axial_mod-1)*gap_axial_mod) / 2.;
00718   
00719       // Define the transaxial and axial edge start positions for the i-Module in the rsector. 
00720       // Enumeration starting with the transaxial modules.
00721       x_start_m += (i%nb_trans_mod) *  (size_trans_mod + gap_trans_mod);
00722       z_start_m += int(i/nb_trans_mod) * (size_axial_mod + gap_axial_mod);
00723   
00724       for (int j=0 ; j < nb_submod ; j++)
00725       {
00726         FLTNBLUT x_start_sm = x_start_m;
00727         FLTNBLUT z_start_sm = z_start_m;
00728         
00729         x_start_sm += (j%nb_trans_submod) *  (size_trans_submod + gap_trans_submod);
00730         z_start_sm += int(j/nb_trans_submod) * (size_axial_submod + gap_axial_submod);
00731 
00732         for (int k=0 ; k < nb_crystal ; k++) 
00733         {
00734           // Define the transaxial and axial center positions for the j-SubModule (crystal) i-Module of the rsector.
00735           // Enumeration starting with the transaxial submodules.
00736           FLTNBLUT Xcrist = x_start_sm + (k%nb_trans_crystal) * (mp_sizeCrystalTrans[lyr] + gap_trans_crystal) + mp_sizeCrystalTrans[lyr]/2;
00737           FLTNBLUT Ycrist = radius + mp_sizeCrystalDepth[lyr]/2;
00738           FLTNBLUT Zcrist = z_start_sm + int(k/nb_trans_crystal) * (mp_sizeCrystalAxial[lyr] + gap_axial_crystal) + mp_sizeCrystalAxial[lyr]/2;
00739           
00740           crystal_center[0][i][j][k]->SetMatriceElt(0,0,Xcrist);
00741           crystal_center[0][i][j][k]->SetMatriceElt(1,0,Ycrist);
00742           crystal_center[0][i][j][k]->SetMatriceElt(2,0,Zcrist); 
00743         }
00744       }
00745     }
00746 
00747     // ============================================================================================================
00748     // Loop over all the other rsectors.
00749     // Positions of the scanner elements are progressively stored in the LUT file.
00750     // ============================================================================================================
00751     for (int i=0 ; i<nb_rsectors ; i++)
00752       for (int j=0 ; j<nb_mod ; j++)
00753         for (int k=0 ; k<nb_submod ; k++)
00754           for (int l=0 ; l<nb_crystal ; l++)
00755           {
00756             int rs=i, c=l;
00757   
00758             // crystal indexation
00759             int cryID = int(j/nb_trans_mod)*nb_axial_submod*nb_axial_crystal*number_crystals_in_ring // nb indexed crystals in the rings covered by the previous (axial) modules
00760                       + int(k/nb_trans_submod)*nb_axial_crystal*number_crystals_in_ring // nb indexed crystals in the rings covered by the previous (axial) submodules
00761                       + int(l/nb_trans_crystal)*number_crystals_in_ring // nb indexed crystals in the rings covered by the previous (axial) crystals
00762                       + i*nb_trans_mod*nb_trans_submod*nb_trans_crystal // nb indexed crystals in the previous rsectors
00763                       + j%nb_trans_mod*nb_trans_submod*nb_trans_crystal // nb indexed crystals in the previous (transaxial) modules
00764                       + k%nb_trans_submod*nb_trans_crystal // nb indexed crystals in the previous (transaxial) submodules
00765                       + l%nb_trans_crystal // previous crystals in the submodule
00766                       + nb_crystals_cur; // nb indexed crystals in the previous layer(s)
00767 
00768             rotation_mtx[rs]->Multiplication(crystal_center[0][j][k][c], crystal_center[i+1][j][k][c]);
00769             
00770             mp_crystalCentralPositionX[cryID]  = (FLTNB)crystal_center[i+1][j][k][c]->GetMatriceElt(0,0);
00771             mp_crystalCentralPositionY[cryID]  = (FLTNB)crystal_center[i+1][j][k][c]->GetMatriceElt(1,0);
00772             mp_crystalCentralPositionZ[cryID]  = (FLTNB)crystal_center[i+1][j][k][c]->GetMatriceElt(2,0);
00773             mp_crystalCentralPositionZ[cryID] += (FLTNB)zshift[rs%rsector_nb_zshift] ;
00774   
00775             // TODO Rotation for non-perfectly cylindric scanner (angles along the Z axis)
00776             mp_crystalOrientationX[cryID] = (FLTNB)rotation_mtx[rs]->GetMatriceElt(0,1);
00777             mp_crystalOrientationY[cryID] = (FLTNB)rotation_mtx[rs]->GetMatriceElt(0,0);
00778             mp_crystalOrientationZ[cryID] = 0.;
00779           }
00780     
00781     // ============================================================================================================
00782     // End
00783     // ============================================================================================================
00784   
00785     // Delete objects
00786   
00787       for (int i = 0; i < nb_rsectors+1 ; i++)
00788        for (int j = 0; j<nb_axial_mod*nb_trans_mod; j++)
00789         for (int k = 0; k<nb_axial_submod*nb_trans_submod; k++)
00790          for (int l = 0; l<nb_axial_crystal*nb_trans_crystal; l++)
00791          {
00792            delete crystal_center[i][j][k][l];
00793          }
00794   
00795   
00796     for(int i = 0; i < nb_rsectors+1 ; i++)
00797      for (int j = 0; j<nb_axial_mod*nb_trans_mod; j++)
00798       for (int k = 0; k<nb_axial_submod*nb_trans_submod; k++)
00799        {
00800         delete crystal_center[i][j][k];
00801        }
00802   
00803   
00804     for(int i = 0; i < nb_rsectors+1 ; i++)
00805      for (int j = 0; j<nb_axial_mod*nb_trans_mod; j++)
00806       {
00807        delete[] crystal_center[i][j];
00808       }
00809   
00810   
00811     for(int i = 0; i < nb_rsectors+1 ; i++)
00812       delete[] crystal_center[i];
00813   
00814     for(int i = 0; i < nb_rsectors ; i++)
00815       delete rotation_mtx[i];
00816     
00817     delete[] crystal_center;
00818     delete[] rotation_mtx;
00819   }
00820 
00821   // ============================================================================================================
00822   // Save LUT if required
00823   // ============================================================================================================
00824 
00825   if(p_scannerManager->SaveLUTFlag())
00826   {
00827     if(m_verbose>=2) Cout("iScannerPET::ComputeLUT() -> SaveLUT ..." << endl);
00828   
00829     string path_to_geom_file = p_scannerManager->GetPathToScannerFile();
00830     
00831     string path_to_LUT = path_to_geom_file.substr(0, path_to_geom_file.find_last_of("."));
00832     string path_to_header_LUT = path_to_LUT + ".ghscan";
00833     path_to_LUT.append(".glut");
00834     
00835     ofstream LUT_file, header_LUT_file;
00836     
00837     LUT_file.open(path_to_LUT.c_str(), ios::binary | ios::out);
00838      
00839     // Write the corresponding crystal parameters in the LUT
00840     for(int i=0 ; i<m_nbCrystals ; i++)
00841     {
00842       LUT_file.write(reinterpret_cast<char*>(&mp_crystalCentralPositionX[i]), sizeof(FLTNB));
00843       LUT_file.write(reinterpret_cast<char*>(&mp_crystalCentralPositionY[i]), sizeof(FLTNB));
00844       LUT_file.write(reinterpret_cast<char*>(&mp_crystalCentralPositionZ[i]), sizeof(FLTNB));
00845       LUT_file.write(reinterpret_cast<char*>(&mp_crystalOrientationX[i]), sizeof(FLTNB));
00846       LUT_file.write(reinterpret_cast<char*>(&mp_crystalOrientationY[i]), sizeof(FLTNB));
00847       LUT_file.write(reinterpret_cast<char*>(&mp_crystalOrientationZ[i]), sizeof(FLTNB));
00848     }
00849   
00850     LUT_file.close(); 
00851     if(m_verbose>=2) Cout("iScannerPET::ComputeLUT() ->  Binary LUT writing completed" << endl);
00852     
00853     // ============================================================================================================
00854     // --> Write header file
00855     // ============================================================================================================
00856   
00857     if(m_verbose>=2) Cout("iScannerPET::ComputeLUT() ->  Start writing header LUT file..." << endl);
00858     header_LUT_file.open(path_to_header_LUT.c_str(), ios::out); 
00859   
00860     string scanner_name = path_to_geom_file.substr(0, path_to_geom_file.find_last_of("."));
00861     header_LUT_file << "scanner name:" << "    " << GetFileFromPath(scanner_name) << endl; 
00862     header_LUT_file << "modality:" << "    " << "PET" << endl; 
00863     
00864     header_LUT_file << "scanner radius:" << "    " << radius_lyr[0];
00865     for (int lyr=1 ; lyr<m_nbLayers ; lyr++) 
00866       header_LUT_file << "," << radius_lyr[lyr] ; header_LUT_file << endl;
00867       
00868     header_LUT_file << "number of rings in scanner:" << "    " << mp_nbRings[0];
00869     for (int lyr=1 ; lyr<m_nbLayers ; lyr++) 
00870       header_LUT_file << ","<< mp_nbRings[lyr] ; header_LUT_file << endl;
00871     header_LUT_file << "number of elements:" << "    " << m_nbCrystals << endl; 
00872     header_LUT_file << "number of layers:" << "    " << m_nbLayers << endl;
00873     header_LUT_file << "number of crystals in layer(s):" << "    " << mp_nbCrystalsInLayer[0]; // TODO nb_cry_in_layer
00874     for (int lyr=1 ; lyr<m_nbLayers ; lyr++) 
00875       header_LUT_file << ","<< mp_nbCrystalsInLayer[lyr] ; header_LUT_file << endl;
00876       
00877     header_LUT_file << "crystals size depth:" << "    " << mp_sizeCrystalDepth[0];
00878     for (int lyr=1 ; lyr<m_nbLayers ; lyr++) 
00879       header_LUT_file << ","<< mp_sizeCrystalDepth[lyr] ; header_LUT_file << endl;
00880       
00881     header_LUT_file << "crystals size transaxial:" << "    " << mp_sizeCrystalTrans[0];  
00882     for (int lyr=1 ; lyr<m_nbLayers ; lyr++) 
00883       header_LUT_file << ","<< mp_sizeCrystalTrans[lyr] ; header_LUT_file << endl;
00884       
00885     header_LUT_file << "crystals size axial:" << "    " << mp_sizeCrystalAxial[0]; 
00886     for (int lyr=1 ; lyr<m_nbLayers ; lyr++) 
00887       header_LUT_file << ","<< mp_sizeCrystalAxial[lyr] ; header_LUT_file << endl;
00888       
00889    
00890     //default reconstruction parameters
00891     uint32_t def_dim_trans = 0, def_dim_axial = 0; // default number of voxels
00892     FLTNB def_FOV_trans = -1., def_FOV_axial = -1; // computed from modules width and gaps of the 1st layer
00893     
00894     if( ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "voxels number transaxial", &def_dim_trans, 1, KEYWORD_MANDATORY) ||
00895         ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "voxels number axial", &def_dim_axial, 1, KEYWORD_MANDATORY) ||
00896         ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "field of view transaxial", &def_FOV_trans, 1, KEYWORD_MANDATORY) ||
00897         ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "field of view axial", &def_FOV_axial, 1, KEYWORD_MANDATORY) )
00898         {
00899           Cerr("***** iScannerPET::ComputeLUT() -> Error occurred when trying to read transaxial/axial dimensions and voxel sizes from scanner geom file !" << endl);
00900           return 1;
00901         }
00902     
00903     header_LUT_file << "voxels number transaxial:" << "    " << def_dim_trans << endl;  
00904     header_LUT_file << "voxels number axial:" << "    " << def_dim_axial << endl; 
00905   
00906     header_LUT_file << "field of view transaxial:" << "    " << def_FOV_trans << endl;  
00907     header_LUT_file << "field of view axial:" << "    " << def_FOV_axial << endl; 
00908     
00909     header_LUT_file << "min angle difference:" << "    " << m_minAngleDifference << " #deg" << endl;
00910     
00911     header_LUT_file << "mean depth of interaction:" << "    " << mp_meanDepthOfInteraction[0];
00912     for (int lyr=1 ; lyr<m_nbLayers ; lyr++) 
00913       header_LUT_file << ","<< mp_meanDepthOfInteraction[lyr] ;
00914     header_LUT_file << " #optional (default value : center of crystal ). Input value must correspond to the distance from the crystal surface, or negative value if default" << endl;
00915     
00916     header_LUT_file << "description:" << "    " << "LUT generated from geom file: " << GetFileFromPath(p_scannerManager->GetPathToScannerFile()) << endl;
00917     
00918     if(m_verbose>=2) Cout("iScannerPET::ComputeLUT() -> Header LUT file writing completed" << endl);
00919   }
00920   
00921   
00922 
00923   delete nb_rsectors_lyr;
00924   delete nb_trans_mod_lyr;
00925   delete nb_axial_mod_lyr;
00926   delete nb_trans_submod_lyr;
00927   delete nb_axial_submod_lyr;
00928   delete nb_trans_crystal_lyr;
00929   delete nb_axial_crystal_lyr;
00930 
00931   delete radius_lyr;
00932   delete rsector_angular_span_lyr;
00933   delete rsector_first_angle_lyr;
00934   delete zshift;
00935   delete gap_trans_mod_lyr;
00936   delete gap_axial_mod_lyr;
00937   delete gap_trans_submod_lyr;
00938   delete gap_axial_submod_lyr;
00939   delete gap_trans_crystal_lyr;
00940   delete gap_axial_crystal_lyr;
00941 
00942   if(m_verbose >= 2)Cout("iScannerPET::ComputeLUT() -> LUT generation completed" << endl);
00943 
00944   return 0;
00945 }
00946 
00947 
00948 
00949 // =====================================================================
00950 // ---------------------------------------------------------------------
00951 // ---------------------------------------------------------------------
00952 // =====================================================================
00953 /*
00954   \fn GetPositionsAndOrientations
00955   \param a_index1 : index of the 1st crystal 
00956   \param a_index2 : index of the 2nd crystal 
00957   \param ap_Position1[3] : x,y,z cartesian position of center of the 1st crystal
00958   \param ap_Position2[3] : x,y,z cartesian position of center of the 2nd crystal
00959   \param ap_Orientation1[3] : x,y,z components of the orientation vector related to the 1st crystal
00960   \param ap_Orientation2[3] : x,y,z components of the orientation vector related to the 2nd crystal
00961   \param ap_POI1 : x,y,z components of the Point Of Interation related to the 1st crystal
00962   \param ap_POI2 : x,y,z components of the Point Of Interation related to the 2nd crystal
00963   \brief Get the central positions and orientations of the scanner elements from their indices.
00964   \details This method is very general and is used by the vProjector. 
00965            From these positions and orientations, other methods can be used by specific projectors to get specific positions.
00966            Position calculations include POI and mean depth of interaction
00967   \todo some cases depending on POI are not implemented
00968   \return 0 if success, positive value otherwise
00969 */
00970 int iScannerPET::GetPositionsAndOrientations( int a_index1, int a_index2,
00971                                                FLTNB ap_Position1[3], FLTNB ap_Position2[3],
00972                                                FLTNB ap_Orientation1[3], FLTNB ap_Orientation2[3],
00973                                                FLTNB* ap_POI1, FLTNB* ap_POI2 )
00974 {
00975   #ifdef CASTOR_VERBOSE
00976   if(m_verbose>=4) Cout("iScannerPET::GetPositionsAndOrientations ..." << endl);
00977   #endif
00978   
00979   // First check crystals existency
00980   if (a_index1<0 || a_index1>=m_nbCrystals)
00981   {
00982     Cerr("***** iScannerPET::GetPositionsAndOrientations() -> Crystal index 1 (" << a_index1 << ") out of range [0:" << m_nbCrystals-1 << "] !" << endl);
00983     return 1;
00984   }
00985   
00986   if (a_index2<0 || a_index2>=m_nbCrystals)
00987   {
00988     Cerr("***** iScannerPET::GetPositionsAndOrientations() -> Crystal index 2 (" << a_index2 << ") out of range [0:" << m_nbCrystals-1 << "] !" << endl);
00989     return 1;
00990   }
00991 
00992   // The POI specification is as follows:
00993   //  - POI[0] and POI[1] express the local x and y coordinates: x is the tangential axis, and y is colinear to the global z axis.
00994   //    Their origins are in the middle of the scanner elements so they range from minus half to half the element dimensions.
00995   //  - POI[2] express the DOI along the local z axis which is colinear to the crystal orientation (which itself is oriented from
00996   //    the center of the scanner to the exterior).
00997   //    Its origin is at the entrance of the element and thus ranges from 0 to the local z size of the element (crystal depth).
00998   //  - If POI[2] has a negative value, this means that the mean depth of interaction should be considered instead, while still
00999   //    using POI[0] and POI[1].
01000   // Remember here that the crystal position is at its center.
01001 
01002   // Case when POI is not provided (we use the mean depth of interaction)
01003   if (ap_POI1==NULL)
01004   {
01005     FLTNB depth = mp_meanDepthOfInteraction[GetLayer(a_index1)] - mp_sizeCrystalDepth[GetLayer(a_index1)]/2;
01006     ap_Position1[0] = mp_crystalCentralPositionX[a_index1] + depth*mp_crystalOrientationX[a_index1];
01007     ap_Position1[1] = mp_crystalCentralPositionY[a_index1] + depth*mp_crystalOrientationY[a_index1];
01008     ap_Position1[2] = mp_crystalCentralPositionZ[a_index1] + depth*mp_crystalOrientationZ[a_index1];
01009   }
01010   // Case when POI[2] is negative (meaning we only have POI[0] or POI[1] specified and to be taken into account)
01011   else if (ap_POI1[2]<0.)
01012   {
01013     // TODO
01014   }
01015   // Case when only the DOI is provided
01016   else if (ap_POI1[0]==0. && ap_POI1[1]==0.)
01017   {
01018     FLTNB depth = ap_POI1[2] - mp_sizeCrystalDepth[GetLayer(a_index1)]/2;
01019     ap_Position1[0] = mp_crystalCentralPositionX[a_index1] + depth*mp_crystalOrientationX[a_index1];
01020     ap_Position1[1] = mp_crystalCentralPositionY[a_index1] + depth*mp_crystalOrientationY[a_index1];
01021     ap_Position1[2] = mp_crystalCentralPositionZ[a_index1] + depth*mp_crystalOrientationZ[a_index1];
01022   }
01023   // Case when the full POI is taken into account
01024   else
01025   {
01026     // TODO
01027   }
01028 
01029   // Case when POI is not provided (we use the mean depth of interaction)
01030   if (ap_POI2==NULL)
01031   {
01032     FLTNB depth = mp_meanDepthOfInteraction[GetLayer(a_index2)] - mp_sizeCrystalDepth[GetLayer(a_index2)]/2;
01033     ap_Position2[0] = mp_crystalCentralPositionX[a_index2] + depth*mp_crystalOrientationX[a_index2];
01034     ap_Position2[1] = mp_crystalCentralPositionY[a_index2] + depth*mp_crystalOrientationY[a_index2];
01035     ap_Position2[2] = mp_crystalCentralPositionZ[a_index2] + depth*mp_crystalOrientationZ[a_index2];
01036   }
01037   // Case when POI[2] is negative (meaning we only have POI[0] or POI[1] specified and to be taken into account)
01038   else if (ap_POI2[2]<0.)
01039   {
01040     // TODO
01041   }
01042   // Case when only the DOI is provided
01043   else if (ap_POI2[0]==0. && ap_POI2[1]==0.)
01044   {
01045     FLTNB depth = ap_POI2[2] - mp_sizeCrystalDepth[GetLayer(a_index2)]/2;
01046     ap_Position2[0] = mp_crystalCentralPositionX[a_index2] + depth*mp_crystalOrientationX[a_index2];
01047     ap_Position2[1] = mp_crystalCentralPositionY[a_index2] + depth*mp_crystalOrientationY[a_index2];
01048     ap_Position2[2] = mp_crystalCentralPositionZ[a_index2] + depth*mp_crystalOrientationZ[a_index2];
01049   }
01050   // Case when the full POI is taken into account
01051   else
01052   {
01053     // TODO
01054   }
01055 
01056   // Get orientations
01057   ap_Orientation1[0] = mp_crystalOrientationX[a_index1];
01058   ap_Orientation1[1] = mp_crystalOrientationY[a_index1];
01059   ap_Orientation1[2] = mp_crystalOrientationZ[a_index1];
01060   ap_Orientation2[0] = mp_crystalOrientationX[a_index2];
01061   ap_Orientation2[1] = mp_crystalOrientationY[a_index2];
01062   ap_Orientation2[2] = mp_crystalOrientationZ[a_index2];
01063     
01064   // End
01065   return 0;
01066 }
01067 
01068 
01069 
01070 
01071 // =====================================================================
01072 // ---------------------------------------------------------------------
01073 // ---------------------------------------------------------------------
01074 // =====================================================================
01075 /*
01076   \fn GetRdmPositionsAndOrientations
01077   \param a_index1 : index of the 1st crystal 
01078   \param a_index2 : index of the 2nd crystal 
01079   \param ap_Position1[3] : x,y,z cartesian position of center of the 1st crystal
01080   \param ap_Position2[3] : x,y,z cartesian position of center of the 2nd crystal
01081   \param ap_Orientation1[3] : x,y,z components of the orientation vector related to the 1st crystal
01082   \param ap_Orientation2[3] : x,y,z components of the orientation vector related to the 2nd crystal
01083   \brief Get random positions of the scanner elements and their orientations from their indices.
01084   \details - Find the scanner elements described by the two indexes passed in parameters. 
01085   \details - Compute random positions inside the elements described by the indexes passed in parameters
01086   \details - Find the scanner elements described by the two indexes passed in parameters.
01087   \details - Write the corresponding random cartesian coordinates in the positions parameters.
01088   \details Position calculations include POI and mean depth of interaction
01089   \todo fix the possibility to draw LOR outside the actual crystal volume (if mp_meanDepthOfInteraction != 0.5)
01090   \todo some cases depending on POI are not implemented
01091   \return 0 if success, positive value otherwise
01092 */
01093 int iScannerPET::GetRdmPositionsAndOrientations(int a_index1, int a_index2,
01094                                                 FLTNB ap_Position1[3], FLTNB ap_Position2[3],
01095                                                 FLTNB ap_Orientation1[3], FLTNB ap_Orientation2[3] )
01096 {
01097   #ifdef CASTOR_VERBOSE
01098   if(m_verbose>=4) Cout("iScannerPET::GetRdmPositionsAndOrientations ..." << endl);
01099   #endif
01100   
01101   // First check crystals existency
01102   if (a_index1<0 || a_index1>=m_nbCrystals)
01103   {
01104     Cerr("***** iScannerPET::GetRdmPositionsAndOrientations() -> Crystal index 1 (" << a_index1 << ") out of range [0:" << m_nbCrystals-1 << "] !" << endl);
01105     return 1;
01106   }
01107   if (a_index2<0 || a_index2>=m_nbCrystals)
01108   {
01109     Cerr("***** iScannerPET::GetRdmPositionsAndOrientations() -> Crystal index 2 (" << a_index2 << ") out of range [0:" << m_nbCrystals-1 << "] !" << endl);
01110     return 1;
01111   }
01112 
01113   if (mp_sizeCrystalTrans[GetLayer(a_index1)]<=0. || mp_sizeCrystalAxial[GetLayer(a_index1)]<=0. ||
01114       mp_sizeCrystalTrans[GetLayer(a_index2)]<=0. || mp_sizeCrystalAxial[GetLayer(a_index2)]<=0. )
01115   {
01116     Cerr("***** iScannerPET::GetRdmPositionsAndOrientations() -> Crystal sizes are unknown or equal to 0. Crystal dimensions are mandatory for this function !" << endl);
01117     return 1;
01118   }
01119 
01120   sRNG* p_RNG = sRNG::GetInstance(); 
01121 
01122   // Get random numbers for the first crystal
01123   FLTNB rdm_axial1 = p_RNG->GenerateRdmNber();
01124   FLTNB rdm_trans1 = p_RNG->GenerateRdmNber();
01125   FLTNB rdm_depth1 = p_RNG->GenerateRdmNber();
01126 
01127   FLTNB size_crystalTrans1 = mp_sizeCrystalTrans[GetLayer(a_index1)];
01128   FLTNB size_crystalAxial1 = mp_sizeCrystalAxial[GetLayer(a_index1)];
01129   FLTNB size_crystalDepth1 = mp_sizeCrystalDepth[GetLayer(a_index1)];
01130 
01131   FLTNB axial1 = (rdm_axial1-0.5) * size_crystalAxial1;
01132   FLTNB trans1 = (rdm_trans1-0.5) * size_crystalTrans1;
01133   FLTNB depth1 = (rdm_depth1-0.5) * size_crystalDepth1;
01134 
01135   ap_Position1[0] = mp_crystalCentralPositionX[a_index1] 
01136                   + depth1 * mp_crystalOrientationX[a_index1] 
01137                   + trans1 * mp_crystalOrientationY[a_index1] 
01138                   + axial1 * mp_crystalOrientationX[a_index1] * mp_crystalOrientationZ[a_index1];
01139                   
01140   ap_Position1[1] = mp_crystalCentralPositionY[a_index1] 
01141                   + depth1 * mp_crystalOrientationY[a_index1] 
01142                   + trans1 * mp_crystalOrientationX[a_index1] 
01143                   + axial1 * mp_crystalOrientationY[a_index1] * mp_crystalOrientationZ[a_index1];
01144                   
01145   ap_Position1[2] = mp_crystalCentralPositionZ[a_index1] 
01146                   + depth1 * mp_crystalOrientationZ[a_index1] 
01147                   + axial1 * sqrt(1-mp_crystalOrientationZ[a_index1] * mp_crystalOrientationZ[a_index1]);
01148 
01149   // Get orientations
01150   ap_Orientation1[0] = mp_crystalOrientationX[a_index1];
01151   ap_Orientation1[1] = mp_crystalOrientationY[a_index1];
01152   ap_Orientation1[2] = mp_crystalOrientationZ[a_index1];
01153 
01154   // Get random numbers for the second crystal
01155   FLTNB rdm_axial2 = p_RNG->GenerateRdmNber();
01156   FLTNB rdm_trans2 = p_RNG->GenerateRdmNber();
01157   FLTNB rdm_depth2 = p_RNG->GenerateRdmNber();
01158 
01159   FLTNB size_crystalTrans2 = mp_sizeCrystalTrans[GetLayer(a_index2)];
01160   FLTNB size_crystalAxial2 = mp_sizeCrystalAxial[GetLayer(a_index2)];
01161   FLTNB size_crystalDepth2 = mp_sizeCrystalDepth[GetLayer(a_index2)];
01162 
01163   FLTNB axial2 = (rdm_axial2-0.5) * size_crystalAxial2;
01164   FLTNB trans2 = (rdm_trans2-0.5) * size_crystalTrans2;
01165   FLTNB depth2 = (rdm_depth2-0.5) * size_crystalDepth2;
01166 
01167   ap_Position2[0] = mp_crystalCentralPositionX[a_index2] 
01168                   + depth2 * mp_crystalOrientationX[a_index2] 
01169                   + trans2 * mp_crystalOrientationY[a_index2] 
01170                   + axial2 * mp_crystalOrientationX[a_index2] * mp_crystalOrientationZ[a_index2];
01171                   
01172   ap_Position2[1] = mp_crystalCentralPositionY[a_index2] 
01173                   + depth2 * mp_crystalOrientationY[a_index2] 
01174                   + trans2 * mp_crystalOrientationX[a_index2] 
01175                   + axial2 * mp_crystalOrientationY[a_index2] * mp_crystalOrientationZ[a_index2];
01176                   
01177   ap_Position2[2] = mp_crystalCentralPositionZ[a_index2] 
01178                   + depth2 * mp_crystalOrientationZ[a_index2] 
01179                   + axial2 * sqrt(1-mp_crystalOrientationZ[a_index2] * mp_crystalOrientationZ[a_index2]);
01180 
01181   // Get orientations
01182   ap_Orientation2[0] = mp_crystalOrientationX[a_index2];
01183   ap_Orientation2[1] = mp_crystalOrientationY[a_index2];
01184   ap_Orientation2[2] = mp_crystalOrientationZ[a_index2];
01185 
01186   // End
01187   return 0;
01188 }
01189 
01190 
01191 
01192 
01193 // =====================================================================
01194 // ---------------------------------------------------------------------
01195 // ---------------------------------------------------------------------
01196 // =====================================================================
01197 /*
01198   \fn GetPositionWithRandomDepth
01199   \param a_index1 : index of the 1st crystal
01200   \param a_index2 : index of the 2nd crystal
01201   \param ap_Position1[3] : x,y,z cartesian position of the point related to the 1st index (see child function for more details)
01202   \param ap_Position2[3] : x,y,z cartesian position of the point related to the 2st index (see child function for more details)
01203   \brief Get the positions and orientations of scanner elements from their indices, with a random depth.
01204   \details Method for testing purposes. Does not include POI and mean depth of interaction
01205   \return 0 if success, positive value otherwise
01206 */
01207 int iScannerPET::GetPositionWithRandomDepth(int a_index1, int a_index2, FLTNB ap_Position1[3], FLTNB ap_Position2[3])
01208 {
01209   #ifdef CASTOR_VERBOSE
01210   if(m_verbose>=4) Cout("iScannerPET::GetPositionWithRandomDepth ..." << endl);
01211   #endif
01212   
01213   // Get instance of random number generator
01214   sRNG* p_RNG = sRNG::GetInstance(); 
01215   
01216   // Shoot first random number
01217   FLTNB shoot1 = p_RNG->GenerateRdmNber();
01218   // Compute associated depth
01219   FLTNB depth1 =  (shoot1-0.5) * mp_sizeCrystalDepth[GetLayer(a_index1)];
01220   
01221   // Compute associated position
01222   ap_Position1[0] = mp_crystalCentralPositionX[a_index1] + depth1*mp_crystalOrientationX[a_index1];
01223   ap_Position1[1] = mp_crystalCentralPositionY[a_index1] + depth1*mp_crystalOrientationY[a_index1];
01224   ap_Position1[2] = mp_crystalCentralPositionZ[a_index1] + depth1*mp_crystalOrientationZ[a_index1];
01225   
01226   // Shoot second random number
01227   FLTNB shoot2 = p_RNG->GenerateRdmNber();
01228   // Compute associated depth
01229   FLTNB depth2 =  (shoot2-0.5) * mp_sizeCrystalDepth[GetLayer(a_index2)];
01230   // Compute associated position
01231   ap_Position2[0] = mp_crystalCentralPositionX[a_index2] + depth2*mp_crystalOrientationX[a_index2];
01232   ap_Position2[1] = mp_crystalCentralPositionY[a_index2] + depth2*mp_crystalOrientationY[a_index2];
01233   ap_Position2[2] = mp_crystalCentralPositionZ[a_index2] + depth2*mp_crystalOrientationZ[a_index2];
01234   // End
01235   return 0;
01236 }
01237 
01238 
01239 
01240 
01241 // =====================================================================
01242 // ---------------------------------------------------------------------
01243 // ---------------------------------------------------------------------
01244 // =====================================================================
01245 /*
01246   \fn GetTwoCorners
01247   \param a_index1 : index of the 1st crystal 
01248   \param a_index2 : index of the 2nd crystal
01249   \param ap_CornerInf1[3]
01250   \param ap_CornerSup1[3]
01251   \param ap_CornerInf2[3]
01252   \param ap_CornerSup2[3]
01253   \brief Get the cartesian coordinaters of the two opposite corners of a scanner element.
01254   \todo Not implemented yet 
01255   \return 0 if success, positive value otherwise
01256 */
01257 int iScannerPET::GetTwoCorners(int a_index1, int a_index2,
01258                                FLTNB ap_CornerInf1[3], FLTNB ap_CornerSup1[3],
01259                                FLTNB ap_CornerInf2[3], FLTNB ap_CornerSup2[3])
01260 {
01261 
01262   #ifdef CASTOR_VERBOSE
01263   if(m_verbose>=4) Cout("iScannerPET::GetTwoCorners ..." << endl);
01264   #endif
01265   
01266   if (a_index1<0 || a_index1>=m_nbCrystals || a_index2<0 || a_index2>=m_nbCrystals)
01267   {
01268     Cerr("***** iScannerPET::GetTwoCorners() -> Crystal index out of range !" << endl);
01269     return 1;
01270   }
01271 
01272   if (mp_sizeCrystalTrans[GetLayer(a_index1)]<=0. || mp_sizeCrystalAxial[GetLayer(a_index1)]<=0. ||
01273       mp_sizeCrystalTrans[GetLayer(a_index2)]<=0. || mp_sizeCrystalAxial[GetLayer(a_index2)]<=0. )
01274   {
01275     Cerr("***** iScannerPET::GetRdmPositionsAndOrientations() -> Crystal sizes are unknown or equal to 0. Crystal dimensions are mandatory for this function !" << endl);
01276     return 1;
01277   }
01278   
01279   Cerr("***** iScannerPET::GetTwoCorners() -> Not implemented yet !" << endl);
01280   return 1;
01281 }
01282 
01283 
01284 
01285 
01286 // =====================================================================
01287 // ---------------------------------------------------------------------
01288 // ---------------------------------------------------------------------
01289 // =====================================================================
01290 /*
01291   \fn GetLayer
01292   \param a_idx : index of the crystal in the loaded LUT
01293   \brief Get the layer from which the 'a_idx' crystal belongs to
01294   \return layer index
01295 */
01296 int iScannerPET::GetLayer(int a_idx)
01297 {
01298   #ifdef CASTOR_VERBOSE
01299   if(m_verbose>=4) Cout("iScannerSPECTConv::GetLayer ..." << endl);
01300   #endif
01301   
01302   int layer=0;
01303   int sum_crystals = mp_nbCrystalsInLayer[layer];
01304   
01305   // Get the layer which the crystal belongs to
01306   while (a_idx >= sum_crystals)
01307   {
01308     layer++;
01309     sum_crystals += mp_nbCrystalsInLayer[layer];
01310   }
01311   
01312   return layer;
01313 }
01314 
01315 
01316 
01317 
01318 // =====================================================================
01319 // ---------------------------------------------------------------------
01320 // ---------------------------------------------------------------------
01321 // =====================================================================
01322 /*
01323   \fn GetRingID
01324   \param a_idx : index of the crystal in the loaded LUT
01325   \brief Get the ring which the #a_idx crystal belongs to
01326   \todo /!\ Current implementation only works with scanner whose elements are ring-indexed 
01327   \todo Need to find a solution to deal with potential geometry containing several layers of crystals with different number of rings
01328         We could use the geometrical position of each elements to get this information,
01329         however maybe too complicated for potential scanners with non-standard geometry
01330   \return layer index
01331 */
01332 int iScannerPET::GetRingID(int a_idx)
01333 {
01334   #ifdef CASTOR_VERBOSE
01335   if(m_verbose>=4) Cout("iScannerSPECTConv::GetRingID ..." << endl);
01336   #endif
01337   
01338   int layer = GetLayer(a_idx);
01339   int start_index = 0;
01340     
01341   if(layer>0)
01342   {
01343     // Get the index of the first crystal of the layer 
01344     int layer_id = 0;
01345     while(layer_id < layer)
01346     {
01347       start_index += mp_nbCrystalsInLayer[layer_id];
01348       layer_id++;
01349     }
01350   }
01351   
01352   // Get the number of crystals in ring for this layer
01353   int nb_crystals_in_ring = mp_nbCrystalsInLayer[layer]/mp_nbRings[layer];
01354   
01355   // Recover the ring ID
01356   int ringID = (int)((a_idx-start_index)/nb_crystals_in_ring);
01357 
01358   // TODO : Need to know how to deal with potential geometry containing several layers of crystals with different number of rings ?
01359   //        In this actual situation, crystals from different layers could have similar rings, but not similar axial positions
01360   //        which is what we want to know 
01361   
01362   return ringID;
01363 }
01364 
01365 
01366 
01367 
01368 // =====================================================================
01369 // ---------------------------------------------------------------------
01370 // ---------------------------------------------------------------------
01371 // =====================================================================
01372 /*
01373   \fn IsAvailableLOR
01374   \param a_elt1 : index of the 1st crystal
01375   \param a_elt2 : index of the 2nd crystal
01376   \brief Check if the LOR formed by the crystalf whose indices are passed 
01377          in parameters is available according to the scanner restrictions
01378   \details This function is dedicated to analytic projection and list-mode sensitivity image generation
01379            The PET implementation checks the LOR availability according to the minimal (transaxial) angle difference 
01380            and the maximal ring difference between two crystals
01381   \todo min angle difference (currently just system dependent) may be estimated from the FOV requested by the user ?
01382   \todo Restriction for ring_ID NOT safe for user using their own LUT !!!
01383         Perhaps throw warning in this case
01384   \return 1 if the LOR is available, 0 otherwise
01385 */
01386 int iScannerPET::IsAvailableLOR(int a_elt1, int a_elt2)
01387 {
01388   #ifdef CASTOR_VERBOSE
01389   if(m_verbose>=4) Cout("iScannerSPECTConv::IsAvailableLOR ..." << endl);
01390   #endif
01391   
01392   // Get absolute angle between the two crystals
01393   FLTNB abs_angle = mp_crystalOrientationX[a_elt1]*mp_crystalOrientationX[a_elt2] 
01394                   + mp_crystalOrientationY[a_elt1]*mp_crystalOrientationY[a_elt2];
01395   
01396   // Handle boundaries (arg of acos() outside range [-1. ; +1]) 
01397   abs_angle = (abs_angle>1.) ? 1 : abs_angle;
01398   abs_angle = (abs_angle<-1.) ? -1 : abs_angle;
01399   
01400   FLTNB angle_diff = acos(abs_angle);
01401 
01402   // Restrictions on LORs between different rings
01403   int ring_diff = 0;
01404   ring_diff = abs(GetRingID(a_elt1)-GetRingID(a_elt2));
01405 
01406   // Check restrictions
01407   // TODO : Restriction for ring_ID still NOT safe for user using their own LUT !!!
01408   // TODO : min angle difference (currently just system dependent) may be estimated from the FOV requested by the user ?
01409   if ( ring_diff<m_maxRingDiff && angle_diff>=m_minAngleDifference )
01410   {
01411     return 1;
01412   }
01413 
01414   return 0;
01415 }
01416 
01417 
01418 
01419 
01420 // =====================================================================
01421 // ---------------------------------------------------------------------
01422 // ---------------------------------------------------------------------
01423 // =====================================================================
01424 /*
01425   \fn GetGeometricInfoFromDatafile
01426   \brief Retrieve PET geometric informations from the datafile
01427   \details Recover the (axial) max ring difference
01428   \return 0 if success, positive value otherwise
01429 */
01430 int iScannerPET::GetGeometricInfoFromDatafile(string a_pathToDF)
01431 {
01432   if(m_verbose>=2) Cout("iScannerPET::GetGeometricInfoFromDatafile ..." << endl);
01433   
01434   // These values may change depending on the optional parameters from the datafile header
01435   if (ReadDataASCIIFile(a_pathToDF, "Maximum ring difference", &m_maxRingDiff, 1, KEYWORD_OPTIONAL)==1)
01436   {
01437     Cerr("***** iScannerPET::GetGeometricInfoFromDatafile() -> Error while reading max number of ring difference in the header data file '" << endl);
01438     return 1;
01439   }
01440   return 0;
01441 }
01442 
01443 
01444 
01445 
01446 // =====================================================================
01447 // ---------------------------------------------------------------------
01448 // ---------------------------------------------------------------------
01449 // =====================================================================
01450 /*
01451   \fn PROJ_GetPETSpecificParameters
01452   \param ap_maxRingDiff
01453   \brief Set pointers passed in argument with the related PET specific variables
01454          This function is used to recover these values in the datafile object
01455   \return 0 if success, positive value otherwise
01456 */
01457 int iScannerPET::PROJ_GetPETSpecificParameters(int* ap_maxRingDiff)
01458 {
01459   if(m_verbose>=2) Cout("iScannerPET::PROJ_GetPETSpecificParameters ..." << endl);
01460   
01461   // Verify that all parameters have been correctly checked
01462   if(m_allParametersChecked == false)
01463   {
01464     Cerr("***** iScannerSPECTConv::GetSPECTSpecificParameters() -> Parameters have not been checked !" << endl);
01465     return 1;
01466   }
01467   
01468   *ap_maxRingDiff = m_maxRingDiff;
01469   return 0;
01470 }
01471 
01472 
01473 
01474 
01475 // =====================================================================
01476 // ---------------------------------------------------------------------
01477 // ---------------------------------------------------------------------
01478 // =====================================================================
01479 /*
01480   \fn ShowHelp
01481   \brief Display help
01482   \todo Provide informations about PET system initialization ?
01483 */
01484 void iScannerPET::ShowHelp()
01485 {
01486   if(m_verbose>=2) Cout("iScannerPET::ShowHelp ..." << endl);
01487     
01488   cout << "This scanner class is dedicated to the description of PET systems." << endl;
01489 }
 All Classes Files Functions Variables Typedefs Defines