// Peida Interactive   -   PI            R. Ansari 97-99
// Traceur3D (Drawer) pour NTupleInterface  
// LAL (Orsay) / IN2P3-CNRS  DAPNIA/SPP (Saclay) / CEA

#include <stdio.h>
#include "sopnamsp.h"
#include "pintup3d.h"

//#define PERC_GARDE 0.05

//++
// Class	PINTuple3D
// Lib		PIext
// include	pintup3d.h
//
//	Classe de traceur 3D à partir des données
//	d'un objet implémentant l'interface *NTupleInterface*.
//	Les objets "PINTuple3D" peuvent tracer des signes (markers)
//	éventuellement avec des barres d'erreur et une étiquette
//	pour chaque point. Si un attribut de ligne, autre que
//	"PI_NotDefLineAtt" est spécifié, les points sont connectés
//	par une ligne.
//--
//++
// Links	Parents
// PIDrawer3D
//--
//++
// Links	Voir aussi
// NTupleInterface
// PINTuple
//--

//++
// Titre	Constructeur
//--
//++
// PINTuple3D(NTupleInterface* nt, bool ad=false)
//	Constructeur. Si "ad == true", l'objet "nt" est détruit par 
//	le destructeur de l'objet "PINTuple".
//	Note : nt doit être créé par new
//--

 
/* --Methode-- */
PINTuple3D::PINTuple3D(NTupleInterface* nt, bool ad)
: PIDrawer3D()
{
  mNT = nt;
  mAdDO = ad; 
  aScxy = aScz = true;
  UseSizeScale(true, 5);
  UseColorScale(true);
  ConnectPoints(false);
  SelectXYZ(NULL, NULL, NULL);
  SelectWt(NULL);
  SelectErrBar();
  SelectLabel(NULL);
  SetName("NTup3DDrw");
}

PINTuple3D::~PINTuple3D()
{
  if (mAdDO && mNT)  delete mNT;
}

//++
// Titre	Méthodes
//--
//++
// void  SelectXYZ(const char* px, const char* py, const char* pz)
//	Choix des noms de colonnes X,Y,Z définissant les coordonnées des points. 
//	Ces trois colonnes doivent être spécifiées pour obtenir un tracé.
// void  SelectErrBar(const char* erbx=NULL, const char* erby=NULL, const char* erbz=NULL)
//	Choix des noms de colonnes pour le tracé des barres d'erreur.
// void  SelectWt(const char* pw=NULL, int nbins=10)
//	Choix du nom de colonne poids. Dans ce cas, la taille du signe
//	(marker) sera proportionnel à la valeur de cette colonne pour 
//	chaque point.
// void  SelectLabel(const char* plabel)
//	Choix du nom de colonne correspondant à l'étiquette.
//--

/* --Methode-- */
void  PINTuple3D::SelectXYZ(const char* px, const char* py, const char* pz)
{
string name;
if (mNT == NULL)  xK = yK = zK = -1;
if (px == NULL) xK = -1;
else { name = px; xK = mNT->ColumnIndex(name); }
if (py == NULL) yK = -1;
else { name = py; yK = mNT->ColumnIndex(name); }
if (pz == NULL) zK = -1;
else { name = pz; zK = mNT->ColumnIndex(name); }
}

/* --Methode-- */
void  PINTuple3D::SelectWt(const char* pw)
{
if (pw == NULL) wK = -1;  
else { string name = pw;   wK = mNT->ColumnIndex(name);  }
 
if (wK >= 0) mNT->GetMinMax(wK, wMin, wMax);
else  { wMin = 0.; wMax = 1.; }
}

/* --Methode-- */
void  PINTuple3D::SelectLabel(const char* plabel)
{
if (plabel == NULL) lK = -1;
else {  string name = plabel;  lK = mNT->ColumnIndex(name);  }
}

/* --Methode-- */
void  PINTuple3D::SelectErrBar(const char* erbx, const char* erby, const char* erbz)
{
string name;
if (mNT == NULL)  xebK = yebK = zebK = -1;
if (erbx == NULL) xebK = -1;
else { name = erbx;  xebK = mNT->ColumnIndex(name); }
if (erby == NULL) yebK = -1;
else { name = erby;  yebK = mNT->ColumnIndex(name); }
if (erbz == NULL) zebK = -1;
else { name = erbz;  zebK = mNT->ColumnIndex(name); }
}


/* --Methode-- */
void PINTuple3D::UpdateLimits()
{
  if (!mNT) return; 
  if (mNT->NbLines() <= 0)  return;
  if ( (xK < 0) || (yK < 0)  || (zK < 0) )   return;

  // Commencer par trouver nos limites
  double xmin, xmax, ymin, ymax, zmin, zmax;
  xmin = ymin = 9.e19;
  xmax = ymax = -9.e19;
  zmax = zmax = -9.e19;
  mNT->GetMinMax(xK, xmin, xmax);
  mNT->GetMinMax(yK, ymin, ymax);
  mNT->GetMinMax(zK, zmin, zmax);

  ComputeScaleFactor(xmin, xmax, ymin, ymax, zmin, zmax, 
		     aScxy, aScz, mScy, mScz);
  
  // cout << " DEBUG - NTup3D X:" << XMin3() << "-" << XMax3() " s=" << mScx
  //     <<  " Y:" YMin3() << "-" << YMax3() << " 
  xmin *= mScx;  xmax *= mScx; 
  ymin *= mScy;  ymax *= mScy; 
  zmin *= mScz;  zmax *= mScz; 
// Centre du champ en C = (xmin+xmax)/2., (ymin+ymax)/2  (zmin+zmax)*0.5
// Distance D = Max(xmax-xmin,ymin-ymax)*2
// Observateur en O = X+D, Yc+2*D 
  double D = xmax-xmin;
  if (D < (ymax-ymin))  D = ymax-ymin;
  D *= 1.4;
  
  Set3DView((xmin+xmax)/2., (ymin+ymax)/2, (zmin+zmax)/2., 
            (xmin+xmax)/2.+D , (ymin+ymax)/2.-2.5*D , zmin+(zmax-zmin)*0.85, 0.25, 0.25);  

  Set3DBox(xmin, xmax, ymin, ymax, zmin, zmax, mScx, mScy, mScz);

  /*
  x3Min = xmin;  // - PERC_GARDE*(xmax-xmin);
  x3Max = xmax;  // + PERC_GARDE*(xmax-xmin);
  y3Min = ymin;  // - PERC_GARDE*(ymax-ymin);
  y3Max = ymax;  // + PERC_GARDE*(ymax-ymin);
  z3Min = zmin;  // - PERC_GARDE*(zmax-zmin);
  z3Max = zmax;  // + PERC_GARDE*(zmax-zmin);
  */


//  printf("PINTuple3D::UpdateLimits() : %g .. %g  %g .. %g  %g .. %g (%g) \n", xmin,xmax,ymin,ymax,zmin,zmax,D);
//  printf("PINTuple3D::UpdateLimits() :  %g %g %g << %g %g %g \n", 
//         (xmin+xmax)/2., (ymin+ymax)/2, (zmin+zmax)*0.5, 
//         (xmin+xmax)/2.+D , (ymin+ymax)/2.+2.*D , zmin+(zmax-zmin)*0.1);
}


/* --Methode-- */
void PINTuple3D::Draw(PIGraphicUC* g, double xmin, double ymin, double xmax, double ymax)
{
double xp,yp,zp,wp,xer,yer,zer;
double xl,yl,zl;
int nok;

// On trace les axes - En attendant de faire mieux - Reza 8/12/98
if (axesFlags != kAxesNone)  DrawAxes(g);

if (!mNT) return;
if ( (xK < 0) || (yK < 0) || (zK < 0) )  return;

#if defined(CC_HAS_RTTI_SUPPORT)
PIGraphic3D* g3 = dynamic_cast<PIGraphic3D*>(g);
#else
PIGraphic3D* g3 = (PIGraphic3D*)(g);
#endif

if (GetGraphicAtt().GetLineAtt() == PI_NotDefLineAtt)  g3->SelLine(PI_ThinLine);

//  Pour tracer des markers avec taille fonction de Wt (poids)
double dw = (wMax-wMin)/nWbins;
if (dw < 1.e-19) dw = 1.e-19;
//  Pour tracer des markers avec couleur en fonction de Wt (poids)
PIColorMap * cmap = NULL;
double dwc = 1.;
double nwc = 1.;
bool revcmap;
CMapId mcmapid = GetGraphicAtt().GetColMapId(revcmap);
if( colorScale && (wK >= 0) && (mcmapid != CMAP_OTHER) ) {
  cmap = new PIColorMap(mcmapid);
  cmap->ReverseColorIndex(revcmap);
  nwc = cmap->NCol();
  dwc = (wMax-wMin)/nwc;
}

int msz,sz;

PIMarker mmrk = GetGraphicAtt().GetMarker();
PIMarker mrk;
if (wK >= 0)  mrk = (mmrk != PI_NotDefMarker) ? mmrk : PI_CircleMarker;
else   mrk = (mmrk != PI_NotDefMarker) ? mmrk : PI_DotMarker;
msz = GetGraphicAtt().GetMarkerSz();
if (msz < 1) msz = 1;
g->SelMarker(msz, mrk);

nok = 0;  
xp = yp = zp = xl = yl = zl = 0;
for (int i=0; i<mNT->NbLines(); i++) {
  xl = xp;  yl = yp;  zl = zp;
  xp = mNT->GetCell(i, xK);
  yp = mNT->GetCell(i, yK);
  zp = mNT->GetCell(i, zK);

// Taille - couleur de marker en fonction du poids
  if (wK >= 0) wp = mNT->GetCell(i, wK);
  if (mrkSzScale && (wK >= 0)) {   // Changement de taille
    sz = (int)((wp-wMin)/dw);
    if (sz < 0) sz = 0;
    if (sz > nWbins)  sz = nWbins;
    sz += msz;
    if (sz < 2)  g->SelMarker(sz, PI_DotMarker);
    else g->SelMarker(sz, mrk);
  }
// Couleur du marker en fonction du poids
  if( colorScale && (wK >= 0) && cmap ) {
    int cid = (int)((wp-wMin)/dwc);
    if (cid < 0) cid = 0;
    if (cid >= nwc)  cid = nwc-1;
    g->SelForeground(*cmap, cid);
  }

  if ( (i > 0) && connectPts ) // On relie les points ...
    g3->DrawLine3D(ScaleX(xl), ScaleY(yl), ScaleZ(zl), ScaleX(xp), ScaleY(yp), ScaleZ(zp));
  nok++;
  if ( xebK >= 0 ) {
    xer = mNT->GetCell(i, xebK);
    g3->DrawLine3D(ScaleX(xp-xer), ScaleY(yp), ScaleZ(zp), ScaleX(xp+xer), ScaleY(yp), ScaleZ(zp) );
  }
  if ( yebK >= 0 ) {
    yer = mNT->GetCell(i, yebK);
    g3->DrawLine3D(ScaleX(xp), ScaleY(yp-yer), ScaleZ(zp), ScaleX(xp), ScaleY(yp+yer), ScaleZ(zp) );
  }
  if ( zebK >= 0 ) {
    zer = mNT->GetCell(i, zebK);
    g3->DrawLine3D(ScaleX(xp), ScaleY(yp), ScaleZ(zp-zer), ScaleX(xp), ScaleY(yp), ScaleZ(zp+zer));
  }
  // Trace du marker
  if ((wK >= 0)||(lK < 0)||(mmrk != PI_NotDefMarker))  
    g3->DrawMarker3D(ScaleX(xp), ScaleY(yp), ScaleZ(zp)); 
  // Trace eventuel du label
  if (lK >= 0) g3->DrawString3D(ScaleX(xp), ScaleY(yp), ScaleZ(zp), 
				mNT->GetCelltoString(i, lK).c_str());
}

return;
}

/*  La methode DecodeOptionString permet de decoder un ensemble d'options 
    et de parametre d'affichage specifie sous forme d'un vecteur de string.
    Si rmdecopt == true, les options decodees sont supprimees du vecteur 
    de string fourni en entree - ce qui permet l'enchainement eventuel 
    de plusieurs decodages de string.
    Les options peuvent etre sous forme de flag : "connectpoints" "noconnectpoints" 
    ou plus complexes, par exemple "dynamic=-3,3"
    Rc: La methode renvoie le nombre d'options decodees 
*/

/* --Methode-- */
int PINTuple3D::DecodeOptionString(vector<string> & opt, bool rmdecopt)
{
  int optsz1 = opt.size();
  if(optsz1<1)  return(0);
  // On appelle d'abord le decodage de la classe PIDrawer de laquelle
  // on herite. (Pas obligatoire) on decode donc ici les attributs de
  // couleur, fontes ...
  int ndec1 = PIDrawer3D::DecodeOptionString(opt, rmdecopt);
  if(optsz1-ndec1<1) return(ndec1);  // si tout a ete decode

  vector<string> udopt;  // On gardera ici les options non decodees
  unsigned int k = 0;
  int ndec = opt.size();
  for( k=0; k<opt.size(); k++ ) {
    string opts = opt[k];
    if (opts == "connectpoints") ConnectPoints(true);
    else if (opts == "noconnectpoints") ConnectPoints(false);
    else if (opts == "colorscale") UseColorScale(true);
    else if (opts == "nocolorscale") UseColorScale(false);
    else if (opts == "sizescale") UseSizeScale(true);
    else if (opts == "nosizescale") UseSizeScale(false);
    else if (opts.substr(0,10) == "sizescale=") { 
      int nbn = atoi(opts.substr(10).c_str());
      UseSizeScale(true, nbn);
    }
    else {
      // Si option non decode
      ndec--;
      // S'il faut supprimer les options decodees
      if (rmdecopt)  udopt.push_back(opts);
    }
  }  
  // S'il faut supprimer les options decodees, on remplace l'argument opt
  // par le vecteur des options non decodees.
  if (rmdecopt)  opt = udopt;
  return(ndec+ndec1);  
}

int PINTuple3D::OptionToString(vector<string> & opt) const
{
 PIDrawer3D::OptionToString(opt);

 if(connectPts) opt.push_back("connectpoints");
   else opt.push_back("noconnectpoints");

 if(colorScale) opt.push_back("colorscale");
   else opt.push_back("nocolorscale");

 if(mrkSzScale) {
   char str[256];
   if(nWbins>0) {sprintf(str,"sizescale=%d",nWbins); opt.push_back(str);}
     else opt.push_back("sizescale");
 } else opt.push_back("nosizescale");

 return 1;
}

/* La methode GetOptionsHelpInfo(string& info) renvoie une chaine 
   avec la description des options comprises par ce drawer  
   Note: Il est preferable de ne pas initialiser la chaine 
   string info au depart, afin de permettre de mettre bout a 
   bout les aides de differents Drawer */

/* --Methode-- */
void PINTuple3D::GetOptionsHelpInfo(string& info)
{
info += " ---- PINTuple3D options help info : \n" ;
info += "  connectpoints: The points are connected by a line \n";
info += "  noconnectpoints (this is the default) \n";
info += "  colorscale/nocolorscale (Use color scale for weight) \n";
info += "  sizescale/sizescale=nbins/nosizescale (Use marker size for weight) \n";
// On recupere ensuite la chaine info de la classe de base
PIDrawer3D::GetOptionsHelpInfo(info);
return;
}

