// Module PI : Peida Interactive     PIAxes
// Methodes de trace des axes 
//                    R. Ansari - 2002
// LAL (Orsay) / IN2P3-CNRS  DAPNIA/SPP (Saclay) / CEA

#include "sopnamsp.h"
#include "machdefs.h"
#include <stdio.h>
#include <iostream>
#include <strings.h>
#include <math.h>
#include "piaxes.h"

//++
// Class	PIAxes
// Lib		PI
// include	piaxes.h
//
//	Classe gestionnaire de tracé d'axes. A utiliser dans
//	un objet PIDrawer.
//--
//++
// Links	Voir
// PIDrawer
//--

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


inline void dble_SWAP(double& a,double& b) {double tmp=a; a=b; b=tmp;}

//++
// PIAxes()
//	Constructeur.
//--

/* --Methode-- */
PIAxes::PIAxes()
{
  ConfigureTicks();
  ConfigureAutoFontSize();
  ConfigureAxesCenter();
  setupDone = false;
}

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

//++
// void  DrawXYAxes(PIGraphicUC* g, PIGraphicAtt& gratt, \
//                  unsigned int flags=kAxesDflt, bool afsz=true)
//	Trace les axes en utilisant les limites de l'objet PIGraphicUC. 
//	"flags" spécifie les attributs d'axes. Constantes prédéfinies: 
//|	kCentAxes : Axes passant par le centre
//|     kBoxAxes  : Axes entourant le tracé
//|	kIntTicks, kExtTicks
//|	kMajTicks, kMinTicks
//|	kLabelsH, kLabelsV, kLabels
//|	kAxesDflt = kStdAxes =
//|	      kBoxAxes | kTicks | | kIntTicks | kLabels
//|	kGridOn, kAxesNone
//	Si "afsz == true", la taille de fonte est choisie automatiquement.
//
// void DrawXYAxes(PIGraphicUC* g, PIGraphicAtt& gratt, \
//        double xmin, double xmax, double ymin, double ymax, \
//        unsigned int flags=kAxesDflt, bool afsz=true)
//	Tracé d'axes avec spécification des limites d'axes.
//
// void DrawXCaption(PIGraphicUC* g, string const& xLabel, PIGraphicAtt const& att)
//	Tracé du label d'axe X.
// void DrawYCaption(PIGraphicUC* g, string const& YLabel, PIGraphicAtt const& att)
//	Tracé du label d'axe Y.
//--

/* --Methode-- */
void PIAxes::DrawXYAxes(PIGraphicUC* g, PIGraphicAtt& gratt, 
			unsigned int flags, bool afsz)
{
  PIGrCoord xmin, xmax, ymin, ymax;
  g->GetGrSpace(xmin, xmax, ymin, ymax);
  DrawXYAxes(g, gratt, (double)xmin, (double)xmax, 
	     (double)ymin, (double)ymax, flags, afsz);
}

/* --Methode-- */
void PIAxes::DrawXYAxes(PIGraphicUC* g, PIGraphicAtt& gratt, 
			double xmin, double xmax, double ymin, double ymax,
			unsigned int flags, bool afsz)

{
  if ((flags&kTicks)&&!(flags&kIntTicks)&&!(flags&kExtTicks))
    flags |= kIntTicks;  // Ticks internes par defaut
  if ((flags&kTicks)&&!(flags&kMajTicks)&&!(flags&kMinTicks))
    flags |= kMajTicks;  // Ticks majeures par defaut
  
  Setup(g, xmin, xmax, ymin, ymax);
  //  g->NoClip();

  if (gratt.GetLineAtt() == PI_NotDefLineAtt)  g->SelLine(PI_ThinLine);
  else g->SelLine(gratt.GetLineAtt());
  if (gratt.GetColor() == PI_NotDefColor) g->SelForeground(PI_Black);
  else g->SelForeground(gratt.GetColor());

  if(afsz) { 
    double fsz = (yMax-yMin)*fontSz;
    g->SelFontSz(fsz);
  }

  if (flags & kCentAxes) {

    // Les axes
    double x0axes = (xMin+xMax)/2.,  y0axes = (yMin+yMax)/2.;
    if(axesCenter) {x0axes = axesCentX0; y0axes = axesCentY0;}
    if(x0axes<xMin || x0axes>xMax) x0axes = (xMin+xMax)/2.;
    if(y0axes<yMin || y0axes>yMax) y0axes = (yMin+yMax)/2.;
    
    g->DrawLine(xMin, y0axes, xMax, y0axes);
    g->DrawLine(x0axes, yMin, x0axes, yMax);
    //    PIArrowMarker amk = g->GetArrowMarker();
    //    if (amk == PI_NotDefArrowMarker) 
    PIArrowMarker amk =  amk = PI_FTriangleArrowMarker;
    g->SelArrowMarkerSz(xMajTickLen*2., amk);
    //    g->SelArrowMarker(5, g->GetArrowMarker());
    double xm2 = g->DeltaUCX(xMax, yMajTickLen*2.);
    g->DrawArrowMarker(xMax, y0axes, xm2, y0axes, true);
    double ym2 = g->DeltaUCY(yMax, xMajTickLen*2.);
    g->DrawArrowMarker(x0axes, yMax, x0axes, ym2, true);
    // La grille en pointilles

    if (flags & kGridOn) DrawGrid(g);
  
    // Les ticks majeurs
  
    if ((flags & kTicks) && (flags & kMajTicks)) {
        DrawHTicks(g, y0axes, xMajTickLen, xMajTickLen, xMajTicks);
        DrawVTicks(g, x0axes, yMajTickLen, yMajTickLen, yMajTicks);
    }
    
    // Les ticks mineurs
  
    if ((flags & kTicks) && (flags & kMinTicks)) {
        DrawHTicks(g, y0axes, xMinTickLen, xMinTickLen, xMinTicks);
        DrawVTicks(g, x0axes, yMinTickLen, yMinTickLen, yMinTicks);
    }
    
    // Les labels
    
    if (flags & kLabelsH) {
      double declab = 2*xMajTickLen;
      if (!aYdir)
        DrawHLabels(g, y0axes-declab, xMajTicks, PI_VerticalTop);
      else 
        DrawHLabels(g, y0axes+declab, xMajTicks, PI_VerticalTop);
    }
    if (flags & kLabelsV) {
      double declab = 2.*yMajTickLen;
      if (!aXdir)
        DrawVLabels(g, x0axes-declab, yMajTicks, PI_HorizontalRight);
      else 
        DrawVLabels(g, x0axes+declab, yMajTicks, PI_HorizontalRight);
    }
    
  }
  
  if (flags & kBoxAxes) {
  
    // La boite
    
    g->DrawLine(xMin, yMin, xMax, yMin);
    g->DrawLine(xMax, yMin, xMax, yMax);
    g->DrawLine(xMax, yMax, xMin, yMax);
    g->DrawLine(xMin, yMax, xMin, yMin);

    // Longueur des ticks
    double extXMajTickLen = flags&kExtTicks ? xMajTickLen : 0;
    double intXMajTickLen = flags&kIntTicks ? xMajTickLen : 0;
    double extXMinTickLen = flags&kExtTicks ? xMinTickLen : 0;
    double intXMinTickLen = flags&kIntTicks ? xMinTickLen : 0;
    double extYMajTickLen = flags&kExtTicks ? yMajTickLen : 0;
    double intYMajTickLen = flags&kIntTicks ? yMajTickLen : 0;
    double extYMinTickLen = flags&kExtTicks ? yMinTickLen : 0;
    double intYMinTickLen = flags&kIntTicks ? yMinTickLen : 0;

    // La grille en pointilles

    if (flags & kGridOn) DrawGrid(g);

    // Les ticks majeurs
  
    if ((flags & kTicks) && (flags & kMajTicks)) {
        DrawHTicks(g, yMin, intXMajTickLen, extXMajTickLen, xMajTicks);
        DrawHTicks(g, yMax, extXMajTickLen, intXMajTickLen, xMajTicks);
        DrawVTicks(g, xMin, extYMajTickLen, intYMajTickLen, yMajTicks);
        DrawVTicks(g, xMax, intYMajTickLen, extYMajTickLen, yMajTicks);
    }
    
    // Les ticks mineurs
  
    if ((flags & kTicks) && (flags & kMinTicks)) {
        DrawHTicks(g, yMin, intXMinTickLen, extXMinTickLen, xMinTicks);
        DrawHTicks(g, yMax, extXMinTickLen, intXMinTickLen, xMinTicks);
        DrawVTicks(g, xMin, extYMinTickLen, intYMinTickLen, yMinTicks);
        DrawVTicks(g, xMax, intYMinTickLen, extYMinTickLen, yMinTicks);
    }


    // Les labels
    
    if (flags & kLabelsH) {
      double declab = xMajTickLen;
      if (flags & kExtTicks) declab *= 2.;
      if (!aYdir) {
        DrawHLabels(g, g->DeltaUCY(yMin, -declab), xMajTicks, PI_VerticalTop);
      }
      else {
        DrawHLabels(g, g->DeltaUCY(yMax,  declab), xMajTicks, PI_VerticalTop);
      }
    }
    if (flags & kLabelsV) {
      double declab = yMajTickLen;
      if (flags & kExtTicks) declab *= 2.;
      if (!aXdir) {
        DrawVLabels(g, g->DeltaUCX(xMin, -declab), yMajTicks, PI_HorizontalRight);
      }
      else {
        DrawVLabels(g, g->DeltaUCX(xMax,  declab), yMajTicks, PI_HorizontalRight);
      }
    }
  }
  g->Clip();
  
}

/* --Methode-- */
void PIAxes::DrawXCaption(PIGraphicUC* g, PIGraphicAtt& att, unsigned int flags, string const& xLabel)
{
  if (xLabel.length() < 1) return;
  if (!setupDone) {
    PIGrCoord xmin, xmax, ymin, ymax;
    g->GetGrSpace(xmin, xmax, ymin, ymax);
    Setup(g, xmin, xmax, ymin, ymax);
  }
  if ( (att.GetFontName() != PI_DefaultFont) ||
       (att.GetFontAtt() != PI_NotDefFontAtt) )
    g->SelFont(att.GetFont());
  PIColors fcol = att.GetFgColor(); 
  if (fcol != PI_NotDefColor)    g->SelForeground(fcol);

  double fx,fy,fh;
  if (flags&kBoxAxes) { 
    fh = -0.090*(yMax-yMin);  
    if (g->isAxeYDirUpDown()) fy = yMax;
    else fy = yMin;
  }
  else {
    fh = 0.04*(yMax-yMin);  
    if(axesCenter) fy = axesCentY0;
    else fy = 0.5*(yMin+yMax);
    if(fy<yMin || fy>yMax) fy = (yMin+yMax)/2.;
  }
  if (g->isAxeYDirUpDown()) fh = -fh;
  fy = g->DeltaUCY(fy, fh);
  fx = g->DeltaUCX(xMax, -(xMax-xMin)*0.25);
  g->DrawString(fx, fy, xLabel.c_str(), 
		PI_HorizontalCenter | PI_VerticalCenter);  
}

/* --Methode-- */
void PIAxes::DrawYCaption(PIGraphicUC* g, PIGraphicAtt& att, unsigned int flags, string const& yLabel)
{
  if (yLabel.length() < 1) return;
  if (!setupDone) {
    PIGrCoord xmin, xmax, ymin, ymax;
    g->GetGrSpace(xmin, xmax, ymin, ymax);
    Setup(g, xmin, xmax, ymin, ymax);
  }
  if ( (att.GetFontName() != PI_DefaultFont) ||
       (att.GetFontAtt() != PI_NotDefFontAtt) )
    g->SelFont(att.GetFont());
  PIColors fcol = att.GetFgColor(); 
  if (fcol != PI_NotDefColor)    g->SelForeground(fcol);

  double fx,fy,fh;
  if (flags&kBoxAxes) { 
    fh = -0.125*(xMax-xMin);
    if (g->isAxeXDirRtoL()) fx = xMax;
    else fx = xMin;
  }
  else {
    fh = 0.04*(xMax-xMin);
    if(axesCenter) fx = axesCentX0;
    else fx = 0.5*(xMin+xMax);
    if(fx<xMin || fx>xMax) fx = (xMin+xMax)/2.;
  }
  if (g->isAxeXDirRtoL()) fh = -fh;
  fx = g->DeltaUCX(fx, fh);
  fy = g->DeltaUCY(yMax,-(yMax-yMin)*0.25);
  unsigned long txtflg = PI_HorizontalCenter | PI_VerticalCenter | PI_TextDirectionVerticalUp;
  if (g->isAxeYDirUpDown()) 
    txtflg = PI_HorizontalCenter | PI_VerticalCenter | PI_TextDirectionVerticalDown;
  g->DrawString(fx, fy, yLabel.c_str(), txtflg);
  
}

//++
// void ConfigureTicks(int ntickx=10, int nticky=12, \
//		       double majtl=0.0125, double mintl=0.07)
//	Configuration du nombre de ticks et taille des ticks.
//	La taille des ticks est exprimée en fraction de la taille totale
//	(X/Y max-min). Les paramètres ne sont pas changés si des valeurs
//	négatives ou nulles sont spécifiées.
// void ConfigureAutoFontSize(double fszf=0.0125)
//	Configuration de la taille de fonte, en mode AutoFontSize.
//	La taille est exprimée en fraction de la taille totale YMax-Min
// void  ConfigureAxesCenter(bool fset,double x0,double y0)
//	Force axe center to be at (x0,y0) or let it be in the middle
//--

/* --Methode-- */
void PIAxes::ConfigureTicks(int ntickx, int nticky, 
			    double majtl, double mintl)
{
  if (ntickx > 0) xNTicks = ntickx;
  if (nticky > 0) yNTicks = nticky;
  if (majtl > 1.e-19) fMajTickLen = majtl;
  if (mintl > 1.e-19) fMinTickLen = mintl;
}

/* --Methode-- */
void PIAxes::ConfigureAutoFontSize(double fszf)
{
  if (fszf > 1.e-19) fontSz = fszf;
}

/* --Methode-- */
void PIAxes::ConfigureAxesCenter(bool fset,double x0,double y0)
{
 axesCenter = fset;
 if(!axesCenter) return;
 axesCentX0 = x0;
 axesCentY0 = y0;
}

/* --Methode-- */
void PIAxes::Setup(PIGraphicUC* g, double xmin, double xmax, 
		       double ymin, double ymax)
{
  
  xMin = xmin;  xMax = xmax;
  yMin = ymin;  yMax = ymax;

  aXdir = g->isAxeXDirRtoL();
  aYdir = g->isAxeYDirUpDown();
  aXlog = g->isLogScaleX();
  aYlog = g->isLogScaleY();

  //   Reza - Mars 03 - Nb de ticks en parametres
  int ntick_x = (aXlog) ? xNTicks : xNTicks;
  if(aXlog) BestTicksLog(xMin,xMax,ntick_x,xMajTicks,xMinTicks);
  else BestTicks(xMin,xMax,ntick_x,xMajTicks,xMinTicks);

  int ntick_y = (aYlog) ? yNTicks : yNTicks;
  if(aYlog) BestTicksLog(yMin,yMax,ntick_y,yMajTicks,yMinTicks);
  else BestTicks(yMin,yMax,ntick_y,yMajTicks,yMinTicks);

  //   Reza - Mars 03 - Taille des ticks en parametres
  yMajTickLen = (xMax-xMin)*fMajTickLen;  // Au lieu de / 100  
  yMinTickLen = (xMax-xMin)*fMinTickLen;  // Au lieu de / 200 ou 250
  xMajTickLen = (yMax-yMin)*fMajTickLen;  // Au lieu de / 100
  xMinTickLen = (yMax-yMin)*fMinTickLen;  // Au lieu de / 200 ou 250

  setupDone = true;
}


/* --Methode-- */
void PIAxes::DrawHTicks(PIGraphicUC* g, double y, double tickUp, 
			double tickDown, vector<double>& xticks)
{
  if(xticks.size()==0) return;
  for(unsigned int i=0;i<xticks.size();i++) {
    if(xticks[i]<xMin) continue;
    if(xticks[i]>xMax) break;
    g->DrawLine(xticks[i],g->DeltaUCY(y,-tickDown),xticks[i],g->DeltaUCY(y,tickUp));
  }
}

/* --Methode-- */
void PIAxes::DrawVTicks(PIGraphicUC* g, double x, double tickLeft, 
			double tickRight, vector<double>& yticks)
{
  if(yticks.size()==0) return;
  for(unsigned int i=0;i<yticks.size();i++) {
    if(yticks[i]<yMin) continue;
    if(yticks[i]>yMax) break;
    g->DrawLine(g->DeltaUCX(x,-tickLeft),yticks[i],g->DeltaUCX(x,tickRight),yticks[i]);
  }
}

/* --Methode-- */
void PIAxes::DrawHLabels(PIGraphicUC* g, double y, vector<double>& xticks, unsigned long just)
{
  if(xticks.size()==0) return;

  // Choix du bon format pour les labels des axes
  string format; double xstep;
  int npuiss = Le_Bon_Format(xticks,format,xstep);
  double fac=(npuiss!=0)? fac=pow(10.,(double)npuiss): 1.;

  char label[64];
  double dum,xpixdeb,xpixfin,largpix;
  g->UC2GrC(xMin-2.*(xMax-xMin),y,xpixfin,dum);
  for(unsigned int i=0;i<xticks.size();i++) {
    if(xticks[i]<xMin) continue;
    if(xticks[i]>xMax) break;
    //Attention erreur d'arrondi x->0 (on code 5.1698e-26 au lieu de 0)
    double xx = (fabs(xticks[i]/xstep)<1.e-5) ? 0.: xticks[i];
    sprintf(label,format.c_str(),xx/fac); Arrange_Label(label);
    double largeur = g->CalcStringWidth(label);
    g->DUC2GrC(largeur,0.,largpix,dum);
    g->UC2GrC(xticks[i],y,xpixdeb,dum);   xpixdeb -= largpix/2.;
    //cout<<"xticks="<<xticks[i]<<" largpix="<<largpix
    //    <<" xpixdeb="<<xpixdeb<<" xpixfin="<<xpixfin<<endl;
    if((aXdir && xpixdeb<xpixfin) || (!aXdir && xpixdeb>xpixfin)) {
      g->DrawString(xticks[i],y,label,PI_HorizontalCenter|just);
      xpixfin = xpixdeb + 1.1*largpix;
    }
  }

  if(npuiss!=0) {
    PIGrCoord asc,desc;
    double h = g->GetFontHeight(asc,desc);
    if((aYdir && (just&PI_VerticalBottom)) || (!aYdir && (just&PI_VerticalTop))) h=-h; 
    double xm = (aXdir)? xMin: xMax;
    double ym = g->DeltaUCY(y,h);
    sprintf(label,"%d",npuiss);
    g->DrawCompString(xm,ym,"x 10",label,NULL,PI_HorizontalCenter|just);
  }

}

/* --Methode-- */
void PIAxes::DrawVLabels(PIGraphicUC* g, double x, vector<double>& yticks, unsigned long just)
{
  if(yticks.size()==0) return;

  // Choix du bon format pour les labels des axes;
  string format; double ystep;
  int npuiss = Le_Bon_Format(yticks,format,ystep);
  double fac=(npuiss!=0)? fac=pow(10.,(double)npuiss): 1.;

  char label[64];
  PIGrCoord asc,desc;
  double dum,ypixdeb,ypixfin,hautpix,hauteur=g->GetFontHeight(asc,desc);
  g->DUC2GrC(0.,hauteur,dum,hautpix);
  g->UC2GrC(x,yMin-2.*(yMax-yMin),dum,ypixfin);
  for(unsigned int i=0;i<yticks.size();i++) {
    if(yticks[i]<yMin) continue;
    if(yticks[i]>yMax) break;
    double yy = (fabs(yticks[i]/ystep)<1.e-5) ? 0.: yticks[i];
    sprintf(label,format.c_str(),yy/fac); Arrange_Label(label);
    g->UC2GrC(x,yticks[i],dum,ypixdeb);   ypixdeb -= hautpix/2.;
    // -- Attention: ypix=0 est en haut de l'ecran
    //               (ypix croissants vers le bas de l'ecran)
    //    donc bien que yMin<yMax on a yMinPix>yMaxPix
    //cout<<"yticks="<<yticks[i]<<" hautpix="<<hautpix
    //    <<" ypixdeb="<<ypixdeb<<" ypixfin="<<ypixfin<<endl;
    if((aYdir && ypixdeb>ypixfin) || (!aYdir && ypixdeb<ypixfin)) {
      g->DrawString(x,yticks[i],label,PI_VerticalCenter|just);
      ypixfin = ypixdeb + 1.1*hautpix;
    }
  }

  if(npuiss!=0) {
    if(aYdir) hauteur = -hauteur; 
    double ym = (aYdir)? yMin: yMax; ym = g->DeltaUCY(ym,0.5*hauteur);
    sprintf(label,"%d",npuiss);
    g->DrawCompString(x,ym,"x 10",label,NULL,PI_VerticalBottom|just);
  }

}

/* --Methode-- */
void PIAxes::DrawGrid(PIGraphicUC* g)
{
  PILineAtt savlineatt = g->GetLineAtt();
  g->SelLine(PI_ThinDottedLine);

  if(xMajTicks.size()>0)
    for(unsigned int i=0;i<xMajTicks.size();i++) {
      if(xMajTicks[i]>xMax) break;
      g->DrawLine(xMajTicks[i], yMin, xMajTicks[i], yMax);
    }

  if(yMajTicks.size()>0)
    for(unsigned int i=0;i<yMajTicks.size();i++) {
      if(yMajTicks[i]>yMax) break;
      g->DrawLine(xMin, yMajTicks[i], xMax, yMajTicks[i]);
    }

  g->SelLine(savlineatt);
}

////////////////////////////////////////////////////////////////////////
//////////////////// METHODES STATIQUES ////////////////////////////////
////////////////////////////////////////////////////////////////////////
/* --Methode-Static-- */
void PIAxes::ReSizeMinMax(bool axelog,double& vmin,double& vmax,double garde)
//  Calcul du min et du max du display a partir des valeurs min et max a plotter
{
 if(garde<0. || garde>=1.) garde = 0.025;
 // cout<<"ReSizeMinMax[log="<<axelog<<",garde="<<garde<<"] vmin="<<vmin<<" vmax="<<vmax<<endl;
 // Cas d'une echelle lineaire
 if(!axelog || vmax<=0.) {
   double dv = garde*(vmax-vmin);
   vmin -= dv;
   vmax += dv;
 }

 // Cas d'une echelle log avec un range raisonnable
 else if(vmin>0.) {
   double dv = pow(vmax/vmin,garde);
   vmin /= dv;
   vmax *= dv;
 }

 // Cas d'une echelle log avec un range de-raisonnable
 else if(vmin<=0.) {
   if(vmin<0.)  vmin += garde*vmin;
   if(vmax==1.) vmax  = 1.+garde;
   if(vmax>1.)  vmax  = pow(vmax,1.+garde);
     else       vmax  = pow(vmax,1.-garde);
 }

 // cout<<"           vmin="<<vmin<<" vmax="<<vmax<<endl;
}

/* --Methode-Static-- */
void PIAxes::BestTicks(double xmin,double xmax,int nticks
                      ,vector<double>& majticks,vector<double>& minticks)
// *** Calcul de l'intervalle entre les ticks et de la valeur du premier tick
//     pour un axe lineaire
{
 if(nticks<=0) nticks = 1;

 double d=xmax-xmin; if(d<1.e-100) d=1.e-100;
 double ld  = log10(d);
 double fld = floor( ((ld<0.)? -ld: ld) );
 double del,del0;
 fld = (ld>=0.)? fld-2.: -(fld+2.);
 del0 = del = pow(10.,fld);
 // *** Intervalle entre les ticks
 // xmin  xmax    d       ld       fld -->fld  del0
 // 1     1500    1499    3.17     3      1    10^1
 // 1     9500    9499    3.98     3      1    10^1
 // 1     1.005   0.005  -2.3      3     -5    10^-5
 // 1     1.995   0.995  -0.0022   1     -3    10^-3
 // - Et recherche de la valeur del={del0,2*del0,...,20*del0,...}
 // telle que "nticks*del" soit le plus petit nombre ">=xmax-xmin"
 double fac[9] = {2.,5.,10.,20.,50.,100.,200.,500.,1000.};
 for(int k=0;k<9;k++) {
   //cout<<"BestTicks: "<<k<<" del="<<del<<" d/del="<<d/del<<"<"<<nticks<<endl;
   if(d/del < (double)nticks) break;
   del=fac[k]*del0;
 }
 double steptick=del;
 //***  Valeur du premier tick
 majticks.resize(0);
 double xfirsttick = floor(fabs(xmin)/steptick)*steptick;
   if(xmin<0.) xfirsttick *= -1.;
 if(xfirsttick<xmin) xfirsttick += steptick;
 while(xfirsttick<=xmax+steptick/10.)
   {majticks.push_back(xfirsttick); xfirsttick+= steptick;}
 //***  Gestion des ticks mineurs
 minticks.resize(0);
 if(majticks.size()>1) {
   double steptickmin = steptick/5.;
   double xfirsttickmin = majticks[0];
   while(xfirsttickmin<=xmax+steptickmin/10.)
     {minticks.push_back(xfirsttickmin); xfirsttickmin+= steptickmin;}
 }
}
/* --Methode-Static-- */
void PIAxes::BestTicksLog(double xmin,double xmax,int nticks
                      ,vector<double>& majticks,vector<double>& minticks)
// *** Calcul des ticks pour un axe logarithmique
{
 if(nticks<=0) nticks = 1;
 //cout<<"BestTicksLog: xmin="<<xmin<<" xmax="<<xmax<<" nticks="<<nticks<<endl;

 // Si xmax<=0, on garde BestTicks
 if(xmax<=0. ) {
   //cout<<"Choix de BestTicks car xmax="<<xmax<<endl;
   BestTicks(xmin,xmax,nticks,majticks,minticks);
   return;
 }

 int dmin, dmax=int(floor(log10(xmax)));
 if(xmin>0.) {
   // Dynamique trop faible, on garde BestTicks
   if(xmax/xmin<5.) {
     //cout<<"Choix de BestTicks car xmax/xmin="<<xmax/xmin<<" <5"<<endl;
     BestTicks(xmin,xmax,nticks,majticks,minticks);
     return;
   }
   dmin=int(floor(log10(xmin)));
 } else {
   if(dmax>3) dmin = dmax/3;
   else dmin = dmax-1;
 }
 if(dmax==dmin) dmax++; else if(dmax<dmin) dmax=dmin+1;
 int inc = (dmax-dmin+1)/nticks; if(inc<1) inc=1;
 //cout<<" dmin="<<dmin<<" dmax="<<dmax<<" inc="<<inc<<endl;

 majticks.resize(0);
 {for(int i=dmin;i<=dmax;i+=inc) {
   double x = pow(10.,(double)i);
   if(x<xmin || x>xmax) continue;
   majticks.push_back(x);
 }}
 //cout<<"majticks.size()="<<majticks.size()<<endl;

 // Pas de puissance de 10 dans l'intervalle on garde BestTicks
 if(majticks.size()==0) {
   BestTicks(xmin,xmax,nticks,majticks,minticks);
   return;
 }

 // Pas suffisamment de ticks majeurs
 if((int)majticks.size()<=nticks/2) {
   int nins = nticks/(majticks.size()+1);
   if(nins<=0) nins=1;
   //cout<<"nins="<<nins<<endl;
   // Sequence judicieuse pour remplir les ticks manquants
   // nins = 1 on insere 3
   //        2           2 5
   //        3           2 4 6
   //        4           1.5 2 4 6
   //      >=5 on reste au cas precedent  
   double seqmaj[4][4] = {{3.,0,0,0},{2.,5.,0,0},{2.,4.,6.,0},{1.5,2.,4.,6.}};
   if(nins>4) nins=4;
   vector<double> tmp;
   {for(unsigned int i=0;i<=majticks.size();i++) {
     double xt;
     if(i<majticks.size()) xt = majticks[i]/10.;
       else                xt = majticks[i-1];
     for(int n=0;n<nins;n++) {
       double xins = seqmaj[nins-1][n]*xt;
       if(xins<xmin || xins>xmax) continue;
       tmp.push_back(xins);
     }
     if(i<majticks.size()) tmp.push_back(majticks[i]);
   }}
   majticks = tmp;
 }
 //cout<<"...majticks.size()="<<majticks.size()<<endl;

 // Les ticks mineurs
 minticks.resize(0);
 {for(unsigned int i=0;i<majticks.size()-1;i++) {
   double dx = (majticks[i+1]-majticks[i])/10.;
   minticks.push_back(majticks[i]);
   for(int j=2;j<=8;j+=2)  {
     double x = majticks[i] + j*dx;
     if(x<xmin || x>xmax) continue;
     minticks.push_back(x);
   }
 }}
 minticks.push_back(majticks[majticks.size()-1]);
 //cout<<"...minticks.size()="<<minticks.size()<<endl;

 // Si on a xmin<=0., on insere zero dans les ticks majeurs
 if(xmin<=0.) {
   vector<double> tmp = majticks;
   majticks.resize(0); majticks.push_back(0.);
   for(unsigned int i=0;i<tmp.size();i++) majticks.push_back(tmp[i]);
   //cout<<"...xmin="<<xmin<<"<=0. add majticks[0]="<<majticks[0]<<endl;
 }

}

/* --Methode-Static-- */
int PIAxes::BonFormatAxes(double xmin,double xmax,double xstep
                         ,string& format,int typf,int add_digit)
// *** Calcul format optimal pour ecrire les labels numeriques des axes:
// ---- Input
// . xmin,xmax : limites du plot sur l'axe considere.
// . xstep : distance entre les ticks.
// . add_digit : nombre de digits a ajouter au nombre de digits minimum.
// . typf : type de format en sortie
//        0 : format optimum %-nn.mme ou %-nn.mmf selon valeurs
//        1 : format %-nn.mme
//        2 : format %-nn.mmf pour imprimer x/10^npuiss
//            tel que x/10^npuiss soit entre [0,10]
//        3 : format %-nn.mmf
// ---- Output
// . format : le format d'impression
// ---- Return:
// Si typ=0 ou 1 ou 3
//   "ndig" : nombre de digits necessaires pour distinguer
//            les valeurs xmin+k*dx (<=xmax)
// Si typ=2
//   "npuiss" : tel que x/10^npuiss soit entre 0 et 10
//              Dans ce cas le format est celui qui imprime x/10^npuiss
{
 format = "%-5g";  // format par default
 if(xmin>=xmax) {if(typf==2) return 0; else return -1;}

 if(xstep<=0. || xstep>xmax-xmin) xstep = xmax-xmin;

 double axmin=fabs(xmin), axmax=fabs(xmax);
 if(axmin>axmax) dble_SWAP(axmin,axmax);

 double l10amax = log10(axmax), l10xstep = log10(xstep);
 int il10amax = int(floor(l10amax));

 // choix du type de format
 char ftype = 'e';
 int npuiss = 0;
 if(typf==2) {
   npuiss = il10amax-1;  // nombre entre [0,10]
   //npuiss = il10amax;  // nombre entre [0,1]
   if(npuiss<-300 || npuiss>300) {
     ftype='e'; npuiss=0;
   } else {
     // On recalcule les valeurs de decision pour  axmax/10^npuiss, xstep/10^npuiss
     l10amax -= (double)npuiss; l10xstep -=  (double)npuiss;
     il10amax = int(floor(l10amax));
     ftype = 'f';
   }
 } else if(typf==1) {
   ftype='e';
 } else if(typf==3) {
   ftype='f';
 } else {
   ftype='e';
   // On evite d'ecrire +a.bbbe+ccc -> format %f
   // Ex: 1.2345e+2 -> 123.45   /  -1.2345e+2 -> -123.45
   //     1.2345e-1 -> 0.12345  /  -1.2345e-1 -> -0.12345
   if((axmin>=1e-4 || axmin==0.) && axmax<1e4) ftype='f';
 }

 //printf("BonFormatAxes[npuiss=%d]: xmin=%-21.14e xmax=%-21.14e\n",npuiss,xmin,xmax);
 //printf("        xstep=%-21.14e log10(xstep/10^%d)=%g\n",xstep,npuiss,l10xstep);
 //printf("        axmax=%-21.14e log10(axmax/10^%d)=%g diff=%g\n"
 //      ,axmax,npuiss,l10amax,l10amax-l10xstep);

 // Nombre de digits necessaires pour ecrire axmax et xstep
 int ndig = il10amax  -  int(floor(l10xstep));
 if(ndig<0) ndig *= -1;  ndig += 1;
 //printf("ndig=%d",ndig);
 
 // Add more digits (or suppress digits)
 ndig += add_digit; if(ndig<0) ndig=0;
 //printf(" + %d  ==> ndig=%d\n",add_digit,ndig);

 // Calcul du bon format
 char str[16];
 if(ftype=='f') { // Calcul du format %-nn.mmf
   int mm=-1, nn;
   if(il10amax<0) { // +0.12345 +0.0012345 +0.0012345 ...
     mm = ndig - il10amax - 1; nn = mm+3;
   } else {         // +1.2345 +12.345 +123.45 ...
     mm = ndig - il10amax - 1; nn = ndig+2;
   }
   //printf("format %%f : mm=%d  nn=%d\n",mm,nn);
   if(mm<0.) mm=0; if(nn<mm+3) nn=mm+3;
   sprintf(str,"%%-%d.%df",nn,mm);
 } else if(ftype=='e') { // Calcul du format %-nn.mme
   //   +d.<--ddd-->e+123
   //   1 2         34567  ==> nn=ndig+7   mm=ndig-1
   sprintf(str,"%%-%d.%de",ndig+7,ndig-1);
 }

 format = str;
 //printf("format=[%s]\n",format.c_str());

 if(typf==2) return npuiss;
 return ndig;
}

/* --Methode-- */
int PIAxes::Le_Bon_Format(vector<double>& xticks,string& format,double& xstep)
// Methode static de decision du bon format
// Decide quel format est le mieux adapte pour ecrire les labels des axes
// Decide si une puissance de 10 doit etre deportee en bout d'axe
// - Input:
//   xticks : vecteur des ticks a ecrire (calcule par BestTicks)
// - Output:
//   format : format a utiliser
//   xstep  : step entre 2 graduations
// - Return:
//   npuiss : si format choisit avec ecriture
//            avec label des puissances de 10 deporte
//   0      : sinon
{
 format="%g"; xstep=1.;
 if(xticks.size()<=1) return 0;

 // On decide du format
 xstep=xticks[1]-xticks[0];
 int npuiss = BonFormatAxes(xticks[0],xticks[xticks.size()-1],xstep,format,2,1);
 if(npuiss>=-2 && npuiss<=2) {
   npuiss = 0;
   BonFormatAxes(xticks[0],xticks[xticks.size()-1],xstep,format,3,1);
 }

 return npuiss;
}

void PIAxes::Arrange_Label(char *label)
// --- Mise en forme optimale du label numerique
// Enleve les blancs, les zeros, le point et les e00
//        inutiles a la fin d'un label
{
 size_t lenl=strlen(label);
 if(lenl==0) return;

 // --- On enleve les blancs et plus au debut du label
 if(label[0]==' ' || label[0]=='+') {
   char *str=new char[lenl+2];
   strcpy(str,label);
   unsigned i=0;
   for(i=0;i<lenl;i++) if(str[i]!=' ' && str[i]!='+') break;
   strcpy(label,&str[i]);
   delete [] str;
   lenl=strlen(label);
   if(lenl==0) return;
 }

 // --- On enleve les blancs a la fin du label
 if(label[lenl-1]==' ') {
   for(int i=lenl-1;i>=0;i--) {
     if(label[i]!=' ') break;
     label[i]='\0';
   }
   lenl=strlen(label);
 }

 // --- On enleve les e... E... non-significatifs
 // ex: a.be-zzz  a.be+zzz  a.bezzz avec zzz=0,00,000,...
 // Attention on n'enleve pas si: a.be+10
 // Attention on ne fait rien si: e+10
 char* e=index(label,'e');
 if(e==NULL) e=index(label,'E');
 if(e) {
   for(int i=lenl-1;i>=0;i--) {
     if(isdigit(label[i]) && label[i]!='0') break;
     if(label[i]=='e' || label[i]=='E')
       {label[i]='\0'; lenl=strlen(label); break;}
   }
 }

 // --- On enleve les zeros non-significatifs a la fin du label
 // On enleve des zeros ou le point si: ab. ab.czzz avec zzz=0,00,000,...
 // Attention on n'enleve pas de zeros si: abzzz
 // Attention a ne pas enlever des zeros si on a ab.ccce+a0
 // Attention on traite 0eaaa -> 0
 if(index(label,'.')==NULL) return;  // Recherche d'un point
 string stre;
 if(e) {if(e==label) return; stre=e; *e='\0'; lenl=strlen(label);}
 {for(int i=lenl-1;i>=0;i--) {
   if(label[i]=='0') label[i]='\0';
   else if(label[i]=='.') {
     if(i>0) label[i]='\0';
     else {
       // Attention: ".e+10" -> "1e+10" MAIS "." -> "0"
       if(e) strcpy(label,"1"); else strcpy(label,"0");
     }
     break;
   }
   else break;  // Ni un point ni un zero
 }}
 if(e) {
   if(strlen(label)==1) if(label[0]=='0') return;
   strcat(label,stre.c_str());
 }

}
