#include "graph.h"

Graph::Graph()
  {
  image=0;
  matrix=0;
  vec=0;
  }

Graph::Graph(Graphics::TBitmap * img)
  {
  Init(img);
  }

Graph::~Graph()
  {
  image=0;
  canvas=0;
  GraphTitle=XAxis=YAxis="";
  if (matrix!=0) delete matrix;
  if (vec!=0) delete [] vec;
  }

void Graph::Init(Graphics::TBitmap * img)
  {
  image=img;
  canvas=image->Canvas;
  matrix=new Matrix(0,0);
  vec=0;
  border_offset=50;
  GraphTitle=XAxis=YAxis="";
  tags=false;
  dots=false;
  tags_position=0;
  transparent_tags=false;
  ClearGraph();
  }

void Graph::SetLabels(AnsiString title, AnsiString x, AnsiString y)
  {
  GraphTitle=title;
  XAxis=x;
  YAxis=y;
  }

void Graph::SetTags(bool TagsVisible, unsigned int TagsPosition, bool TagsTransparent)
  {
  tags=TagsVisible;
  tags_position=TagsPosition;
  transparent_tags=TagsTransparent;
  }

void Graph::SetDots(bool DotsVisible)
  {
  dots=DotsVisible;
  }

void Graph::SetRange(double xmin, double xmax, double ymin, double ymax)
  {
  x_min=xmin;
  x_max=xmax;
  y_min=ymin;
  y_max=ymax;

  x_scale=(image->Width-2*border_offset)/(x_max-x_min);
  y_scale=(image->Height-2*border_offset)/(y_max-y_min);
  }

void Graph::SetScale(double xtick, double xscale, double ytick, double yscale)
  {
  x_graduation=xscale;
  x_tick_position=xtick;
  y_graduation=yscale;
  y_tick_position=ytick;
  }

void Graph::GetRange(double *xmin, double *xmax, double *ymin, double *ymax)
  {
  *xmin=x_min;
  *xmax=x_max;
  *ymin=y_min;
  *ymax=y_max;
  }

void Graph::GetGraduation(double* xgrad, double* ygrad)
  {
  *xgrad=x_graduation;
  *ygrad=y_graduation;
  }

void Graph::AutoScale()
  {
  double xmin,xmax,ymin,ymax;
  if (matrix!=0)
    {
    xmin=matrix->ColMin(0);
    xmax=matrix->ColMax(0);
    ymin=matrix->ColMin(1);
    ymax=matrix->ColMax(1);
    double x_range=xmax-xmin;
    double y_range=ymax-ymin;
    if (x_range==0)
      {
      xmin-=1;
      xmax+=1;
      }
      else
        {
        xmin-=(x_range)*0.05;
        xmax+=(x_range)*0.05;
        }
    if (y_range==0)
      {
      ymin-=1;
      ymax+=1;
      }
      else
        {
        ymin-=(y_range)*0.05;
        ymax+=(y_range)*0.05;
        }
    }
    else
      {
      xmin=-10;
      xmax=10;
      ymin=-10;
      ymax=10;
      }
  SetRange(xmin,xmax,ymin,ymax);
  x_tick_position=0;
  y_tick_position=0;
  x_graduation=ceil(x_max-x_min)/8.0;
  y_graduation=ceil(y_max-y_min)/8.0;
  //Plot();
  }

void Graph::Proportional()
  {
  double x_range=x_max-x_min;
  double y_range=y_max-y_min;
  double x_center=(x_min+x_max)/2.0;
  double y_center=(y_min+y_max)/2.0;
  double proportion=(image->Width*1.0-2*border_offset)/(image->Height-2*border_offset);

  if (x_range/y_range > proportion)
    SetRange(x_min,x_max,y_center-x_range/proportion/2.0,y_center+x_range/proportion/2.0);
    else
      SetRange(x_center-y_range*proportion/2.0,x_center+y_range*proportion/2.0,y_min,y_max);
  y_graduation=ceil(y_max-y_min)/8.0;
  x_graduation=y_graduation;
  }

void Graph::DrawAxes()
  {
  TColor text_color=clBlack;
  TColor bg_color=clWhite;
  TColor axes_color=clBlack;
  unsigned int tick_length=5;

  double hgrad,vgrad;
  if (x_graduation == 0)
    hgrad=ceil(x_max-x_min)/8.0;
    else
      hgrad=fabs(x_graduation);
  if (y_graduation == 0)
    vgrad=ceil(y_max-y_min)/8.0;
    else
      vgrad=fabs(y_graduation);

  TColor pcol=canvas->Pen->Color;
  TColor bcol=canvas->Brush->Color;
  TBrushStyle bstyle=canvas->Brush->Style;
  TColor fcol=canvas->Font->Color;

  canvas->Font->Color=text_color;
  canvas->Pen->Color=bg_color;
  canvas->Brush->Style=bsSolid;
  canvas->Brush->Color=bg_color;
  canvas->Rectangle(0,0,image->Width,border_offset);
  canvas->Rectangle(0,image->Height-border_offset,image->Width,image->Height);
  canvas->Rectangle(0,border_offset,border_offset,image->Height-border_offset);
  canvas->Rectangle(image->Width-border_offset,border_offset,image->Width,image->Height-border_offset);

  canvas->Pen->Color=axes_color;
  canvas->Brush->Color=axes_color;
  canvas->Brush->Style=bsClear;

  TPoint points[4];
  points[0] = Point(border_offset,border_offset);
  points[1] = Point(image->Width-border_offset,border_offset);
  points[2] = Point(image->Width-border_offset,image->Height-border_offset);
  points[3] = Point(border_offset,image->Height-border_offset);
  canvas->Polygon(points, 3);

  double x_tick1_position=ceil((x_min-x_tick_position)/hgrad)*hgrad+x_tick_position;
  unsigned int x_nticks=int((x_max-x_tick1_position)/hgrad)+1;
  double y_tick1_position=ceil((y_min+1/y_scale-y_tick_position)/vgrad)*vgrad+y_tick_position;
  unsigned int y_nticks=int((y_max-1/y_scale-y_tick1_position)/vgrad)+1;

  AnsiString text;
  if (x_tick1_position<x_max)
    for (unsigned int i=0; i < x_nticks; i++)
      {
      canvas->MoveTo(border_offset+ceil((x_tick1_position+i*hgrad-x_min)*x_scale),image->Height-border_offset);
      canvas->LineTo(border_offset+ceil((x_tick1_position+i*hgrad-x_min)*x_scale),image->Height-border_offset-tick_length);
      canvas->MoveTo(border_offset+ceil((x_tick1_position+i*hgrad-x_min)*x_scale),border_offset);
      canvas->LineTo(border_offset+ceil((x_tick1_position+i*hgrad-x_min)*x_scale),border_offset+tick_length);
      text=FloatToStrF(x_tick1_position+i*hgrad,ffFixed,15,(FloatToStr(hgrad).Length()-IntToStr(long(hgrad)).Length()-1)<0?0:(FloatToStr(hgrad).Length()-IntToStr(long(hgrad)).Length()-1));
      canvas->TextOut(border_offset+ceil((x_tick1_position+i*hgrad-x_min)*x_scale)-canvas->TextExtent(text).cx/2,image->Height-border_offset+2,text);
      }
  if (y_tick1_position<y_max)
    for (unsigned int i=0; i < y_nticks; i++)
      {
      canvas->MoveTo(border_offset,border_offset+ceil((y_max-(y_tick1_position+i*vgrad))*y_scale));
      canvas->LineTo(border_offset+tick_length,border_offset+ceil((y_max-(y_tick1_position+i*vgrad))*y_scale));
      canvas->MoveTo(image->Width-border_offset,border_offset+ceil((y_max-(y_tick1_position+i*vgrad))*y_scale));
      canvas->LineTo(image->Width-border_offset-tick_length,border_offset+ceil((y_max-(y_tick1_position+i*vgrad))*y_scale));
      text=FloatToStrF(y_tick1_position+i*vgrad,ffFixed,15,(FloatToStr(vgrad).Length()-IntToStr(long(vgrad)).Length()-1)<0?0:(FloatToStr(vgrad).Length()-IntToStr(long(vgrad)).Length()-1));
      canvas->TextOut(border_offset-canvas->TextExtent(text).cx-2,border_offset+ceil((y_max-(y_tick1_position+i*vgrad))*y_scale)-canvas->TextExtent(text).cy/2,text);
      }

  canvas->TextOut(image->Width/2-canvas->TextExtent(XAxis).cx/2,image->Height-border_offset/2-canvas->TextExtent(XAxis).cy/2,XAxis);
  canvas->TextOut(image->Width/2-canvas->TextExtent(GraphTitle).cx/2,border_offset/2-canvas->TextExtent(GraphTitle).cy/2,GraphTitle);

  Graphics::TBitmap *bitmap=new Graphics::TBitmap;

    bitmap->Height=canvas->TextExtent(YAxis).cy;
    bitmap->Width=canvas->TextExtent(YAxis).cx;
    bitmap->Canvas->Font->Color=text_color;
    bitmap->Canvas->Brush->Color=bg_color;
    bitmap->Canvas->Font=canvas->Font;
    bitmap->Canvas->TextOut(0,0,YAxis);

    for (int i=0; i < bitmap->Width; i++)
      for (int j=0; j < bitmap->Height; j++)
        if (bitmap->Canvas->Pixels[i][j]!=bg_color)
          canvas->Pixels[/*border_offset/2-bitmap->Height+*/j+5][image->Height/2+bitmap->Width/2-i]=bitmap->Canvas->Pixels[i][j];

    delete bitmap;

  canvas->Pen->Color=pcol;
  canvas->Brush->Color=bcol;
  canvas->Brush->Style=bstyle;
  canvas->Font->Color=fcol;
  }

void Graph::DrawCircle(int x, int y, int r, TColor color)
  {
  TColor pcol=canvas->Pen->Color;
  TColor bcol=canvas->Brush->Color;
  TBrushStyle bstyl=canvas->Brush->Style;

  canvas->Brush->Color=color;
  canvas->Brush->Style=bsSolid;
  canvas->Pen->Color=color;
  canvas->Ellipse(x-r,y-r,x+r,y+r);
  if (r == 0) canvas->Pixels[x][y]=color;

  canvas->Pen->Color=pcol;
  canvas->Brush->Color=bcol;
  canvas->Brush->Style=bstyl;
  }

void Graph::DrawFrameOnTop(int xmin, int xmax, int ymin, int ymax)
  {
  ClearGraph();
  DrawGraph();
  DrawAxes();

  TColor pcol=canvas->Pen->Color;
  TBrushStyle bstyl=canvas->Brush->Style;

  canvas->Pen->Color=clBlue;
  canvas->Brush->Style=bsClear;

  TPoint points[4];
  points[0] = Point(xmin,ymin);
  points[1] = Point(xmax,ymin);
  points[2] = Point(xmax,ymax);
  points[3] = Point(xmin,ymax);
  canvas->Polygon(points, 3);

  canvas->Pen->Color=pcol;
  canvas->Brush->Style=bstyl;

  //image->Repaint();
  //image->Refresh();
  }

void Graph::DrawPoint(double x, double y, int r, TColor color)
  {
  if ((x >= x_min) && (x <= x_max) && (y >= y_min) && (y <= y_max))
    DrawCircle(border_offset+(x-x_min)*x_scale,border_offset+(y_max-y)*y_scale,r,color);
  }

void Graph::DrawTxt(double x, double y, AnsiString label, int size, TColor color)
  {
  if (transparent_tags)
    {
    Graphics::TBitmap *bitmap=new Graphics::TBitmap;
      bitmap->Canvas->Font=canvas->Font;
      bitmap->Canvas->Font->Size=canvas->Font->Size+(size-3);
      bitmap->Canvas->Font->Name="Times New Roman";
      bitmap->Canvas->Font->Color=color;
      bitmap->Height=bitmap->Canvas->TextExtent(label).cy;
      bitmap->Width=bitmap->Canvas->TextExtent(label).cx;
      bitmap->Canvas->TextOut(0,0,label);
      bitmap->Transparent=true;
      bitmap->TransparentColor=clWhite;
      bitmap->TransparentMode=tmFixed;
      if (dots)
        if (tags_position==0)
          canvas->Draw(x-bitmap->Width/2,y-bitmap->Height-size,bitmap);
          else
            canvas->Draw(x-bitmap->Width/2,y+size,bitmap);
        else
          canvas->Draw(x-bitmap->Width/2,y-bitmap->Height/2,bitmap);
      delete bitmap;
    }
    else
      {
      TColor fcol=canvas->Font->Color;
      int fsize=canvas->Font->Size;
      AnsiString fname=canvas->Font->Name;
      canvas->Font->Color=color;
      canvas->Font->Size=fsize+(size-3);
      canvas->Font->Name="Times New Roman";
      if (dots)
        if (tags_position==0)
          canvas->TextOut(x-canvas->TextExtent(label).cx/2,y-canvas->TextExtent(label).cy-size,label);
          else
            canvas->TextOut(x-canvas->TextExtent(label).cx/2,y+size,label);
        else
        canvas->TextOut(x-canvas->TextExtent(label).cx/2,y-canvas->TextExtent(label).cy/2,label);
      canvas->Font->Color=fcol;
      canvas->Font->Size=fsize;
      canvas->Font->Name=fname;
      }
  }

void Graph::DrawTag(double x, double y, AnsiString label, int size, TColor color)
  {
  if ((x >= x_min) && (x <= x_max) && (y >= y_min) && (y <= y_max))
    DrawTxt(border_offset+(x-x_min)*x_scale,border_offset+(y_max-y)*y_scale,label,size,color);
  }

void Graph::ClearGraph()
  {
  TColor color=clWhite;

  TColor pcol=canvas->Pen->Color;
  TColor bcol=canvas->Brush->Color;
  TBrushStyle styl=canvas->Brush->Style;

  canvas->Pen->Color=color;
  canvas->Brush->Color=color;
  canvas->Brush->Style=bsSolid;
  canvas->Rectangle(0,0,image->Width,image->Height);

  canvas->Pen->Color=pcol;
  canvas->Brush->Color=bcol;
  canvas->Brush->Style=styl;
  }

void Graph::AddPoint(double x, double y, int r, TColor color)
  {
  double vec[4]={x,y,r,color};
  matrix->AddRow(vec,4);
  //DrawPoint(x,y,r,color);
  }

void Graph::ErasePoints()
  {
  matrix->Resize(0,0);
  if (vec!=0) delete [] vec;
  vec=0;
  }

void Graph::DrawGraph()
 {
 unsigned int n=matrix->nrows();
 for (unsigned int i=0; i < n; i++)
   //DrawPoint((*matrix)[i][0],(*matrix)[i][1],(*matrix)[i][2],TColor((*matrix)[i][3]));
   if (((*matrix)[i][0] >= x_min) && ((*matrix)[i][0] <= x_max) && ((*matrix)[i][1] >= y_min) && ((*matrix)[i][1] <= y_max))
     {
     if (dots==true)
       {
       DrawCircle(border_offset+((*matrix)[i][0]-x_min)*x_scale,border_offset+(y_max-(*matrix)[i][1])*y_scale,(*matrix)[i][2],TColor((*matrix)[i][3]));
       }
     if (tags==true)
       {
       DrawTag((*matrix)[i][0],(*matrix)[i][1],vec[i],(*matrix)[i][2],TColor((*matrix)[i][3]));
       }
     }
 }

void Graph::Plot()
  {
  ClearGraph();
  DrawGraph();
  DrawAxes();

  //image->Update();
  //image->Repaint();
  //image->Invalidate();
  //image->Refresh();
  }

void Graph::Pan(double x, double y)
  {
  x_min-=x/x_scale;
  x_max-=x/x_scale;
  y_min+=y/y_scale;
  y_max+=y/y_scale;
  Plot();
  }

void Graph::Zoom(int left, int right, int top, int bottom)
  {
  double xmin,xmax,ymin,ymax;

  xmin=x_min+(left-border_offset)/x_scale;
  xmax=x_min+(right-border_offset)/x_scale;
  ymin=y_max-(bottom-border_offset)/y_scale;
  ymax=y_max-(top-border_offset)/y_scale;
  SetRange(xmin,xmax,ymin,ymax);

  x_graduation=ceil(x_max-x_min)/8.0;
  y_graduation=ceil(y_max-y_min)/8.0;

  Plot();
  }

void Graph::ZoomIn(int x, int y)
  {
  double x_range=x_max-x_min;
  double y_range=y_max-y_min;
  double x_position=x_min+(x-border_offset)/x_scale;
  double y_position=y_max-(y-border_offset)/y_scale;
  SetRange(x_position-x_range/3,x_position+x_range/3,y_position-y_range/3,y_position+y_range/3);
  x_graduation=ceil(x_range)/8.0;
  y_graduation=ceil(y_range)/8.0;
  Plot();
  }

void Graph::ZoomOut(int x, int y)
  {
  double x_range=x_max-x_min;
  double y_range=y_max-y_min;
  double x_position=x_min+(x-border_offset)/x_scale;
  double y_position=y_max-(y-border_offset)/y_scale;
  SetRange(x_position-x_range*1.5,x_position+x_range*1.5,y_position-y_range*1.5,y_position+y_range*1.5);
  x_graduation=ceil(x_max-x_min)/8.0;
  y_graduation=ceil(y_max-y_min)/8.0;
  Plot();
  }

void Graph::FromMatrix(Matrix & m, AnsiString * v)
  {
  if (m.ncols()==4)
    {
    delete [] vec;
    vec=0;
    AddVec(v,m.nrows());
    
    (*matrix)=m;
    }
    else
      ErasePoints();
  }

void Graph::FromMatrix(Matrix & m, int r, TColor color, AnsiString * v)
  {
  if (m.ncols()>=2)
    {
    delete [] vec;
    vec=0;
    AddVec(v,m.nrows());

    matrix->Resize(m.nrows(),4);
    for (unsigned int i=0; i < m.nrows(); i++)
      {
      (*matrix)[i][0]=m[i][0];
      (*matrix)[i][1]=m[i][1];
      (*matrix)[i][2]=r;
      (*matrix)[i][3]=color;
      }
    }
    else
      ErasePoints();
  }

void Graph::AddMatrix(Matrix & m, AnsiString * v)
  {
  if (m.ncols()==4)
    {
    AddVec(v,m.nrows());
    matrix->AddRows(m);
    }
  }

void Graph::AddMatrix(Matrix & m, int r, TColor color, AnsiString * v)
  {
  if (m.ncols()>=2)
    {
    Matrix *tmp=new Matrix(m.nrows(),4);
    for (unsigned int i=0; i < m.nrows(); i++)
      {
      (*tmp)[i][0]=m[i][0];
      (*tmp)[i][1]=m[i][1];
      (*tmp)[i][2]=r;
      (*tmp)[i][3]=color;
      }
    AddMatrix(*tmp,v);
    delete tmp;
    }
  }

void Graph::AddVec(AnsiString * v, unsigned int n)
  {
  if (vec!=0)
    {
    AnsiString *tmp=new AnsiString[n+matrix->nrows()];
    for (unsigned int i=0; i < matrix->nrows(); i++)
      {
      tmp[i]=vec[i];
      }
    delete [] vec;
    vec=tmp;
    tmp=0;
    for (unsigned int i=0; i < n; i++)
      {
      vec[i+matrix->nrows()]=v[i];
      }
    }
    else
      {
      vec=new AnsiString[n];
      for (unsigned int i=0; i < n; i++)
        vec[i]=v[i];
      }
  }

void Graph::ToFile(AnsiString filename)
  {
  Graphics::TBitmap *img=new Graphics::TBitmap;
  img->Width=1920;
  img->Height=1200;
  Graph wyk(img);
  wyk.FromMatrix(*matrix,vec);
  wyk.SetRange(x_min,x_max,y_min,y_max);
  wyk.x_graduation=x_graduation;
  wyk.y_graduation=y_graduation;
  wyk.x_tick_position=x_tick_position;
  wyk.y_tick_position=y_tick_position;
  wyk.GraphTitle=GraphTitle;
  wyk.XAxis=XAxis;
  wyk.YAxis=YAxis;
  wyk.tags=tags;
  wyk.dots=dots;
  wyk.tags_position=tags_position;
  wyk.transparent_tags=transparent_tags;
  wyk.Plot();
  //wyk.image->Picture->SaveToFile(filename+".bmp");

  TJPEGImage *jpg=new TJPEGImage();
  jpg->Assign(img);
  jpg->SaveToFile(filename);
  delete jpg;

  delete img;
  }


