#include "winplus.h"
#pragma hdrstop
#include <math.h>

// =======================================================================
// Код защищенных методов
// =======================================================================

void CGraphicsSamples::ReleaseArrays()
{
  if(lpVertices)
  {
    /*MemOverride::*/free(lpVertices);
    lpVertices=NULL;
  }
  if(lpRGBTriples)
  {
    /*MemOverride::*/free(lpRGBTriples);
    lpRGBTriples=NULL;
  }
  iVertexCount=iMaxVertex=0;
  fuDefinedArrays=GS_NONE;

  bMinMaxValid=FALSE;
}

// =======================================================================

void CGraphicsSamples::AllocateArrays()
{
  int SizeV, SizeC;
  SizeV=sizeof(TGraphVertex2d)*iMaxVertex;
  SizeC=sizeof(DWORD)*iMaxVertex;
  bLastError=TRUE;
  lpVertices=/*MemOverride::*/(TGraphVertex2d*)malloc(SizeV);
  lpRGBTriples=/*MemOverride::*/(DWORD*)malloc(SizeC);
  if((lpRGBTriples==NULL)||(lpVertices==NULL))
  {
    ReleaseArrays();
    return;
  }
  memset(lpVertices,0,SizeV);
  memset(lpRGBTriples,0,SizeC);
  bLastError=FALSE;
}

// =======================================================================

void CGraphicsSamples::ResetMinMax()
{
  int i;
  bLastError=TRUE;
  if(((fuDefinedArrays&GS_COORD_ARRAY)==0)||(iVertexCount<1)) return;
  MinX=lpVertices[0].x;
  MinY=lpVertices[0].y;
  MaxX=lpVertices[0].x;
  MaxY=lpVertices[0].y;
  for(i=1;i<iVertexCount;i++)
  {
    if(MinX>lpVertices[i].x) MinX=lpVertices[i].x;
    else
      if(MaxX<lpVertices[i].x) MaxX=lpVertices[i].x;
    if(MinY>lpVertices[i].y) MinY=lpVertices[i].y;
    else
      if(MaxY<lpVertices[i].y) MaxY=lpVertices[i].y;
  }
  bLastError=FALSE;
  bMinMaxValid=TRUE;
}

// =======================================================================
// Конструктор и деструктор
// =======================================================================

CGraphicsSamples::CGraphicsSamples(CDCObject *lpCDC)
{
  iMaxVertex=iVertexCount=0;
  iSamplesType=-1;
  lpVertices=NULL;
  lpRGBTriples=NULL;
  bMinMaxValid=FALSE;
  bUseCustomMinMaxX=FALSE;
  bUseCustomMinMaxY=FALSE;
  fuDefinedArrays=GS_NONE;
  bLastError=TRUE;
  this->lpCDC=lpCDC;
  lpCDC->GetClientArea(&rcOut);
  lpInitialPen=new CPen;
  lpInitialPen->SetColor(0);
  lpInitialBrush=new CBrush;
  lpInitialBrush->SetColor(RGB(127,127,127));
  bLastError=FALSE;
}

CGraphicsSamples::~CGraphicsSamples()
{
  if(lpInitialBrush) delete lpInitialBrush;
  if(lpInitialPen) delete lpInitialPen;
  ReleaseArrays();
}

// =======================================================================
// Команды рисования
// =======================================================================

void CGraphicsSamples::BeginPath(int iMaxVertex)
{
  vZeroPoint.x=vZeroPoint.y=0;
  vZoom.x=vZoom.y=1;
  dAngle=0;
  bLastError=TRUE;
  ReleaseArrays();
  this->iMaxVertex=iMaxVertex;
  AllocateArrays();
  if(bLastError) return;
  iVertexCount=0;
  bLastError=FALSE;
}

void CGraphicsSamples::Vertex2d(double x, double y)
{
  if(iVertexCount>=iMaxVertex) return;
  lpVertices[iVertexCount].x = x;
  lpVertices[iVertexCount].y = y;
  lpRGBTriples[iVertexCount]=lpInitialPen->GetLogPen()->lopnColor;
  iVertexCount++;
}

void CGraphicsSamples::Vertex2i(int x, int y)
{
  if(iVertexCount>=iMaxVertex) return;
  lpVertices[iVertexCount].x = x;
  lpVertices[iVertexCount].y = y;
  lpRGBTriples[iVertexCount]=lpInitialPen->GetLogPen()->lopnColor;
  iVertexCount++;
}

void CGraphicsSamples::Color3d(double r, double g, double b)
{
  lpInitialPen->SetColor(RGB((DWORD)r,(DWORD)g,(DWORD)b));
}

void CGraphicsSamples::Color3i(int r, int g, int b)
{
  lpInitialPen->SetColor(RGB((DWORD)r,(DWORD)g,(DWORD)b));
}

void CGraphicsSamples::EndPath()
{
  // Если iMaxVertex == 0 то мы вне операторных скобок
  bLastError=TRUE;
  if(iMaxVertex>0)
  {
    if(iVertexCount<=0)
    {
      ReleaseArrays();
      return;
    }
    fuDefinedArrays=GS_BOTH;
    bLastError=FALSE;
    ResetMinMax();
    return;
  }
}

void CGraphicsSamples::VertexPointer2d(int iStride,int iCount,void *aVertices)
{
  int i;
  BYTE *ptr;
  bLastError=TRUE;
  vZeroPoint.x=vZeroPoint.y=0;
  vZoom.x=vZoom.y=1;
  dAngle=0;
  if((iStride<0)||(iCount<=0)||(aVertices==NULL)) return;
  // Подобная установка делает невозможным вызовы VertexXX и ColorXX
  iMaxVertex=0;
  if(fuDefinedArrays&GS_COORD_ARRAY)
  {
    fuDefinedArrays&=~GS_COORD_ARRAY;
    if(lpVertices) /*MemOverride::*/free(lpVertices);
    if(fuDefinedArrays&GS_COLOR_ARRAY)
    {
      // Не будем копировать больше вершин, чем уже есть цветов
      if(iCount>iVertexCount) iCount=iVertexCount;
    }
  }
  else
  {
    if(fuDefinedArrays&GS_COLOR_ARRAY)
    {
      if(iCount>iVertexCount) iCount=iVertexCount;
    }
  }
  lpVertices=(TGraphVertex2d*)/*MemOverride::*/malloc(iCount*
    sizeof(TGraphVertex2d));
  if(lpVertices==NULL) return;
  iVertexCount=iCount;
  ptr=(BYTE*)aVertices;
  for(i=0;i<iCount;i++)
  {
    lpVertices[i].x=*((double*)ptr);
    lpVertices[i].y=*((double*)ptr+1);
    ptr+=2*sizeof(double)+iStride;
  }
  fuDefinedArrays|=GS_COORD_ARRAY;
  ResetMinMax();
  bLastError=FALSE;
}

void CGraphicsSamples::VertexPointer2i(int iStride,int iCount,void *aVertices)
{
  int i;
  BYTE *ptr;
  bLastError=TRUE;
  vZeroPoint.x=vZeroPoint.y=0;
  vZoom.x=vZoom.y=1;
  dAngle=0;
  if((iStride<0)||(iCount<=0)||(aVertices==NULL)) return;
  // Подобная установка делает невозможным вызовы VertexXX и ColorXX
  iMaxVertex=0;
  if(fuDefinedArrays&GS_COORD_ARRAY)
  {
    fuDefinedArrays&=~GS_COORD_ARRAY;
    if(lpVertices) /*MemOverride::*/free(lpVertices);
    if(fuDefinedArrays&GS_COLOR_ARRAY)
    {
      // Не будем копировать больше вершин, чем уже есть цветов
      if(iCount>iVertexCount) iCount=iVertexCount;
    }
  }
  else
  {
    if(fuDefinedArrays&GS_COLOR_ARRAY)
    {
      if(iCount>iVertexCount) iCount=iVertexCount;
    }
  }
  lpVertices=(TGraphVertex2d*)/*MemOverride::*/malloc(iCount*
    sizeof(TGraphVertex2d));
  if(lpVertices==NULL) return;
  iVertexCount=iCount;
  ptr=(BYTE*)aVertices;
  for(i=0;i<iCount;i++)
  {
    lpVertices[i].x=*((int*)ptr);
    lpVertices[i].y=*((int*)ptr+1);
    ptr+=2*sizeof(int)+iStride;
  }
  fuDefinedArrays|=GS_COORD_ARRAY;
  ResetMinMax();
  bLastError=FALSE;
}

void CGraphicsSamples::ColorPointer1i(int iStride,int iCount,void *aRGBTriples)
{
  int i;
  BYTE *ptr;
  bLastError=0;
  if((iStride<0)||(iCount<=0)) return;
  // Подобная установка делает невозможным вызовы VertexXX и ColorXX
  iMaxVertex=0;
  if(fuDefinedArrays&GS_COLOR_ARRAY)
  {
    fuDefinedArrays&=~(DWORD)GS_COLOR_ARRAY;
    if(lpRGBTriples) /*MemOverride::*/free(lpRGBTriples);
    if(lpRGBTriples==NULL) return;
    if(fuDefinedArrays&GS_COORD_ARRAY)
    {
      // Не будем копировать больше цветов, чем уже есть вершин
      if(iCount>iVertexCount) iCount=iVertexCount;
    }
  }
  iVertexCount=iCount;
  lpRGBTriples=/*MemOverride::*/(DWORD*)malloc(iCount*sizeof(DWORD));
  if(aRGBTriples)
  {
    ptr=(BYTE*)aRGBTriples;
    for(i=0;i<iVertexCount;i++)
    {
      lpRGBTriples[i]=*((DWORD*)ptr);
      ptr+=sizeof(DWORD)+iStride;
    }
  }
  else
  {
    DWORD RGB = lpInitialPen->GetLogPen()->lopnColor;
    for(i=0;i<iVertexCount;i++) lpRGBTriples[i]=RGB;
  }
  fuDefinedArrays|=GS_COLOR_ARRAY;
}

void CGraphicsSamples::DrawArrays(int iDrawMode)
{
  int i;
  COLORREF DefaultColor = lpInitialPen->GetLogPen()->lopnColor;
  if((fuDefinedArrays&GS_COORD_ARRAY)==0) return;
  if((fuDefinedArrays&GS_COLOR_ARRAY)==0)
  {
    ColorPointer1i(0,iVertexCount,NULL);
  }
  switch(iDrawMode)
  {
    case GS_POINTS:
      // Рисуем точки
      if(iVertexCount==0) return;
      for(i=0;i<iVertexCount;i++)
      {
        lpCDC->SetPixel(lpVertices[i].x,lpVertices[i].y,lpRGBTriples[i]);
      }
      break;
    case GS_LINES:
      // Рисуем линии
      // Четные значения цвета игнорируются
      if(iVertexCount<2) return;
      lpInitialPen->SetColor(lpRGBTriples[0]);
      lpCDC->CreatePen(lpInitialPen);
      lpCDC->Line(lpVertices[0].x,lpVertices[0].y,lpVertices[1].x,
          lpVertices[1].y);
      for(i=2;i<(iVertexCount-1);i+=2)
      {
        // Новое перо создаем только если цвет сменился
        if(lpRGBTriples[i]!=lpRGBTriples[i-2])
        {
          lpInitialPen->SetColor(lpRGBTriples[i]);
          lpCDC->CreatePen(lpInitialPen);
        }
        lpCDC->Line(lpVertices[i].x,lpVertices[i].y,lpVertices[i+1].x,
          lpVertices[i+1].y);
      }
      break;
    case GS_VLINES:
      // Рисуем вертикальные линии от нулевой точки
      if(iVertexCount==0) return;
      lpInitialPen->SetColor(lpRGBTriples[0]);
      lpCDC->CreatePen(lpInitialPen);
      lpCDC->Line(lpVertices[0].x,vZeroPoint.y,lpVertices[0].x,
        lpVertices[0].y);
      for(i=1;i<iVertexCount;i++)
      {
        // Новое перо создаем только если цвет сменился
        if(lpRGBTriples[i]!=lpRGBTriples[i-1])
        {
          // Цвет i-й линии опеределяется элементом #i
          lpInitialPen->SetColor(lpRGBTriples[i]);
          lpCDC->CreatePen(lpInitialPen);
        }
        lpCDC->Line(lpVertices[i].x,vZeroPoint.y,lpVertices[i].x,
          lpVertices[i].y);
      }
      break;
    case GS_LINESTRIP:
      // Всего отрисовываем iVertexCount - 1 линий
      // Последнее значение цвета игнорируется
      if(iVertexCount<2) return;
      lpInitialPen->SetColor(lpRGBTriples[0]);
      lpCDC->CreatePen(lpInitialPen);
      lpCDC->MoveTo(lpVertices[0].x,lpVertices[0].y);
      lpCDC->LineTo(lpVertices[1].x,lpVertices[1].y);
      for(i=2;i<iVertexCount;i++)
      {
        // Новое перо создаем только если цвет сменился
        if(lpRGBTriples[i-1]!=lpRGBTriples[i-2])
        {
          // Цвет i-й линии опеределяется элементом # i-1
          lpInitialPen->SetColor(lpRGBTriples[i-1]);
          lpCDC->CreatePen(lpInitialPen);
        }
        lpCDC->LineTo(lpVertices[i].x,lpVertices[i].y);
      }
      break;
    case GS_BARS:
      // Всего отрисовываем iVertexCount / 2 столбиков
      // Для отрисовки контура используются четные значения цвета,
      // для закраски столбиков - нечетные
      // Высота столбика определяется ординатой первой вершины
      if(iVertexCount<2) return;
      lpInitialPen->SetColor(lpRGBTriples[0]);
      lpInitialBrush->SetColor(lpRGBTriples[1]);
      lpCDC->CreatePen(lpInitialPen);
      lpCDC->CreateBrush(lpInitialBrush);
      lpCDC->Rectangle(lpVertices[0].x,lpVertices[0].y,
        lpVertices[1].x,vZeroPoint.y);
      for(i=2;i<(iVertexCount-1);i+=2)
      {
        if(lpRGBTriples[i]!=lpRGBTriples[i-2])
        {
          lpInitialPen->SetColor(lpRGBTriples[i]);
          lpCDC->CreatePen(lpInitialPen);
        }
        if(lpRGBTriples[i+1]!=lpRGBTriples[i-1])
        {
          lpInitialBrush->SetColor(lpRGBTriples[i+1]);
          lpCDC->CreateBrush(lpInitialBrush);
        }
        lpCDC->Rectangle(lpVertices[i].x,lpVertices[i].y,
          lpVertices[i+1].x,vZeroPoint.y);
      }
      break;
    case GS_BARSTRIP:
      // Всего отрисовываем iVertexCount - 1 столбиков
      // Для отрисовки контура используется текущее перо, а массив
      // цветов используется для закраски столбиков
      // Ордината последней вершины игнорируется
      if(iVertexCount<2) return;
      lpCDC->CreatePen(lpInitialPen);
      lpInitialBrush->SetColor(lpRGBTriples[0]);
      lpCDC->CreateBrush(lpInitialBrush);
      lpCDC->Rectangle(lpVertices[0].x,lpVertices[0].y,
        lpVertices[1].x,vZeroPoint.y);
      for(i=1;i<(iVertexCount-1);i++)
      {
        if(lpRGBTriples[i-1]!=lpRGBTriples[i])
        {
          lpInitialBrush->SetColor(lpRGBTriples[i]);
          lpCDC->CreateBrush(lpInitialBrush);
        }
        lpCDC->Rectangle(lpVertices[i].x,lpVertices[i].y,
          lpVertices[i+1].x,vZeroPoint.y);
      }
      break;
  } // end switch( iDrawMode )
} // end DrawArrays

// =======================================================================
// Координаты и преобразования
// =======================================================================

void CGraphicsSamples::GetMinMaxX(double *MinX, double *MaxX)
{
  bLastError=TRUE;
  if((MinX==NULL)&&(MaxX==NULL)) return;
  if(bUseCustomMinMaxX)
  {
    if(MinX) *MinX=this->CustomMinX;
    if(MaxX) *MaxX=this->CustomMaxX;
    bLastError=FALSE;
    return;
  }
  if(bMinMaxValid==FALSE) ResetMinMax();
  if(bMinMaxValid==FALSE)
  {
    if(MinX) *MinX=0;
    if(MaxX) *MaxX=0;
    return;
  }
  if(MinX) *MinX=this->MinX;
  if(MaxX) *MaxX=this->MaxX;

  bLastError=FALSE;
}

// =======================================================================

void CGraphicsSamples::GetMinMaxY(double *MinY, double *MaxY)
{
  bLastError=TRUE;
  if((MinY==NULL)&&(MaxY==NULL)) return;
  if(bUseCustomMinMaxY)
  {
    if(MinY) *MinY=this->CustomMinY;
    if(MaxY) *MaxY=this->CustomMaxY;
    bLastError=FALSE;
    return;
  }
  if(bMinMaxValid==FALSE) ResetMinMax();
  if(bMinMaxValid==FALSE)
  {
    if(MinY) *MinY=0;
    if(MaxY) *MaxY=0;
    return;
  }
  if(MinY) *MinY=this->MinY;
  if(MaxY) *MaxY=this->MaxY;

  bLastError=FALSE;
}

// =======================================================================

void CGraphicsSamples::UseCustomMinMaxX(double MinX, double MaxX,
  BOOL bUse)
{
  bUseCustomMinMaxX=bUse;
  if(bUse)
  {
    this->CustomMinX=MinX*vZoom.x+vZeroPoint.x;
    this->CustomMaxX=MaxX*vZoom.x+vZeroPoint.x;
  }
  else
    ResetMinMax();
}

// =======================================================================

void CGraphicsSamples::UseCustomMinMaxY(double MinY, double MaxY,
  BOOL bUse)
{
  bUseCustomMinMaxY=bUse;
  if(bUse)
  {
    this->CustomMaxY=MinY*vZoom.y+vZeroPoint.y;
    this->CustomMinY=MaxY*vZoom.y+vZeroPoint.y;
  }
  else
    ResetMinMax();
}

// =======================================================================

void CGraphicsSamples::Scale(double dFactorX, double dFactorY)
{
  bLastError=TRUE;
  int i;
  if(((fuDefinedArrays&GS_COORD_ARRAY)==0)||
    (dFactorX==0)||(dFactorY==0)||(iVertexCount<1)) return;
  for(i=0;i<iVertexCount;i++)
  {
    lpVertices[i].x*=dFactorX;
    lpVertices[i].y*=dFactorY;
  }
  vZoom.x*=dFactorX;
  vZoom.y*=dFactorY;
  vZeroPoint.x*=dFactorX;
  vZeroPoint.y*=dFactorY;
  if(bUseCustomMinMaxX)
  {
    CustomMinX*=dFactorX;
    CustomMaxX*=dFactorX;
  }
  if(bUseCustomMinMaxY)
  {
    CustomMinY*=dFactorY;
    CustomMaxY*=dFactorY;
  }
  bLastError=FALSE;
  bMinMaxValid=FALSE;
}

// =======================================================================

void CGraphicsSamples::Translate(double dTranslateX, double dTranslateY)
{
  bLastError=TRUE;
  int i;
  if(((fuDefinedArrays&GS_COORD_ARRAY)==0)||(iVertexCount<1)) return;
  for(i=0;i<iVertexCount;i++)
  {
    lpVertices[i].x+=dTranslateX;
    lpVertices[i].y+=dTranslateY;
  }
  vZeroPoint.x+=dTranslateX;
  vZeroPoint.y+=dTranslateY;
  if(bUseCustomMinMaxX)
  {
    CustomMinX+=dTranslateX;
    CustomMaxX+=dTranslateX;
  }
  if(bUseCustomMinMaxY)
  {
    CustomMinY+=dTranslateY;
    CustomMaxY+=dTranslateY;
  }
  bLastError=FALSE;
  bMinMaxValid=FALSE;
}

// =======================================================================

void CGraphicsSamples::Rotate(double angle, double CX, double CY)
{
  bLastError=TRUE;
  angle=fmod(angle,2*M_PI);
  int i;
  if(((fuDefinedArrays&GS_COORD_ARRAY)==0)||(iVertexCount<1)) return;
  // Перевод градусов в радианы
  angle *= RADinDEG;
  double s = sin(angle), c = cos(angle);
  double x,x1,y,y1;
  for(i=0;i<iVertexCount;i++)
  {
    //  Паpаллельный пеpенос
    x=lpVertices[i].x-CX;
    y=lpVertices[i].y-CY;
    //  Повоpот
    x1=x*c-y*s;
    y1=y*c+x*s;
    //  Паpаллельный пеpенос
    lpVertices[i].x=x1+CX;
    lpVertices[i].y=y1+CY;
  }
  // Поворот нулевой точки
  x=vZeroPoint.x-CX;
  y=vZeroPoint.y-CY;
  x1=x*c-y*s;
  y1=y*c+x*s;
  vZeroPoint.x=x1+CX;
  vZeroPoint.y=y1+CY;
  // Накопление результатов поворота
  dAngle=fmod(dAngle+angle,2*M_PI);
  bLastError=FALSE;
  bMinMaxValid=FALSE;
}

// =======================================================================

void CGraphicsSamples::SetOutputRect(RECT *lprcOut)
{
  bLastError=TRUE;
  if((lprcOut==NULL)||(::IsRectEmpty(lprcOut))) return;
  rcOut.left=lprcOut->left;
  rcOut.top=lprcOut->top;
  rcOut.right=lprcOut->right;
  rcOut.bottom=lprcOut->bottom;
  bLastError=FALSE;
}

// =======================================================================

void CGraphicsSamples::GetOutputRect(RECT *lprcOut)
{
  bLastError=TRUE;
  if(lprcOut==NULL) return;
  lprcOut->left=rcOut.left;
  lprcOut->top=rcOut.top;
  lprcOut->right=rcOut.right;
  lprcOut->bottom=rcOut.bottom;
  bLastError=FALSE;
}

// =======================================================================
// Подгон под размер окна
void CGraphicsSamples::ScaleFromArrays(double dReqSizeX, double dReqSizeY,
  DWORD fuScaleFlags)
{
  double ScaleX, ScaleY;
  double minx,maxx,miny,maxy;
  GetMinMaxX(&minx,&maxx);
  GetMinMaxY(&miny,&maxy);
  if((dReqSizeX==0)||(dReqSizeY==0))
  {
    bLastError=TRUE;
    return;
  }
  if(((fuScaleFlags&GS_IGNOREZERO)==0)&&(bUseCustomMinMaxX==FALSE))
  {
    if(maxx<vZeroPoint.x) maxx=vZeroPoint.x;
    else
      if(minx>vZeroPoint.x) minx=vZeroPoint.x;
  }
  if(((fuScaleFlags&GS_IGNOREZERO)==0)&&(bUseCustomMinMaxY==FALSE))
  {
    if(maxy<vZeroPoint.y) maxy=vZeroPoint.y;
    else
      if(miny>vZeroPoint.y) miny=vZeroPoint.y;
  }
  ScaleX = dReqSizeX/(maxx-minx);
  ScaleY = dReqSizeY/(maxy-miny);
  if(fuScaleFlags&GS_PROPORTIONAL)
  {
    // Общий коэффициента масштабирования - меньший из двух
    if(ScaleX<ScaleY) ScaleY=ScaleX; else ScaleX=ScaleY;
  }
  Scale(ScaleX,ScaleY);
}

void CGraphicsSamples::TranslateFromArrays(double dReqStartX,
                                           double dReqStartY,
                                           DWORD fuFlags)
{
  double dX, dY;
  double minx,miny;
  GetMinMaxX(&minx,NULL);
  GetMinMaxY(&miny,NULL);
  if(((fuFlags&GS_IGNOREZERO)==0)&&(bUseCustomMinMaxX==FALSE))
  {
    if(minx>vZeroPoint.x) minx=vZeroPoint.x;
  }
  if(((fuFlags&GS_IGNOREZERO)==0)&&(bUseCustomMinMaxY==FALSE))
  {
    if(miny>vZeroPoint.y) miny=vZeroPoint.y;
  }
  dX = dReqStartX-minx;
  dY = dReqStartY-miny;
  Translate(dX,dY);
}

