// Module PI : Peida Interactive  PIYfXDrawer, PIFuncDrawer
// Drawer Y=f(X)      E.Aubourg, R. Ansari  96-02
// (C) LAL (Orsay) / IN2P3-CNRS  DAPNIA/SPP (Saclay) / CEA

#include "sopnamsp.h"
#include "machdefs.h"
#include <stdlib.h>
#include <iostream>
#include <math.h>
#include "piyfxdrw.h"
#include "parradapter.h"

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
// ----------------------------------------------------------
//  Classe de trace de points/fonctions Y=f(X)
//  Trace Y=ay->Value() = f(X= ax->Value()) si ax et ay != NULL
//  Trace Y=ax->Value() = f(X= ax->X())  si ay == NULL
// ----------------------------------------------------------
// --------------------------------------------------------------------------

//++
// Class	PIYfXDrawer
// Lib		PI
// include	piyfxdrw.h
//
//	Classe de traceur de points/fonctions Y = f(X).
//--
//++
// Links	Parents
// PIDrawer
//--
//++
// Links	Voir aussi
// P1DArrayAdapter
//--
//++
// Titre	Constructeur
//--

//++
// PIYfXDrawer(P1DArrayAdapter* ax, P1DArrayAdapter* ay, bool ad)
//	Constructeur. Tracé de 
//|	 Y=ay->Value() = f(X= ax->Value()) si ax et ay != NULL
//|	Y=ax->Value() = f(X= ax->X())  si ay == NULL
//	Si "ad==true" les objets "ax" et "ay" sont détruits par 
//	le destructeur de "PIYfXDrawer".
//--

/* --Methode-- */
PIYfXDrawer::PIYfXDrawer(P1DArrayAdapter* ax, P1DArrayAdapter* ay, bool ad)
    : PIDrawer()
{
  mAdDO = ad;
  mAx = ax;
  mAy = ay;
  mName = "YfXDrawer";
  NptDraw = 0;
} 

/* --Methode-- */
PIYfXDrawer::~PIYfXDrawer()
{
  if (mAdDO && mAx)  delete mAx;
  if (mAdDO && mAy)  delete mAy;
}

/* --Methode-- */
void PIYfXDrawer::UpdateLimits()
{
  if ((mAx == NULL))  mSz = 0;
  else {
    if (!mAy)  mSz = mAx->Size();
    else   mSz = (mAx->Size() < mAy->Size()) ? mAx->Size() :  mAy->Size();
    }
  if (mSz <= 0)  return;
  double xmin, xmax, ymin, ymax;
  int i=0;
  double cv;
  if (mAy) {
    xmin = xmax = mAx->Value(0);
    ymin = ymax = mAy->Value(0);
    for(i=1; i<mSz; i++) {
      cv = mAx->Value(i); 
      if (cv < xmin)  xmin = cv;
      if (cv > xmax)  xmax = cv;
      cv = mAy->Value(i); 
      if (cv < ymin)  ymin = cv;
      if (cv > ymax)  ymax = cv;
      }
    }
  else {
    xmin = xmax = mAx->X(0);
    ymin = ymax = mAx->Value(0);
    for(i=1; i<mSz; i++) {
      cv = mAx->X(i); 
      if (cv < xmin)  xmin = cv;
      if (cv > xmax)  xmax = cv;
      cv = mAx->Value(i); 
      if (cv < ymin)  ymin = cv;
      if (cv > ymax)  ymax = cv;
      }
    }

  PIAxes::ReSizeMinMax(isLogScaleX(),xmin,xmax);
  PIAxes::ReSizeMinMax(isLogScaleY(),ymin,ymax);
  SetLimits(xmin,xmax,ymin,ymax);
  // NE PAS FAIRE - Reza Mai 2005  SetAxesFlags(kBoxAxes | kExtTicks | kLabels);
}


/* --Methode-- */
void PIYfXDrawer::Draw(PIGraphicUC* g, double xmin, double ymin, double xmax, double ymax)
{
  double xp,yp;
  int i;

  if (axesFlags != kAxesNone) DrawAxes(g);

  if ((mAx == NULL))  mSz = 0;
  else {
    if (!mAy)  mSz = mAx->Size();
    else   mSz = (mAx->Size() < mAy->Size()) ? mAx->Size() :  mAy->Size();
    }
  if (mSz <= 0)  return;
  NptDraw=0;

//  Trace des markers 
  if ( (GetGraphicAtt().GetMarker() != PI_NotDefMarker) ||  
       (GetGraphicAtt().GetLineAtt() == PI_NotDefLineAtt) ) {
    for(i=0; i<mSz; i++) {
      if (mAy) { xp = mAx->Value(i); yp = mAy->Value(i); }
      else { xp = mAx->X(i);  yp = mAx->Value(i); }
      if ( (xp < xmin) || (xp > xmax) || (yp < ymin) || (yp > ymax) )  continue;
      g->DrawMarker(xp, yp);
      NptDraw++;
    }
  }

  if (GetGraphicAtt().GetLineAtt() == PI_NotDefLineAtt)    return;

// Trace des lignes 
  NptDraw=0;
  double xp2, yp2;
  if (mAy) { xp2 = mAx->Value(0); yp2 = mAy->Value(0); }
  else { xp2 = mAx->X(0);  yp2 = mAx->Value(0); }
  for(i=1; i<mSz; i++) {
    xp = xp2; yp = yp2;
    if (mAy) { xp2 = mAx->Value(i); yp2 = mAy->Value(i); }
    else { xp2 = mAx->X(i);  yp2 = mAx->Value(i); }
    if ( ((xp < xmin) || (xp > xmax) || (yp < ymin) || (yp > ymax)) &&
         ((xp2 < xmin) || (xp2 > xmax) || (yp2 < ymin) || (yp2 > ymax)) )  continue;
    g->DrawLine(xp, yp, xp2, yp2);
    NptDraw++;
  }

}


/* --Methode-- */
double PIYfXDrawer::GetDistanceToPoint(double x, double y)
{
 if(mSz<=0)  return 1.e+9;

 const int nessai = 100;
 long inc = (NptDraw>nessai) ? (long)(NptDraw/nessai)+1 : 1;

 double dist = -1.e+18;
 long n = 0;
 for(long i=0; i<mSz; i++) {
   double xp,yp;
   if(mAy) {xp=mAx->Value(i); yp=mAy->Value(i);}
     else  {xp=mAx->X(i);     yp=mAx->Value(i);}
   if(xp<XMin() || xp>XMax()) continue;
   if(yp<YMin() || yp>YMax()) continue;
   if(n%inc==0) {
     xp = (xp-x)/(XMax()-XMin())/0.5;
     yp = (yp-y)/(YMax()-YMin())/0.5;
     xp = xp*xp+yp*yp;
     if(dist<0. || xp<dist) dist = xp;
   }
   n++;
 }
 dist=sqrt(fabs(dist));
 //cout<<"PIYfXDrawer: xlim="<<XMin()<<","<<XMax()<<" ylim="<<YMin()<<","<<YMax()
 //    <<" mSz="<<mSz<<" inc="<<inc<<endl;
 //cout<<"....d="<<dist<<" x="<<x<<" y="<<y<<" NptDraw="<<NptDraw<<endl;

 return dist;
}





//================================================================
// PIFuncDrawer
// Classe de traceur de fonction  double = f(fouble)
//================================================================

//++
// Class	PIFuncDrawer
// Lib		PI
// include	piyfxdrw.h
//
//	Classe de traceur de fonctions Y = f(double X).
//--
//++
// Links	Parents
// PIDrawer
//--
//++
// Titre	Constructeur
//--
//++
//  PIFuncDrawer(FUNC f)
//	Constructeur de traceur d'une fonction "FUNC f" avec
//|	typedef double(*FUNC)(double)
//--


/* --Methode-- */
PIFuncDrawer::PIFuncDrawer(PIFuncDrawer::FUNC f)
  : mFunc(f)
{}

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

/* --Methode-- */
void
PIFuncDrawer::Draw(PIGraphicUC* g, double /*xmin*/, double/*ymin*/, double/*xmax*/, double/*ymax*/)
{
  if (mFunc == NULL) return;

  PIGrCoord x1, x2, y1, y2;
  g->GetGrSpace(x1, x2, y1, y2);
  double xMax = x2;
  double xMin = x1;
  double yMax = y2;
  double yMin = y1;
  double xStep = (xMax - xMin)/100;
  double xOld = xMin;
  double yOld = mFunc(xMin);
  for (double x = xMin+xStep; x<xMax; x+=xStep) {
    double y = mFunc(x);
    if (y>yMin && yOld>yMin && 
        y<yMax && yOld<yMax) 
           g->DrawLine(xOld, yOld, x, y);
    xOld = x;
    yOld = y;
  }
}


/* --Methode-- */
double PIFuncDrawer::GetDistanceToPoint(double x, double y)
{
 if(mFunc==NULL) return 1.e+9;

 double xStep = (XMax()-XMin())/50;
 double dist = -1.e+18;
 for(double xx=XMin()+xStep; xx<XMax(); xx+=xStep) {
   double xp = (xx-x)/(XMax()-XMin())/0.5;
   double yp = (mFunc(xp)-y)/(YMax()-YMin())/0.5;
   xp = xp*xp+yp*yp;
   if(dist<0. || xp<dist) dist = xp;
 }
 dist=sqrt(fabs(dist));
 //cout<<"PIFuncDrawer: xlim="<<XMin()<<","<<XMax()<<" ylim="<<YMin()<<","<<YMax()<<endl;
 //cout<<"....d="<<dist<<" x="<<x<<" y="<<y<<endl;

 return dist;
}
