// Module PI : Peida Interactive PIDrawer3D PIDraw3DWdg
// Drawer  3D                    R. Ansari 06/98
// LAL (Orsay) / IN2P3-CNRS  DAPNIA/SPP (Saclay) / CEA

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include "sopnamsp.h"
#include "pi3ddrw.h"


// ......................................................................
// ........................  Classe PIDrawer3D ..........................
// ......................................................................
//++
// Class	PIDrawer3D
// Lib		PI
// include	pidrawer.h
//
//	Classe pour la création d'objets, capables de produire
//	un dessin 3D dans une composante graphique.
//--
//++
// Links	Parent
// PIDrawer
//--
//++
// Links	Voir aussi
// PIGraphic3D
// PIDraw3DWdg
//--

//++
// Titre	Constructeur, méthodes
//--
//++
// PIDrawer3D(bool asxy=false, asz=false)
//	Constructeur
// void Set3DView_Obs(double xo, double yo, double zo, double teta, double phi, double psi, -
//               double dax, double day, double co, double dco=0.2)
//	Définition de la vue 3D. Voir "PIGraphic3D::Set3DCS_Obs(...)"
// void Set3DView(double xc, double yc, double zc, double xo, double yo, double zo, -
//                double dax, double day, double dco=0.2, double psi=0.)
//	Définition de la vue 3D. Voir "PIGraphic3D::Set3DCS(...)"
// bool Get3DView(double& xc, double& yc, double& zc, double& xo, double& yo, double& zo, -
//                double& teta, double& phi, double& psi, double& dax, double& day, double& co, double& dco)
//	Renvoie la définition de la Vue-3D. Retourne "true " si définie par "Set3DView_Obs()"
// void Set3DBox(double xmin, double xmax, double ymin, double ymax, double zmin, double zmax, -
//               double scx=1., double scy=1., double scz=1.)
//	Définition de la boite 3D.
// void ComputeScaleFactor(double xmin, double xmax, double ymin, double ymax,
//                         double zmin, double zmax, bool asxy, bool asz,
//                         double& scy, double& scz)
//	Calcul des facteurs d'echelles scy, scz
//	Si asxy == true : Application d'un facteur d'échelle pour égaliser 
//	le rapport d'aspect des deux axes X et Y
//	Si asz == true : Application d'un facteur d'échelle pour égaliser 
//	le rapport d'aspect de l'axe Z par rapport a XY.
//
// void Draw3DBoxe(PIGraphic3D* g3, double xx1, double xx2, double yy1, double yy2,  -
//                 double zz1, double zz2, double xl1, double xl2, -
//                 double yl1, double yl2, double zl1, double zl2)
//	Trace une boîte avec les coordonnées matérialisant la vue 3D.
//--

/* --Methode-- */
PIDrawer3D::PIDrawer3D()
        : PIDrawer()
{
  xO = yO = 0.; zO = -10.;
  xC = yC = zC = 0.;
  tO = fO = pO = 0.; 
  daxO = dayO = 0.25;
  lCO = 10.;  dlCO = 3.;  
  v3D_obs = false;
  Set3DBox(-1., 1., -1., 1., -1., 1.);
  SetAutoScale(false, false);
}

/* --Methode-- */
PIDrawer3D::~PIDrawer3D()
{
}

/* --Methode-- */
void PIDrawer3D::Set3DView_Obs(double xo, double yo, double zo, double teta, double phi, double psi, 
                               double dax, double day, double co, double dco)
{
  tO = teta;  fO = phi;  pO = psi;
  daxO = dax;  dayO = day;
  lCO = co;   dlCO = dco;
  v3D_obs = true;
}

/* --Methode-- */
void PIDrawer3D::Set3DView(double xc, double yc, double zc, double xo, double yo, double zo, 
                           double dax, double day, double dco, double psi)
{
  xC = xc;  yC = yc;  zC = zc;
  xO = xo;  yO = yo;  zO = zo;
  pO = psi;
  daxO = dax;  dayO = day;
  dlCO = dco;
  v3D_obs = false;
}

/* --Methode-- */
bool PIDrawer3D::Get3DView(double& xc, double& yc, double& zc, double& xo, double& yo, double& zo,
                           double& teta, double& phi, double& psi, double& dax, double& day, double& co, double& dco)
{
  xo = xO;  yo = yO;  zo = zO;
  xc = xC;  yc = yC;  zc = zC;
  teta = tO;  phi = fO;  psi = pO;
  dax = daxO;   day = dayO;
  co = lCO;     dco = dlCO;
  return(v3D_obs);
}
/* --Methode-- */
void PIDrawer3D::Set3DBox(double xmin, double xmax, double ymin, double ymax,
			  double zmin, double zmax, double scx, double scy, double scz)
{
  x3Min = xmin;  x3Max = xmax;
  y3Min = ymin;  y3Max = ymax;
  z3Min = zmin;  z3Max = zmax;
  mScx = scx;  mScy = scy;  mScz = scz;
}

/* --Methode-- */
void PIDrawer3D::ComputeScaleFactor(double xmin, double xmax, double ymin, double ymax,
				    double zmin, double zmax, bool asxy, bool asz,
				    double& scy, double& scz)
{
  scy = scz = 1.;
  if (!asxy && !asz) return;
  double scm=1.;
  if ((ymax-ymin) <= 1.e-19)  ymax = ymin + 1.e-19;
  if (asxy)  { 
    scy = (xmax-xmin)/(ymax-ymin);
    scm = xmax-xmin;
    }
  else scm =  ((xmax-xmin) > (ymax-ymin)) ? (xmax-xmin)  : (ymax-ymin);
  if ((zmax-zmin) <= 1.e-19)  zmax = zmin + 1.e-19;
  if (asz) scz = scm/(zmax-zmin);
  
}

/* --Methode-- */
PIGraphicUC* PIDrawer3D::SetDrwWdg(PIBaseWdgGen* drw, int x0, int y0, int dx, int dy, PIGraphicGen* g)
{
  mBWdg = drw;
  xW0 = x0;   yW0 = y0;
  xWd = dx;   yWd = dy;
  if (mGrUC)  delete mGrUC;
  PIGraphic3D* gr3d;
  gr3d = new PIGraphic3D(g, x0, y0, dx, dy);
  if (v3D_obs)  gr3d->Set3DCS_Obs(xO, yO, zO, tO, fO, pO, daxO, dayO, lCO, dlCO);
  else  gr3d->Set3DCS(xC,yC,zC, xO, yO, zO, daxO, dayO, dlCO, pO);
  if (mFgHighLight) gr3d->SelGOMode(PI_GOInvert);  // $CHECK$ A changer , Reza 07/2001
  mGrUC = gr3d;
  return(gr3d); 
}

/* --Methode-- */
void PIDrawer3D::DrawAxes(PIGraphicUC* g)
{
  if (axesFlags == kAxesNone) return;
#if defined(CC_HAS_RTTI_SUPPORT)
  PIGraphic3D* g3 = dynamic_cast<PIGraphic3D*>(g);
#else
  PIGraphic3D* g3 = (PIGraphic3D*)(g);
#endif
  Draw3DBoxe(g3, XMin3(), XMax3(), YMin3(), YMax3(), ZMin3(), ZMax3(),
	     XMin3()/mScx, XMax3()/mScx, YMin3()/mScy, YMax3()/mScy, 
	     ZMin3()/mScz, ZMax3()/mScz);  
}

/* --Methode-- */
void PIDrawer3D::Draw3DBoxe(PIGraphic3D* g3, 
                            double xx1, double xx2, double yy1, double yy2, 
                            double zz1, double zz2, double xl1, double xl2, 
                            double yl1, double yl2, double zl1, double zl2)
{
  if (mGrAtt.GetLineAtt() == PI_NotDefLineAtt)    g3->SelLine(PI_ThinLine);
  // RZZZZZ  if (mGrAtt. == PI_NotDefFontSize)    g3->SelFontSz((YMax()-YMin())/150.);

  PIGrCoord x1,y1,z1,x2,y2,z2;
  char strg[64];
  int k;
  int NTicks = 5;  // Nb de ticks sur chaque axe

  x1 = xx1;  y1 = yy1;  z1 = zz1;
  x2 = xx2;   y2 = yy1;   z2 = zz1; 
  g3->DrawLine3D(x1, y1, z1, x2, y2, z2);
  y1 = y2 = yy2;
  g3->DrawLine3D(x1, y1, z1, x2, y2, z2);
  z1 = z2 = zz2;
  g3->DrawLine3D(x1, y1, z1, x2, y2, z2);
  y1 = y2 = yy1;  
  g3->DrawLine3D(x1, y1, z1, x2, y2, z2);

// Trace des ticks et labels axe X :
  x1 = x2 = xx1;   y1 = y2 = yy1;  z1 = z2 = zz1;
  for (k=0; k<NTicks; k++) {
    x1 = x2 = (xx1+(double)k*(xx2-xx1)/(NTicks-1.));
    z1 = zz1; z2 = zz1-0.02*(zz2-zz1);
    y1 = y2 = yy1;
    g3->DrawLine3D(x1, y1, z1, x2, y2, z2);
    z2 = zz1-0.05*(zz2-zz1); 
    y2 = yy1-0.075*(yy2-yy1);  
    sprintf(strg,"%.2g", xl1+(double)k*(xl2-xl1)/(NTicks-1.));
    g3->DrawString3D(x2, y2, z2, strg);
    }
  x2 = xx2+0.075*(xx2-xx1);  y2 = yy1;   z2 = zz1-0.05*(zz2-zz1); 
  g3->DrawString3D(x2, y2, z2, "X");

  x1 = xx1;  y1 = yy1;  z1 = zz1;
  x2 = xx1;   y2 = yy2;   z2 = zz1; 
  g3->DrawLine3D(x1, y1, z1, x2, y2, z2);
  x1 = x2 = xx2;
  g3->DrawLine3D(x1, y1, z1, x2, y2, z2);
  z1 = z2 = zz2;
  g3->DrawLine3D(x1, y1, z1, x2, y2, z2);
  x1 = x2 = xx1;  
  g3->DrawLine3D(x1, y1, z1, x2, y2, z2);
  x1 = x2 = xx2;   y1 = y2 = yy1;  z1 = z2 = zz1;
  for (k=1; k<NTicks; k++) {   // Attention, par le tick 0 pour Y
    y1 = y2 = (yy1+(double)k*(yy2-yy1)/(NTicks-1.));
    z2 = zz1-0.02*(zz2-zz1);
    x1 = x2 = xx2; 
    g3->DrawLine3D(x1, y1, z1, x2, y2, z2);
    z2 = zz1-0.05*(zz2-zz1); 
    x2 = xx2+0.075*(xx2-xx1);  
    sprintf(strg,"%.2g", yl1+(double)k*(yl2-yl1)/(NTicks-1.));
    g3->DrawString3D(x2, y2, z2, strg);
    }
  x2 = xx2;  y2 = yy2+0.075*(yy2-yy1);   z2 = zz1-0.05*(zz2-zz1); 
  g3->DrawString3D(x2, y2, z2, "Y");

  x1 = xx1;  y1 = yy1;  z1 = zz1;
  x2 = xx1;   y2 = yy1;   z2 = zz2; 
  g3->DrawLine3D(x1, y1, z1, x2, y2, z2);
  x1 = x2 = xx2;
  g3->DrawLine3D(x1, y1, z1, x2, y2, z2);
  y1 = y2 = yy2;
  g3->DrawLine3D(x1, y1, z1, x2, y2, z2);
  x1 = x2 = xx1;  
  g3->DrawLine3D(x1, y1, z1, x2, y2, z2);
  x1 = x2 = xx1;   y1 = y2 = yy1;  z1 = z2 = zz1;
  for (k=0; k<NTicks; k++) {
    z1 = z2 = (zz1+(double)k*(zz2-zz1)/(NTicks-1.));
    y2 = yy1-0.015*(yy2-yy1);
    x2 = xx1-0.015*(xx2-xx1);
    g3->DrawLine3D(x1, y1, z1, x2, y2, z2);
    x2 = xx1-0.1*(xx2-xx1);  
    y2 = yy1-0.1*(yy2-yy1);  
    sprintf(strg,"%.2g", zl1+(double)k*(zl2-zl1)/(NTicks-1.));
    g3->DrawString3D(x2, y2, z2, strg);
    }
  x2 = xx1-0.075*(xx2-xx1);  y2 = yy1-0.075*(yy2-yy1);   z2 = zz2+0.05*(zz2-zz1); 
  g3->DrawString3D(x2, y2, z2, "Z");

}

/* --Methode-- */
int PIDrawer3D::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 = PIDrawer::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 == "rescale") || (opts == "autoscale") ) aScxy = aScz = true;
    else if ( (opts == "norescale") || (opts == "noautoscale") ) aScxy = aScz = false;
    else if ( (opts == "rescalexy") || (opts == "autoscalexy") )  aScxy = true;
    else if ( (opts == "norescalexy") || (opts == "noautoscalexy") )  aScxy = false;
    else if ( (opts == "rescalez") || (opts == "autoscalez") )  aScz = true;
    else if ( (opts == "norescalez") || (opts == "noautoscalez") )  aScz = false; 
    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);  
}

/* --Methode-- */
int PIDrawer3D::OptionToString(vector<string> & opt) const
{
 PIDrawer::OptionToString(opt);
 if ( aScxy || aScz ) opt.push_back("autoscale");
 else if (!aScxy && !aScz ) opt.push_back("noautoscale");
 else {
   if(aScxy) opt.push_back("autoscalexy"); else opt.push_back("noautoscalexy");
   if(aScz) opt.push_back("autoscalez"); else opt.push_back("noautoscalez");
 }
 return 1;
}

/* --Methode-- */
void PIDrawer3D::GetOptionsHelpInfo(string& info)
{
info += " ---- PIDrawer3D options help info : \n" ;
info += "  X/Y,Z axis rescaling option (-> cubic 3D box)  \n";
info += "  rescale=autoscale/ norescale=noautoscale : X/Y and Z axis \n";
info += "  rescalexy=autoscalexy / norescalexy=noautoscalexy : X/Y  axis \n";
info += "  rescalexy=autoscalexy / norescalexy=noautoscalexy : Z axis \n";
info += " --- + Usual color/line/marker/... attribute decoding  \n";
// On recupere ensuite la chaine info de la classe de base
PIDrawer::GetOptionsHelpInfo(info);
return;
}

/*  --------------------------------------------------------------  */
/*  ------------------- Classe PIElDrawer3D ------------------------  */
/*  --------------------------------------------------------------  */

//++
// Class	PIElDrawer3D
// Lib		PI
// include	p3ddrw.h
//
//	Classe de traceur ("Drawer") capable de gérer et de 
//	tracer une liste d'éléments simples. Cette classe
//	est utilisé pour le tracé des axes et des éléments de
//	texte ds les "PIScDrawWdg", "PIDraw3DWdg" et "PIImage".
//--
//++
// Links	Parents
// PIDrawer3D
//--
//++
// Links	Voir aussi
// PIElDrawer
// PIElDrwMgr
//--

//++
// Titre	Constructeurs et méthodes
//--

//++
// PIElDrawer3D()
//	Constructeur
//--

/* --Methode-- */
PIElDrawer3D::PIElDrawer3D()
{
  ShowTitles();
}

/* --Methode-- */
PIElDrawer3D::~PIElDrawer3D()
{
}


//++
//  void  SetTitles(string tt, string tb)
//  void  SetTitles(string tt, string tb, PIGraphicAtt const& att)
//	Ajout de titres haut ("tt") et bas ("tb")
//--


/* --Methode-- */
int PIElDrawer3D::DecodeOptionString(vector<string> & opt, bool rmdecopt)
{
  int nopt0 = opt.size();
  if (nopt0 < 1)  return(0);  
  int ndec1 = PIDrawer3D::DecodeOptionString(opt, rmdecopt);
  if ((nopt0 - ndec1) < 1) return(ndec1);  // si tout a ete decode
  vector<string> udopt;
  unsigned int k = 0;
  int ndec = opt.size();
  for( k=0; k<opt.size(); k++ ) {
    string opts = opt[k];
    if (opts == "title" || opts == "tit") ShowTitles(true);
    else if (opts == "notitle" || opts == "notit") ShowTitles(false);
    else if (opts == "axesnone")   SetAxesFlags(kAxesNone);
    else {
      ndec--;
      if (rmdecopt)  udopt.push_back(opts);
    }
  }  

  if (rmdecopt)  opt = udopt;
  return(ndec+ndec1);
}


/* --Methode-- */
void  PIElDrawer3D::Draw(PIGraphicUC* g, double xmin, double ymin, double xmax, double ymax)
{
SelGraAtt(g);   // DrawAxes change certains attributs graphiques
DrawAxes(g);   // Trace des axes 
g->SelFont(PI_NormalSizeFont);
SelGraAtt(g);   // DrawAxes change certains attributs graphiques

// Trace des titres 
if ( showTitles && ((titleT.length() > 0) || (titleB.length() > 0)) ) {
  PIGraphicAtt grs = GetGraphicAtt();
  UpdateGraphicAtt(titleAtt);
  SelGraAtt(g); 

  aXdir = false;  // Vrai si Axe X de Droite vers Gauche 
  aYdir = false;  // Vrai si Axe Y de Haut vers Bas
  double fx, fy, fh;
//  fh = g->GetFontHeightUC(gas, gds);
  fh = 0.075*(YMax()-YMin());
  if ( titleT.length() > 0) {
      if (g->isAxeYDirUpDown()) fy = g->DeltaUCY(YMin(), -fh);
      else fy = g->DeltaUCY(YMax(), fh);
      g->DrawString((XMax()+XMin())*0.5, fy, titleT.c_str(), PI_HorizontalCenter | PI_VerticalBottom);
//      printf(" PIElDrawer3D::Draw()/DBG (%g , %g) %s  - %g \n", fx, fy, titleT.c_str(), fh);
    }
  if ( titleB.length() > 0) {
      fx = g->CalcStringWidth(titleB.c_str());
      if(aXdir) fx = g->DeltaUCX(XMax(), -(XMax()-XMin()-fx)/2.);
      else fx = g->DeltaUCX(XMin(), (XMax()-XMin()-fx)/2.);
      if (g->isAxeYDirUpDown()) fy =  g->DeltaUCY(YMax(), fh*1.5);
      else fy = g->DeltaUCY(YMin(), -fh*1.5);
      g->DrawString((XMax()+XMin())*0.5, fy, titleB.c_str(), PI_HorizontalCenter | PI_VerticalBottom);
//      printf(" PIElDrawer3D::Draw()/DBG (%g , %g) %s  - %g \n", fx, fy, titleT.c_str(), fh);
    }
  SetGraphicAtt(grs);
  }

 SelGraAtt(g);
// Trace des elements
 ElDrwMgr().DrawElements(g, xmin, ymin, xmax, ymax);

}

