// Module PI : Peida Interactive     PIDrawer
// Classe de traceurs pouvat être attachés à une PIBaseWdg
//	      E. Aubourg , R.Ansari     96-98
// LAL (Orsay) / IN2P3-CNRS  DAPNIA/SPP (Saclay) / CEA

#include "machdefs.h"
#include <strings.h>
#include <iostream.h>
#include "pidrawer.h"

#include <math.h>
//  Pour la declaration de finite() isnan() ...
#if defined(SunOS)
#include <ieeefp.h>
#endif

//++
// Class	PIDrawer
// Lib		PI
// include	pidrawer.h
//
//	Classe pour la création d'objets, capables de se dessiner
//	dans une composante graphique ("PIBaseWdg").
//--
//++
// Links	Voir
// PIBaseWdg
//--

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

//++
// PIDawer()
//	Constructeur.
//--

PIDrawer::PIDrawer()
{
  mBWdg = NULL; 
  mGrUC = NULL;
  xW0 = yW0 = 0; xWd = yWd = 100; 
  aXdir = false;  // Vrai si Axe X de Droite vers Gauche 
  aYdir = false;  // Vrai si Axe Y de Haut vers Bas
  aXlog = aYlog = false;   // Vrai -> echelle axe logarithmique
  SetLimits(-1, 1, -1, 1, kAxeDirLtoR, kAxeDirDownUp);
  limitsFixed = 0;
  SetAxesFlags(kAxesNone);   // Pas de trace d'axes par defaut 
  mDndfg = false;  // Pour controle de l'appel de Detach() si delete

  mFgHighLight = false;  // Flag de control d'affichage en mode HighLight

  mFCol = mBCol = PI_NotDefColor;
  mLAtt = PI_NotDefLineAtt;
  mFSz = PI_NotDefFontSize;
  mFAtt = PI_NotDefFontAtt;
  mMSz = -1;
  mMrk = PI_NotDefMarker;
  mCmapid = CMAP_OTHER;
  mRevCmap = false;
  mFName = PI_DefaultFont;
}

PIDrawer::~PIDrawer()
{
  list<DrwBWId>::iterator it;
  mDndfg = true;
  for(it = mBWdgList.begin(); it != mBWdgList.end(); it++)
         (*it).wdg->RemoveDrawer((*it).id);
  if (mGrUC)  delete mGrUC;
}

//++
// Titre	Les axes
//--
//++
// void  SetLimits(double xmin, double xmax, double ymin, double ymax, -
//                 int axrl=kAxeDirSame, int ayud=kAxeDirSame)
//	Définit les limites du système de coordonnées.
//|	kAxeDirSame , kAxeDirAuto
//|	kAxeDirLtoR , kAxeDirRtoL  (Axe X)
//|	kAxeDirDownUp , kAxeDirUpDown (Axe Y)	
// void  SetLogScale(bool logx, bool logy) 
//	Définition d'échelle logarithmique pour les axes X,Y.
// void  GetAxesConfig(int& xa, int& ya) 
//	Renvoie la configuration des axes.
// bool  isLogScaleX()   isLogScaleY() 
//	Renvoie "true" si échelle logarithmique pour axe X , Y
// double XMin()  XMax()
//	Limites de l'axe X
// double YMin()  YMax()
//	Limites de l'axe Y
// void  SetAxesFlags(unsigned int flags=kAxesNone)
//	Attributs de tracé d'axes
//|	kStdAxes , kBoxAxes , kTicks , kIntTicks , kExtTicks 
//|	kMajTicks , kMinTicks , kLabels , kGridOn
//|	kAxesDflt , kAxesNone
// unsigned int GetAxesFlags()
//	Renvoie les attributs de tracé d'axes  
// void  DrawAxes(PIGraphicUC* g)
//	Méthode de tracé des axes
//--

void
PIDrawer::SetLimits(double xmin, double xmax, double ymin, double ymax,
                    int axrl, int ayud)
{
  if (axrl == kAxeDirAuto) {
    double ff;
    if (xmax < xmin)  { axrl = kAxeDirRtoL;   ff=xmin;  xmin=xmax;  xmax=ff; }
    else  axrl = kAxeDirLtoR;
  }
  if (ayud == kAxeDirAuto) {
    double ff;
    if (ymax < ymin)  { ayud = kAxeDirUpDown;   ff=ymin;  ymin=ymax;  ymax=ff; }
    else  ayud = kAxeDirDownUp;
  }
//  if (xmax <= xmin || ymax <= ymin) {  
//    cerr << "PIDrawer::SetLimits() Error - xmax <= xmin  || ymax <= ymin ! " << endl;
//    return; 
//    }
#if defined(OSF1) || defined(SunOS) || (defined(Linux) && !defined(__KCC__) )
  if (!finite(xmin) || !finite(xmax) || !finite(ymin) || !finite(ymax)) {
    cerr << "PIDrawer::SetLimits() Error - Non finite value for x/ymin-max ! " << endl;
    return; 
    }
#endif
  if (axrl == kAxeDirLtoR)  aXdir = false;
  else if (axrl == kAxeDirRtoL)  aXdir = true;
  if (ayud == kAxeDirDownUp)  aYdir = false;
  else if (ayud == kAxeDirUpDown)  aYdir = true;
  xMin = xmin;
  xMax = xmax;
  yMin = ymin;
  yMax = ymax;
  aXFlg = axrl;  aYFlg = ayud;
  limitsFixed = 1;
}


void
PIDrawer::GetAxesConfig(int& xa, int& ya)
{
xa = (aXdir) ? kAxeDirRtoL : kAxeDirLtoR;
ya = (aYdir) ? kAxeDirUpDown : kAxeDirDownUp;
}

//++
//  void  Draw(PIGraphicUC* g, double xmin, double ymin, double xmax, double ymax)
//	Méthode qui est appelé lorsquer l'objet PIDrawer doit rafraichir la zone
//	définie par "xmin-xmax" , "ymin-ymax" dans le système de coordonnées du
//	PIDrawer.
//  void  Refresh()
//	Rafraîchit le dessin du PIDrawer sur tous les PIBaseWdg auxquel il est attaché.
//  void  UpdateLimits()
//	Cette méthode doit calculer les limites (X/Y Min-Max) préferées par l'objet
//	et doit appeler "SetLimits()". L'implementation par défaut ne fait rien.
//  void  HighLight(bool fgh)
//      Rafraichit le dessin du PIDrawer sur tous les PIBaseWdg auxquel il est attaché,
//	en mode "HighLight" si "fgh==true", en mode normal sinon.
//  void  AppendTextInfo(string& info, double xmin, double ymin, double xmax, double ymax)
//	Méthode qui met à jour la chaîne "info", avec les informations (textuelles) de 
//	la zone définie par "xmin-xmax" , "ymin-ymax".
//--

void
PIDrawer::UpdateLimits()
{
// Ne fait rien !
  return;
}

void
PIDrawer::Refresh()
{
  list<DrwBWId>::iterator it;
  for(it = mBWdgList.begin(); it != mBWdgList.end(); it++)
         (*it).wdg->CallDrawer((*it).id);

}
void
PIDrawer::HighLight(bool fgh)
{
  mFgHighLight = fgh;
  Refresh();
  mFgHighLight = false;
}

//++
// Titre	Les attributs graphiques
//--
//++
// void  SetColAtt(PIColors fg=PI_NotDefColor, PIColors bg=PI_NotDefColor)
//	Modifie la couleur d'avant-plan et de fond par défaut
// void  SetLineAtt(PILineAtt lat=PI_NotDefLineAtt)
//	Modifie l'attribut type de ligne
// void  SetFontAtt(PIFontSize fsz=PI_NotDefFontSize, PIFontAtt fat=PI_NotDefFontAtt)
//	Selection taille et attribut de fonte 
// void  SetFont(PIFontName fn, PIFontSize fsz=PI_NotDefFontSize, PIFontAtt fat=PI_NotDefFontAtt)
//	Selection de type, taile et attribut de fonte
// void  SetMarkerAtt(int sz=-1, PIMarker mrk=PI_NotDefMarker)
//	L'attribut type et taille de marker
// void  SetColMapId(CMapId cid=CMAP_OTHER, bool rev=false)
//	L'attribut choix de la table de couleurs, et son flag d'inversion
// void  SelGraAtt(PIGraphicUC* g)
//	Modifie les attributs graphiques de "g" à partir des attributs courant
//	de l'objet "PIDrawer"
// 
// PIColors  GetFgColAtt()
//	Renvoie l'attribut de couleur d'avant-plan
// PIColors  GetBgColAtt()
//	Renvoie l'attribut de couleur d'arrière-plan
// PILineAtt  GetLineAtt()
//	Renvoie l'attribut de ligne
// PIFontSize GetFontSz()
//	Renvoie l'attribut de taille de fonte
// PIFontAtt  GetFontAtt()
//	Renvoie l'attribut de type de fonte
// int  GetMarkerSz()
//	Renvoie l'attribut de taille de marker
// PIMarker GetMarker()
//	Renvoie l'attribut de type de marker
// CMapId GetColMapId()
//	Renvoie l'attribut de type de table de couleur
// CMapId GetColMapId(bool & rev)
//	Renvoie l'attribut de type de table de couleur, 
//	et le flag d'inversion de la table de couleur
//--

void 
PIDrawer::SetColAtt(PIColors fg, PIColors bg)
{
  mFCol = fg;
  mBCol = bg;
}

void 
PIDrawer::SetLineAtt(PILineAtt lat)
{
  mLAtt = lat;
}

void 
PIDrawer::SetFontAtt(PIFontSize fsz, PIFontAtt fat)
{
  mFSz = fsz;
  mFAtt = fat;
}

void 
PIDrawer::SetFont(PIFontName fn, PIFontSize fsz, PIFontAtt fat)
{
  mFName = fn;
  mFSz = fsz;
  mFAtt = fat;
}

void 
PIDrawer::SetMarkerAtt(int sz, PIMarker mrk)
{
  mMSz = sz;
  mMrk = mrk;
}

void 
PIDrawer::SetColMapId(CMapId cid, bool rev)
{
  mCmapid = cid;
  mRevCmap = rev;
}

void 
PIDrawer::SelGraAtt(PIGraphicUC* g)
{
  if (mFCol != PI_NotDefColor)    g->SelForeground(mFCol);
  if (mBCol != PI_NotDefColor)    g->SelBackground(mBCol);
  if (mLAtt != PI_NotDefLineAtt)  g->SelLine(mLAtt);
  if (mFName != PI_DefaultFont) {
    PIFont myfont(mFName);
    myfont.SetFontAtt(mFAtt);
    myfont.SetFontSz(mFSz);
    g->SelFont(myfont);
  }
  else if ( (mFSz != PI_NotDefFontSize) || (mFAtt != PI_NotDefFontAtt)  )
                                  g->SelFont(mFSz, mFAtt);
  if ( (mMrk != PI_NotDefMarker) || (mMSz >= 0) )
                                  g->SelMarker(mMSz, mMrk);
}


void
PIDrawer::Attach(PIBaseWdgGen* wdg, int id)
{
  if (!wdg) return;
  DrwBWId bwi;
  bwi.id = id;  bwi.wdg = wdg;
  mBWdgList.push_back(bwi);
  return;
}

void
PIDrawer::Detach(PIBaseWdgGen* wdg, int id)
{
  if (mDndfg) return;
  if (!wdg) return;
  list<DrwBWId>::iterator it;
  for(it = mBWdgList.begin(); it != mBWdgList.end(); it++)
     if ( ((*it).id == id) && ((*it).wdg == wdg) ) { mBWdgList.erase(it);  break; }
  return;
}


PIGraphicUC*
PIDrawer::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;
  mGrUC = new PIGraphicUC(g, x0, y0, dx, dy);
  mGrUC->SetUCS(xMin, xMax, yMin, yMax, aXFlg, aYFlg);
  if (aXlog || aYlog) mGrUC->SetLogScale(aXlog, aYlog);
  if (mFgHighLight) mGrUC->SelGOMode(PI_GOInvert); // $CHECK$ A changer , Reza 07/2001
  return(mGrUC);
}

/* --Methode-Static-- */
void PIDrawer::BestTicks(double rmin,double rmax,int nticks,double& majt)
{
double d = rmax - rmin;
//if (d < 1.e-39) d = 1.e-39; 
if (d < 1.e-100) d = 1.e-100; 
double ld = log10(d);
double fld = floor(fabs(ld));
double del,del0;
double fac[4] = {2.,5.,10.,20.};
if(ld>=0.) { fld -= 1.; del0 = del = pow(10.,fld); }
else { fld += 2.; del0 = del = pow(10., -fld); }
int k=0;
while( d/del > (double) nticks && k<4 ) { del = fac[k]*del0; k++; }
majt = del;
}

void
PIDrawer::CalcTicks()
{
  int ntick_x = (aXlog) ? 6 : 10;
  BestTicks(xMin,xMax,ntick_x,xMajTickStep);
  xMinTickStep = xMajTickStep/5;
  xFirstMajTick = int(xMin / xMajTickStep) * xMajTickStep;
  if (xFirstMajTick < xMin) xFirstMajTick += xMajTickStep;
  xFirstMinTick = int(xMin / xMinTickStep) * xMinTickStep;
  if (xFirstMinTick < xMin) xFirstMinTick += xMinTickStep;

  int ntick_y = (aYlog) ? 6 : 12;
  BestTicks(yMin,yMax,ntick_y,yMajTickStep);
  yMinTickStep = yMajTickStep/5;
  yFirstMajTick = int(yMin / yMajTickStep) * yMajTickStep;
  if (yFirstMajTick < yMin) yFirstMajTick += yMajTickStep;
  yFirstMinTick = int(yMin / yMinTickStep) * yMinTickStep;
  if (yFirstMinTick < yMin) yFirstMinTick += yMinTickStep;

  yMajTickLen = (xMax-xMin)/100;
  yMinTickLen = (xMax-xMin)/250;
  xMajTickLen = (yMax-yMin)/100;
  xMinTickLen = (yMax-yMin)/250;
}

void
PIDrawer::SetAxesFlags(unsigned int flags)
{
  axesFlags = flags;
  if (axesFlags & (kIntTicks | kExtTicks | kMajTicks | kMinTicks))
    axesFlags |= kTicks;
  if ((axesFlags & (kTicks | kIntTicks | kExtTicks)) == kTicks)
    axesFlags |= kIntTicks | kExtTicks;
  if ((axesFlags & (kTicks | kMajTicks | kMinTicks)) == kTicks)
    axesFlags |= kMajTicks;
}


void
PIDrawer::DrawAxes(PIGraphicUC* g)
{
  g->NoClip();
  if (mLAtt == PI_NotDefLineAtt)  g->SelLine(PI_ThinLine);
  else g->SelLine(mLAtt);
  //  On calcule les ticks 
  CalcTicks();

  unsigned int flags = axesFlags;

  if (flags & kStdAxes) {

    // Les axes
    
    g->DrawLine(xMin, 0, xMax, 0);
    g->DrawLine(0, yMin, 0, yMax);

    // La grille en pointilles

    if (flags & kGridOn) DrawGrid(g);
  
    // Les ticks majeurs
  
    if (flags & kMajTicks) {
        DrawHTicks(g, 0, xMajTickLen, xMajTickLen, xFirstMajTick, xMajTickStep);
        DrawVTicks(g, 0, yMajTickLen, yMajTickLen, yFirstMajTick, yMajTickStep);
    }
    
    // Les ticks mineurs
  
    if (flags & kMinTicks) {
        DrawHTicks(g, 0, xMinTickLen, xMinTickLen, xFirstMinTick, xMinTickStep);
        DrawVTicks(g, 0, yMinTickLen, yMinTickLen, yFirstMinTick, yMinTickStep);
    }
    
    // Les labels
    
    if (flags & kLabels) {
      if (!aYdir)
        DrawHLabels(g, -xMajTickLen*8, xFirstMajTick, xMajTickStep,0);
      else 
        DrawHLabels(g, xMajTickLen*8, xFirstMajTick, xMajTickStep,0);
      if (!aXdir)
        DrawVLabels(g, -yMajTickLen*2, yFirstMajTick, yMajTickStep,1);
      else 
        DrawVLabels(g, yMajTickLen*2, yFirstMajTick, yMajTickStep,1);
    }
    
  }
  
  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 & kMajTicks) {
        DrawHTicks(g, yMin, intXMajTickLen, extXMajTickLen, xFirstMajTick, xMajTickStep);
        DrawHTicks(g, yMax, extXMajTickLen, intXMajTickLen, xFirstMajTick, xMajTickStep);
        DrawVTicks(g, xMin, extYMajTickLen, intYMajTickLen, yFirstMajTick, yMajTickStep);
        DrawVTicks(g, xMax, intYMajTickLen, extYMajTickLen, yFirstMajTick, yMajTickStep);
    }
    
    // Les ticks mineurs
  
    if (flags & kMinTicks) {
        DrawHTicks(g, yMin, intXMinTickLen, extXMinTickLen, xFirstMinTick, xMinTickStep);
        DrawHTicks(g, yMax, extXMinTickLen, intXMinTickLen, xFirstMinTick, xMinTickStep);
        DrawVTicks(g, xMin, extYMinTickLen, intYMinTickLen, yFirstMinTick, yMinTickStep);
        DrawVTicks(g, xMax, intYMinTickLen, extYMinTickLen, yFirstMinTick, yMinTickStep);
    }


    // Les labels
    
    if (flags & kLabels) {
      if (!aYdir) {
        DrawHLabels(g, g->DeltaUCY(yMin, -xMajTickLen*8), xFirstMajTick, xMajTickStep,0);
        DrawHLabels(g, g->DeltaUCY(yMax,  xMajTickLen*2), xFirstMajTick, xMajTickStep,0);
      }
      else {
        DrawHLabels(g, g->DeltaUCY(yMin, -xMajTickLen*2), xFirstMajTick, xMajTickStep,0);
        DrawHLabels(g, g->DeltaUCY(yMax,  xMajTickLen*8), xFirstMajTick, xMajTickStep,0);
      }
      if (!aXdir) {
        DrawVLabels(g, g->DeltaUCX(xMin, -yMajTickLen*2), yFirstMajTick, yMajTickStep,1);
        DrawVLabels(g, g->DeltaUCX(xMax,  yMajTickLen*2), yFirstMajTick, yMajTickStep,-1);
      }
      else {
        DrawVLabels(g, g->DeltaUCX(xMin, -yMajTickLen*2), yFirstMajTick, yMajTickStep,-1);
        DrawVLabels(g, g->DeltaUCX(xMax,  yMajTickLen*2), yFirstMajTick, yMajTickStep,1);
      }
    }
  }
  g->Clip();
}

  

void
PIDrawer::Draw(PIGraphicUC* g, double /*xmin*/, double /*ymin*/, double /*xmax*/, double /*ymax*/)
{
}

void
PIDrawer::AppendTextInfo(string& info, double /*xmin*/, double /*ymin*/, double /*xmax*/, double /*ymax*/)
{
}

void
PIDrawer::DrawHTicks(PIGraphicUC* g, double y, double tickUp, double tickDown, double xBeg, double xStep)
{
  for (double x=xBeg; x<=xMax+xStep/10.; x += xStep)
    g->DrawLine(x, g->DeltaUCY(y, -tickDown), x, g->DeltaUCY(y, tickUp));
}

void
PIDrawer::DrawVTicks(PIGraphicUC* g, double x, double tickLeft, double tickRight, double yBeg, double yStep)
{
  for (double y=yBeg; y<=yMax; y += yStep)
    g->DrawLine(g->DeltaUCX(x, -tickLeft), y, g->DeltaUCX(x,tickRight), y);
}

void
PIDrawer::DrawHLabels(PIGraphicUC* g, double y, double xBeg, double xStep, int just)
{
  double xOffset = 0;
  int kk;

  g->SelFontSz(xMajTickLen*3., mFAtt);

  // Choix du bon format pour les labels des axes;
  string format;
  BonFormatAxes(xBeg,xMax,xStep,format);

  double xlastlabelfin = xBeg - 2.*(xMax-xBeg);
  for (double x=xBeg; x<=xMax+xStep/10.; x += xStep) {
      // Attention aux erreurs d'arrondi quand x->0
      //        car on code 5.1698e-26 au lieu de 0
      double xx = (fabs(x/xStep)<1.e-5) ? 0.: x;
      char label[64]; sprintf(label,format.c_str(), xx);
      for(kk=0;kk<64;kk++) if(label[kk]==' ') {label[kk]='\0'; break;}
      double largeur = g->CalcStringWidth(label);
      if(just == 1)        xOffset = -largeur;
        else if(just == 0) xOffset = -largeur/2;
          else             xOffset = 0;
      if(aXdir)            xOffset = -xOffset;
      if( x+xOffset > xlastlabelfin ) {
        g->DrawString(g->DeltaUCX(x,xOffset), y, label);
        xlastlabelfin = x + xOffset + 1.1*largeur;
      }
  }
}

void
PIDrawer::DrawVLabels(PIGraphicUC* g, double x, double yBeg, double yStep, int just)
{
  double xOffset = 0;
  g->SelFontSz(xMajTickLen*3., mFAtt);

  // Choix du bon format pour les labels des axes;
  string format;
  BonFormatAxes(yBeg,yMax,yStep,format);

  for (double y=yBeg; y<=yMax; y += yStep) {
      double yy = (fabs(y/yStep)<1.e-5) ? 0.: y;
      char label[64]; sprintf(label,format.c_str(),yy);
      for(int kk=0;kk<64;kk++) if(label[kk]==' ') {label[kk]='\0'; break;}
      double largeur = g->CalcStringWidth(label);
      if (just == 1)
        xOffset = -largeur;
      else if (just == 0)
        xOffset = -largeur/2;
      else
        xOffset = 0;
      if (aXdir)  xOffset = -xOffset;
      g->DrawString(g->DeltaUCX(x, xOffset), y, label);
  }
}

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

  for (double x=xFirstMajTick; x<=xMax; x += xMajTickStep)
    g->DrawLine(x, yMin, x, yMax);

  for (double y=yFirstMajTick; y<=yMax; y += yMajTickStep)
    g->DrawLine(xMin, y, xMax, y);

  g->SelLine(savlineatt);
}

/* --Methode-Static-- */
int PIDrawer::BonFormatAxes(double xmin,double xmax,double dx,string& format, int add_digit)
// Calcul format optimal pour ecrire les axes
// double = 17 digits : +d.(17digits)e+ddd  -> min 25%17e  -> securite 26%18e
// ** add_digit nombre de digit a ajouter au nombre de digit minimum.
{
 format = "%-6g";  // format par default
 if(xmin>=xmax || dx<=0.) return -1;

 //////////// Calcul du nombre de digits necessaires
 char str[32],str0[32];
 int ndig = 0;
 //printf("           1         2         3\n");
 //printf("|0123456789012345678901234567890|\n");
 int npass = 0;  // au cas ou dx est tout petit et "x+=dx == x" !
 for(double x=xmin; x<=xmax; x+=dx) {
   // Le format %e ecrit les "-" pour x<0 mais pas les "+" pour x>0
   // Il faut gerer le zero car 0 -> 0.000e+00 mais -0 -> -0.000e+00
   if(x>0.)      sprintf(str,"+%-26.18e",x);
   else if(x<0.) sprintf(str,"-%-26.18e",fabs(x));
   else          sprintf(str,"+%-26.18e",0.);
   //printf("|%s|",str);
   if(npass!=0) {
     int j;
     for(j=0;j<(int)strlen(str);j++) if(str0[j]!=str[j]) break;
     //printf(" -> ...j= %d",j);
     if(j>ndig) ndig=j;
     //printf("    ndig= %d  |%26.18e| (%-6g)\n",ndig,x,x);
   } //else printf("\n");
   strcpy(str0,str);
   if(npass>1000) break; npass++;
 }

 //////////// Calcul du bon format
 // position du "e"
 char* cdum = index(str,'e');
 int pose = cdum - str;
 //printf("pose=%d   strlen=%d\n",pose,strlen(str));

 // on differe dans les puissances ..e+ddd
 if(ndig>pose) return 0;

 // on calcule le nombre de digits necessaires
 ndig -= 2;  // +1 - 3 (car par ex:"+9.")

 // on ajoute des digits au cas ou on veut plus que le minimum requis
 ndig += add_digit;

 // Si peu de digits on reste avec le format par defaut
 // Attention: %6g arrondi le 6ieme digit -> ndig<5 !
 if(ndig<6) return ndig;

 // Calcule du nombre nn devant le format %nn.ddde
 //                        +a.<---ddd--->e+123
 //                       nndig = ddd + 8
 int nndig = ndig + 8; if(nndig<=0) nndig = 26;

 // On evite d'ecrire d.ddde+00   -> format %f
 if(  (xmin>=1. && xmin<10. && xmax>=1. && xmax<10.) 
   || (xmin>-10. && xmin<=-1. && xmax>-10. && xmax<=-1.) ) {
   sprintf(str,"%%-%d.%df",nndig,ndig);
 } else {
   sprintf(str,"%%-%d.%de",nndig,ndig);
 }
 format = str;
 //printf("format= %s\n",format.c_str());
 return ndig;
}

/*  --------------------------------------------------------------  */
/*  ------------------- Classe PIElDrawer ------------------------  */
/*  --------------------------------------------------------------  */

//++
// Class	PIElDrawer
// Lib		PI
// include	pidrawer.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
// PIDrawer
//--

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

//++
// PIElDrawer()
//	Constructeur
//--

/* --Methode-- */
PIElDrawer::PIElDrawer()
{
mEn = 0;
titleT = titleB = "";
}

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


//++
//  void  SetTitles(const char* tt=NULL, const char* tb=NULL)
//	Ajout de titres haut ("tt") et bas ("tb")
//  void  SetTitles(string const & tt, string const & tb)
//	Ajout de titres haut ("tt") et bas ("tb")
//--

/* --Methode-- */
void PIElDrawer::SetTitles(const char* tt, const char* tb)
{
if (tt != NULL)  titleT = tt;
if (tb != NULL)  titleB = tb;
}

/* --Methode-- */
void PIElDrawer::SetTitles(string const & tt, string const & tb)
{
titleT = tt;  titleB = tb;
}

//++
// int  ElAddText(PIGrCoord x, PIGrCoord y, const char* s, PIColors c=PI_NotDefColor)
//	Ajout d'un élément texte avec un attribut de couleur.
//	Renvoie le numéro identificateur de l'élément.
// int  ElAddLine(PIGrCoord x1, PIGrCoord y1, PIGrCoord x2, PIGrCoord y2, -
//                 PIColors c=PI_NotDefColor)
//	Ajout d'un élément de ligne avec un attribut de couleur.
//	Renvoie le numéro identificateur de l'élément.
// int  ElAddRect(PIGrCoord x, PIGrCoord y, PIGrCoord dx, PIGrCoord dy, -
//                PIColors c=PI_NotDefColor)
//	Ajout d'un élément de type rectangle avec un attribut de couleur.
//	Renvoie le numéro identificateur de l'élément.
// int  ElAddFRect(PIGrCoord x, PIGrCoord y, PIGrCoord dx, PIGrCoord dy, -
//                PIColors c=PI_NotDefColor)
//	Ajout d'un élément de type rectangle plein avec un attribut de couleur.
//	Renvoie le numéro identificateur de l'élément.
// int  ElAddCirc(PIGrCoord x, PIGrCoord y, PIGrCoord r, PIColors c=PI_NotDefColor)
//	Ajout d'un élément de type cercle avec un attribut de couleur.
//	Renvoie le numéro identificateur de l'élément.
// int  ElAddFCirc(PIGrCoord x, PIGrCoord y, PIGrCoord r, PIColors c=PI_NotDefColor)
//	Ajout d'un élément de type cercle plein avec un attribut de couleur.
//	Renvoie le numéro identificateur de l'élément.
// 
// void  ElDel(int id)
//	Suppression de l'élément avec l'identificateur "id".
// void  ElDelAll()
//	Suppression de tous les éléments du dessin.
//--

/* --Methode-- */
void  PIElDrawer::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, mFAtt);
SelGraAtt(g);   // DrawAxes change certains attributs graphiques

// Trace des titres 
if ( (titleT.length() > 0) || (titleB.length() > 0) ) {
  aXdir = false;  // Vrai si Axe X de Droite vers Gauche 
  aYdir = false;  // Vrai si Axe Y de Haut vers Bas
  PIGrCoord gas, gds;
  double fx, fy, fh;
//  fh = g->GetFontHeightUC(gas, gds);
  fh = 0.075*(YMax()-YMin());
  if ( titleT.length() > 0) {
      fx = g->CalcStringWidth(titleT.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(YMin(), -fh);
      else fy = g->DeltaUCY(YMax(), fh);
      g->DrawString(fx, fy, titleT.c_str());
//      printf(" PIElDrawer::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(fx, fy, titleB.c_str());
//      printf(" PIElDrawer::Draw()/DBG (%g , %g) %s  - %g \n", fx, fy, titleT.c_str(), fh);
    }
  }
DrwElList::iterator it;
PIColors cc = g->GetForeground();
for (it = mElist.begin(); it != mElist.end(); it++)
  {
  if ((*it).col != PI_NotDefColor)  g->SelForeground((*it).col);
  switch ( (*it).etyp )
    {
    case PIDEL_Line :
      g->DrawLine((*it).ex, (*it).ey, (*it).edx, (*it).edy ); 
      break;
    case PIDEL_Text :
      g->DrawString((*it).ex, (*it).ey, (char*) ((*it).es.c_str()) ); 
      break;
    case PIDEL_Rect :
      g->DrawBox((*it).ex, (*it).ey, (*it).edx, (*it).edy ); 
      break;
    case PIDEL_FRect :
      g->DrawFBox((*it).ex, (*it).ey, (*it).edx, (*it).edy ); 
      break;
    case PIDEL_Circ :
      g->DrawCircle((*it).ex, (*it).ey, (*it).edx ); 
      break;
    case PIDEL_FCirc :
      g->DrawFCircle((*it).ex, (*it).ey, (*it).edx ); 
      break;
    default :
      break;
    }
  g->SelForeground(cc);
  }
}


/* --Methode-- */
int PIElDrawer::ElAdd(int typ, PIGrCoord x, PIGrCoord  y, PIGrCoord dx, PIGrCoord dy, const char* s, PIColors c)
{
DrwEl dre;
mEn++;
dre.eid = mEn;  dre.etyp = typ;
dre.ex = x;   dre.ey = y;
dre.edx = dx;  dre.edy = dy;
dre.col = c;
if (s)  dre.es = s;
mElist.push_back(dre);
return(mEn);
}

/* --Methode-- */
void  PIElDrawer::ElDel(int id)
{
DrwElList::iterator it;
for (it = mElist.begin(); it != mElist.end(); it++)
  if ( (*it).eid == id) { mElist.erase(it);   break; }
return;
}

/* --Methode-- */
void  PIElDrawer::ElDelAll()
{
mElist.erase(mElist.begin(), mElist.end());
return;
}

