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

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

#include "ucckprot.h"


//++
// 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
// PIGraphicAtt
// PIAxes
//--

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

//++
// PIDawer()
//	Constructeur.
// ~PIDrawer()
//	Destructeur
// void  SetAutoDeleteOnDetach(bool fg)
//	Si "fg==true", l'objet PIDrawer est détruit lorsqu'il est détaché du dernier 
//	PIBaseWdg auquel il était attaché.
// void  SetAutoRefreshOnDelete(bool fg)
//	Si "fg==true", la méthode "Refresh()" est appelé sur les PIBaseWdg lors de 
//	la destruction (delete) de l'objet PIDrawer.
// void	 SetName(string const& name)
//	Spécification d'un nom pour l'objet PIDrawer.
// string  Nom()
//	Renvoie le nom de l'objet.
// string& Name()
//	Renvoie le nom de l'objet (référence à). 
//--

PIDrawer::PIDrawer()
{
  mFgDeleteOnDetach = false;
  mFgRefreshOnDelete = false;
  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 = false;
  SetAxesFlags(kAxesNone);   // Pas de trace d'axes par defaut 
  SetAxesAutoFontSize();

  mDndfg = false;  // Pour controle de l'appel de Detach() si delete

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


  // Nom par defaut : Le nom de la classe
  //  mName = typeid(*this).name();
  mName = "PIDrawer";
  // Par defaut, pas de fenetre de controle specifique
  mFgSpecContWind = false;
}

PIDrawer::~PIDrawer()
{
  // Desactivation totale de la fenetre de controle specialise 
  // ---> parametre d'appel PIBaseWdgGen* wdg=NULL
  // DeactivateControlWindow(NULL);
  // Il semble qu'a ce niveau d'appel du desctructeur,
  // la table des fonctions virtuelles soit alterees et 
  // la fonction virtuelle DeactivateControlWindow() pointe sur celle
  // de base - DeactivateControlWindow(NULL) doit donc etre appele
  // ds le destructeur de la classe derivee 
  //                    Reza - Octobre 2002 

  // Mise a jour eventuelle de la fenetre PIDrwTools
  bool ckdeact = (PIDrwTools::GetActiveDrawer() == this) ? true : false;

  list<DrwBWId>::iterator it;
  mDndfg = true;
  for(it = mBWdgList.begin(); it != mBWdgList.end(); it++) {
    (*it).wdg->RemoveDrawer((*it).id);
    if (mFgRefreshOnDelete) (*it).wdg->Refresh();
  }
  if (mGrUC)  delete mGrUC;
  if (ckdeact) PIDrwTools::UpdateActiveDrawer();
  // On supprime la fenetre PIDrwOptionStringWindow le cas echeant
  PIDrwOptionStringWindow::RemoveDrwOptionStringWindow(this);
}

//++
// Titre	Gestion des axes et des limites
//      Le tracé des axes 2D est pris en charge par la classe PIAxes.
//--
//++
// 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  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.
// bool  LimitsFixed()
//	Renvoie la valeur du flag "limitsFixed". Le flag est mis à 
//	"true" si la méthode "SetLimits()" a été appelée.
// void  FreeLimits()
//	Met le flag "limitsFixed" à "false".
//
// void  SetLogScale(bool logx, bool logy) 
//	Choix d'échelle logarithmique pour les axes X,Y.
// void  GetAxesConfig(int& xa, int& ya) 
//	Renvoie la configuration des axes.
//|	kAxeDirLtoR , kAxeDirRtoL  (Axe X)
//|	kAxeDirDownUp , kAxeDirUpDown (Axe Y)	
// 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 (voir la classe *PIAxes*)
//|	kStdAxes , kBoxAxes , kTicks , kIntTicks , kExtTicks 
//|	kMajTicks , kMinTicks , kLabels , kGridOn
//|	kAxesDflt , kAxesNone
// void SetAxesAutoFontSize(bool fg=true)
//	Si "fg==true", la taille de la fonte pour les labels d'axes (chiffres) est 
//	choisie automatiquement, en s'adaptant à la taille de la zone de tracé - 
//	sinon, taille de la fonte de l'objet PIDrawer.
// unsigned int GetAxesFlags()
//	Renvoie les attributs de tracé d'axes  
// bool  isAxesAutoFontSize()
//	Renvoie la valeur du flag de choix automatique de taille de fonte pour axes.
// void  DrawAxes(PIGraphicUC* g)
//	Méthode de tracé des axes (le tracé est pris en charge par la classe *PIAxes*)
//--

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 (!UC_CheckFinite(xmin, xmax) || !UC_CheckFinite(ymin, ymax)) {
    cerr << "PIDrawer::SetLimits() Error - Non finite value for x/ymin-max ! " << endl;
    return; 
    }

  if (axrl == kAxeDirLtoR)  aXdir = false;
  else if (axrl == kAxeDirRtoL)  aXdir = true;
  if (ayud == kAxeDirDownUp)  aYdir = false;
  else if (ayud == kAxeDirUpDown)  aYdir = true;
  //  cerr << " DBG-SetLimits xmax-xmin= " << xmax-xmin << " xmin,max=" 
  //     << xmin << "," << xmax << endl;
  UC_CheckMinMaxDiff(xmin, xmax);
  //cerr << " DBG-SetLimits ymax-ymin= " << ymax-ymin << " ymin,max=" 
  //     << ymin << "," << ymax << endl;
  UC_CheckMinMaxDiff(ymin, ymax);
  xMin = xmin;
  xMax = xmax;
  yMin = ymin;
  yMax = ymax;
  aXFlg = axrl;  aYFlg = ayud;
  limitsFixed = true;
}


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

//++
// Titre	Gestion du tracé
//--

//++
//  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(bool refwdg=false)
//	"refwdg==false": Rafraîchit le dessin du PIDrawer sur tous les PIBaseWdg auxquel il est attaché.
//	"refwdg==true": Appelle *Refresh()* sur tous les PIBaseWdg auxquel il est attaché.
//  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
PIDrawer::UpdateLimits()
{
// Ne fait rien !
  return;
}

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

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

//++
// Titre	Les attributs graphiques
//	Les attributs graphiques des PIDrawer sont gérés par un objet *PIGraphicAtt*.
//--
//++
// void  SetGraphicAtt(PIGraphicAtt const& att)
//	Modifie le contenu de l'objet PIGraphicAtt associé au PIDrawer à partir de 
//	"att" (opération d'affectation).
// void  UpdateGraphicAtt(PIGraphicAtt const& att)
//	Met à jour le contenu de l'objet PIGraphicAtt associé au PIDrawer à partir de "att".
// PIGraphicAtt&  GetGraphicAtt()
//	Renvoie l'objet PIGraphicAtt associé au PIDrawer.
// void SelGraAtt(PIGraphicUC* g)
//	Change les attributs graphiques de l'objet "PIGraphicUC* g" à partir des attributs 
//	de l'objet PIGraphicAtt associé au PIDrawer.
//--


void 
PIDrawer::SelGraAtt(PIGraphicUC* g)
{
  PIColors fcol = GetGraphicAtt().GetFgColor(); 
  if (fcol != PI_NotDefColor)    g->SelForeground(fcol);
  PIColors bcol = GetGraphicAtt().GetBgColor(); 
  if (bcol != PI_NotDefColor)    g->SelBackground(bcol);
  if (GetGraphicAtt().GetLineAtt() != PI_NotDefLineAtt)  
    g->SelLine(GetGraphicAtt().GetLineAtt());
  if ( (GetGraphicAtt().GetFontName() != PI_DefaultFont) ||
       (GetGraphicAtt().GetFontAtt() != PI_NotDefFontAtt) )
    g->SelFont(GetGraphicAtt().GetFont());
  if (GetGraphicAtt().GetMarker() != PI_NotDefMarker) 
    g->SelMarker(GetGraphicAtt().GetMarkerSize(), GetGraphicAtt().GetMarker());
  if (GetGraphicAtt().GetArrowMarker() !=  PI_NotDefArrowMarker) 
    g->SelArrowMarker(GetGraphicAtt().GetArrowMarkerSize(), 
		      GetGraphicAtt().GetArrowMarker());
}


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;

  // Desactivation de la fenetre de controle specialisee pour la 
  // base-widget duquel on se detache 
  DeactivateControlWindow(wdg);
  
  // Mise a jour eventuelle de la fenetre PIDrwTools
  bool ckdeact = (PIDrwTools::GetActiveDrawer() == this) ? true : false;

  list<DrwBWId>::iterator it;
  for(it = mBWdgList.begin(); it != mBWdgList.end(); it++)
     if ( ((*it).id == id) && ((*it).wdg == wdg) ) { mBWdgList.erase(it);  break; }
  if (mFgDeleteOnDetach && (mBWdgList.size() == 0))  delete this;

  if (ckdeact) PIDrwTools::UpdateActiveDrawer();
  return;
}

//++
// Titre	Fenêtre de contrôle spécialisé
//	Les méthodes suivantes permettent de faciliter la gestion d'une fenêtre de contrôle 
//	spécialisée pour un type de traceur donné.
//|	ATTENTION 
//|	Si fenetre de controle specilisee, il faut appeler explicitement 
//|	DeactivateControlWindow(NULL) dans le destructeur.	
//--
//++
// bool HasSpecificControlWindow() const 
//	Renvoie "true" si le traceur possède une fenêtre de contrôle. l'attribut protégé booléan
//	"mFgSpecContWind" doit être initialisé à si la méthode "ShowControlWindow" est redéfinie.
// void ShowControlWindow(PIBaseWdgGen* wdg)
//	Méthode effectuant l'affichage de la fenêtre de contrôle spécialisée. L'implémentation
//	par défaut affiche la fenêtre *PIDrwTools*.
//	L'argument "wdg" est le pointeur de l'objet PIBaseWdgGen qui a déclenché l'action.
// void DeactivateControlWindow(PIBaseWdgGen* wdg)
//	Méthode de désactivation de la la fenêtre de contrôle spécialisée.
//	Appelée lors de la destruction du drawer, avec le paramètre "PIBaseWdgGen* wdg = NULL",
//	ou lors du détachement du traceur. Dans ce dernier cas, le paramètre "PIBaseWdgGen* wdg"
//	pointe vers le PIBaseWdg duquel le traceur (PIDrawer) est en cours de détachement.
//	"DeactivateControlWindow(NULL)" doit être appelé dans le destructeur de la classe dérivée
//	si celle-ci possède une fenêtre de contrôle spécialisée.
//--

void
PIDrawer::ShowControlWindow(PIBaseWdgGen* wdg)
{
  // On affiche par defaut la fenetre de gestion des attributs graphiques
   PIDrwTools::ShowPIDrwTools(wdg);
}

//--------------------------------------------------------------
// Methode de desactivation de la fenetre de controle specialise
// Cette methode est appellee dans deux cas :
// 1/ lors de la destruction du drawer
//    Dans ce cas, le parametre PIBaseWdgGen* wdg = NULL
// 2/ Lorsque le drawer est detache d'un PIBaseWdg 
//    Dans ce cas, le parametre PIBaseWdgGen* wdg pointe vers le 
//    PIBaseWdg duquel le drawer est detache
// Evidemment, "this" pointe toujours sur le drawer qui est 
// en cours de destruction ou de detachement
//--------------------------------------------------------------
void
PIDrawer::DeactivateControlWindow(PIBaseWdgGen* wdg)
{
  // La mise a jour eventuelle de PIDrwTools se fait 
  // directement lors de Detach() ou delete 
  // La methode par defaut ne fait donc rien 
}

//++
// Titre	Décodage des options
//--
//++
// int  DecodeOptionString(vector<string> & opt, bool rmdecopt=true)
//	Effectue le décodage des options du traceur à partir de "opt". Si "rmdecopt==true"
//	les options décodées sont supprimées du vecteur "opt". 
//	Retourne le nombre d'options décodées.
//	L'implémentation par défaut effectue le décodage des attributs graphiques en 
//	utilisant la méthode *PIGraphicAtt::DecodeAttStrings()*
// int  OptionToString(vector<string> & opt) const
//	Méthode virtuelle qui peut être redéfinie dans les classes filles,
//	pour fournir des chaines de caracteres décodable par *DecodeOptionString()* .
//	Renvoie le nombre d'éléments ajoutés.
//	L'implémentation par défaut effectue le décodage des attributs graphiques en 
//	utilisant la méthode *PIGraphicAtt::AttToStrings()*
// void	 GetOptionsHelpInfo(string& info)
//	Ajoute au bout de la chaine "info" l'aide concernant le décodage des options du traceur.
//--
int
PIDrawer::DecodeOptionString(vector<string> & opt, bool rmdecop)
{
  if (opt.size() < 1)  return(0);  
  PIGraphicAtt gratt;
  int ndec = gratt.DecodeAttStrings(opt, rmdecop);
  UpdateGraphicAtt(gratt);
  return(ndec);
}

int
PIDrawer::OptionToString(vector<string> & opt) const
{
  return mGrAtt.AttToStrings(opt);
}

void
PIDrawer::GetOptionsHelpInfo(string& info)
{
  info += " ------- PIDrawer options help info ---------- \n";
  info += ">>> color=ColorName - fgcolor=ColorName - bgcolor=ColorName \n";
  info += "   ColorName: black white grey red blue green yellow \n";
  info += "              magenta cyan turquoise navyblue orange \n";
  info += "              siennared purple  limegreen gold violet \n";
  info += "              violetred blueviolet darkviolet skyblue \n";
  info += "              royalblue forestgreen orangered brown \n";
  info += ">>> line=DashType,LineWidth  \n";
  info += "   DashType: solid, dash, dotted, dashdotted   Width: 1,2,...\n";
  info += ">>> font=FontName,FontAtt,FontSize  \n";
  info += "   FontName: courier, helvetica, times, symbol   \n";
  info += "   FontAtt: roman, bold, italic, bolditalic  \n";
  info += "   FontSize: 6,8,10,12... (pts) - integer \n";
  info += ">>> marker=MarkerType,MarkerSize (MarkerSize: integer 3,5,7... \n";
  info += "   MarkerType: dot, plus, cross, circle, fcircle, box, fbox \n";
  info += "               triangle, ftriangle, star, fstar \n";
  info += ">>> arrow=ArrowType,ArrowSize (ArrowSize: integer 3,5,7... \n";
  info += "   ArrowType: basic, triangle, ftriangle, \n";
  info += "              arrowshaped, farrowshaped\n";
  info += ">>> ColorTables: defcmap  grey32  invgrey32  colrj32  colbr32 \n";
  info += "                grey128  invgrey128  colrj128  colbr128 \n";
  info += "                red32cm  green32cm  blue32cm  yellow32cm \n";
  info += "                orange32cm cyan32cm violet32cm \n";
  info += "                midas_pastel midas_heat midas_rainbow3 midas_bluered\n";
  info += "                midas_bluewhite midas_redwhite \n";
  info += "                multicol16 multicol64\n";
  info += ">   revcmap : This flag reverses ColorMap indexing \n";
  info += "------- Obsolete- graphic att ---------- \n";  
  info += ">> Lines:  defline normalline thinline thickline dashedline thindashedline \n";
  info += "           thickdashedline dottedline thindottedline thickdottedline \n";
  info += ">> Font Att: deffontatt normalfont boldfont italicfont bolditalicfont  \n";
  info += "             smallfont smallboldfont smallitalicfont smallbolditalicfont \n";
  info += "             bigfont bigboldfont bigitalicfont bigbolditalicfont \n";
  info += "             hugefont  hugeboldfont hugeitalicfont hugebolditalicfont \n";
  info += ">> Font Names: deffont courierfont helveticafont timesfont symbolfont  \n";
  info += ">> Marker: dotmarker<S>  plusmarker<S>  crossmarker<S> circlemarker <S> \n";
  info += "           fcirclemarker<S> boxmarker<S> fboxmarker<S> trianglemarker<S> \n";
  info += "           ftrianglemarker<S>  starmarker<S>  fstarmarker<S> \n";
  info += "   with <S> = 1 3 5 7 9 , Example fboxmarker5 , plusmarker9 ... \n";
  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 (!limitsFixed) UpdateLimits();
  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
    mGrUC->SelForeground(PI_HighlightBlue);
    mGrUC->LockForegroundColor();
    lastHLStatus = true;
  }
  else lastHLStatus = false;
  
  return(mGrUC);
}

void
PIDrawer::WdgCoord2DrwCoord(int x, int y, double& xu, double& yu, 
			    int x0, int y0, int dx, int dy)
{
  if (!limitsFixed) UpdateLimits();
  PIGraphicUC guc(NULL, x0, y0, dx, dy);
  guc.SetUCS(xMin, xMax, yMin, yMax, aXFlg, aYFlg);
  if (aXlog || aYlog) guc.SetLogScale(aXlog, aYlog);
  guc.GrC2UC(x,y,xu,yu);
  return;
}

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();
  PIAxes axes;
  axes.DrawXYAxes(g, mGrAtt, XMin(), XMax(), YMin(), YMax(),
		  axesFlags, axesAFSz); 
  g->Clip();
}

  

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

//++
// Titre	Informations textuelles associées au drawer
//	Les méthodes suivantes peuvent renvoyer une information textuelle correspondant à
//	un point ou une zone dans l'espace de coordonnées de l'objet PIDrawer.
//--
//++
// void  GetClickInfo(string& info,double x, double y, double x0=0.,double y0=0., bool fgdiff=false)
//	Met à jour la chaine "info" avec l'information associée à la position "(x,y)".
//	Si "fgdiff=true", une information d'écart par rapport à la position origine "(x0,y0)"
//	peut aussi être fournie.
//	L'implémentation par défaut ne fait rien.
// void  AppendTextInfo(string& info, double xmin, double ymin, double xmax, double ymax)
//	Met à jour la chaine "info avec l'information textuelle  associée a la zone 
//	rectangulaire "(xmin,ymin) -- (xmax,ymax)". 
//	L'implémentation par défaut ne fait rien.
// double  GetDistanceToPoint(double x, double y)
//	Fournit une distance au point "x,y". Valeur entre 0 et 1 au bord xmin/max,ymin/max.  
//	L'implémentation par défaut retourne la distance normalisée au point 
//	((xmin+xmax)/2,(ymin+ymax)/2)
//--


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

void
PIDrawer::GetClickInfo(string& info,double x,double y,double x0,double y0,bool fgdiff)
{
}

double
PIDrawer::GetDistanceToPoint(double x, double y)
{
  double xc = 0.5*(XMin()+XMax());
  double yc = 0.5*(YMin()+YMax());
  double delx = (x-xc)/(0.5*(XMax()-XMin()));
  double dely = (y-yc)/(0.5*(YMax()-YMin()));  
  return sqrt((delx*delx+dely*dely)/2.);
}
