#include <string.h>
#include <math.h>

#include "buffer.hh"
#include "global.hh"
#include "font8x8.hh"

#define EPSILON 0.01

// Transparency dither table
DOUBLE T8[8][8] =
{
  {0.000000, 0.507937, 0.126984, 0.634921, 0.031746, 0.079365, 0.158730, 0.666667},
  {0.761905, 0.253968, 0.888889, 0.380952, 0.793651, 0.285714, 0.920635, 0.412698},
  {0.190476, 0.698413, 0.063492, 0.571429, 0.222222, 0.730159, 0.095238, 0.603175},
  {0.952381, 0.444444, 0.825397, 0.317460, 0.984127, 0.476190, 0.857143, 0.349206},
  {0.047619, 0.555556, 0.174603, 0.682540, 0.015873, 0.523810, 0.142857, 0.650794},
  {0.809524, 0.301587, 0.936508, 0.428571, 0.777778, 0.269841, 0.904762, 0.396825},
  {0.238095, 0.746032, 0.111111, 0.619048, 0.206349, 0.714286, 0.079365, 0.587302},
  {1.000000, 0.492063, 0.873016, 0.365079, 0.968254, 0.460317, 0.841270, 0.333333}
};


// initialisation des mebres statiques

ULONG VECBuffer::ZBufSize = 0;
DOUBLE *VECBuffer::ZBuffer = NULL;

ULONG *VECBuffer::ZFastIdx = new ULONG[RESOLUTION_MAX_Y];

// bornes pour les scan lines

VECBorder *VECBuffer::G_Start = new VECBorder[RESOLUTION_MAX_Y];
VECBorder *VECBuffer::G_End = new VECBorder[RESOLUTION_MAX_Y];

UINT VECBuffer::G_YMin = 0;   // 0 pour la premiere initialisation
                              // vaudra : RESOLUTION_MAX_Y ensuite

UINT VECBuffer::G_YMax = RESOLUTION_MAX_Y; // reciproquement

/*******************************************************
*   Constructor : VECBuffer(initHeight, initWidth)     *
*                                                      *
*   UINT initHeight : Height of the buffer in pixels   *
*   UINT initWidth  : Width of the buffer in pixels    *
*------------------------------------------------------*
*                                                      *
* Construit un buffer de initHeight x initWidth pixels *
*                                                      *
* Augmente la taille du ZBuffer si necessaire          *
*                                                      *
*******************************************************/
VECBuffer::VECBuffer(UINT initHeight, UINT initWidth)
 : Height(initHeight), 
   Width(initWidth), 
   Depht(VEC_8BITS), 
   CMap(GLOB.CMap),
   ClipXMin(0), 
   ClipXMax(initWidth-1), 
   ClipYMin(0), 
   ClipYMax(initHeight-1),
   HalfWidth(initWidth>>1),
   HalfHeight(initHeight>>1)
{

  ULONG Size = Height*Width;

  Data = NULL;
  FastIdx = NULL;

  Data = new UBYTE[Size*Depht];

  ColorMode = VEC_DITHER;
  GetColor = VECColorMap::GetDitherColor;

  if (Data)
  {
    FastIdx = new ULONG[Height];

    if (FastIdx)
    {
      ULONG * Tmp = FastIdx;
      ULONG Sum = 0;
      ULONG Inc = Width * Depht;

      for(INT h=0; h<Height; h++)
      {
        *(Tmp++) = Sum;
        Sum += Inc;
      }

      if(ZBufSize < Size)
      {
        if(ZBuffer)
	{
          delete ZBuffer;
	} 

        ZBuffer = new DOUBLE[Size];

        ZBufSize = Size;

        if(!ZBuffer);
        {
           // PEY : erreur impossible d'alouer le Z_Buffer
        }
      }

    } else {
      // PEY : remplacer cout par stderr ou equivalent
      //cout << MESSAGE[0];
      delete Data; Data = NULL;
    }
  } else {
    // PEY : gestion d'erreur
    //cout << MESSAGE[0];   
  }
}

/*******************************************************
*   Destructor : ~VECBuffer()                          *
*------------------------------------------------------*
*                                                      *
*******************************************************/
VECBuffer::~VECBuffer()
{
  if (Data)
  {
    delete Data; Data = NULL;
  }
  if (FastIdx)
  {
    delete FastIdx; FastIdx = NULL;
  }
}

/*******************************************************
*   Function : VECBuffer::SetBackground(BGCol)         *
*                                                      *
*   VECColor BGCol : new background color              *
*------------------------------------------------------*
*   Set BGCol as current Background color              *
*******************************************************/
VOID VECBuffer::SetBackground(VECColor BGCol)
{
  BGColor = BGCol;
}

/*******************************************************
*   Function : VECBuffer::SetForeground(BGCol)         *
*                                                      *
*   VECColor BGCol : new foreground color              *
*------------------------------------------------------*
*   Set BGCol as current Foreground color              *
*******************************************************/
VOID VECBuffer::SetForeground(VECColor FGCol)
{
  FGColor = FGCol;
}

/*******************************************************
*   Function : VECBuffer::GetBackground()              *
*------------------------------------------------------*
*   Return the current Background color                *
*******************************************************/
VECColor VECBuffer::GetBackground()
{
  return BGColor;
}

/*******************************************************
*   Function : VECBuffer::GetForeground()              *
*------------------------------------------------------*
*   Return the current foreground color                *
*******************************************************/
VECColor VECBuffer::GetForeground()
{
  return FGColor;
}

/*******************************************************
*   Function : VECBuffer::SetClippingBox(p1, p2)       *
*                                                      *
*  VECPixel P1, P2 : opposites corners of the          *
*                    clipping box                      *
*------------------------------------------------------*
*  Use clipping box defined by (P1, P2) and limited    *
*  by the buffer size to set current clipping box      *
*******************************************************/
VOID VECBuffer::SetClippingBox(VECPixel P1, VECPixel P2)
{
  if (P1.X < P2.X)
  {
    ClipXMin = (P1.X < 0)?0:((P1.X<Width)?P1.X:Width); 
    ClipXMax = (P2.X < Width)?((P2.X<0)?0:P2.X):Width;
  } else {
    ClipXMin = (P2.X < 0 )?0:((P2.X<Width)?P1.X:Width); 
    ClipXMax = (P1.X <Width)?((P1.X<0)?0:P1.X):Width;
  }
  if (P1.Y < P2.Y)
  {
    ClipYMin = (P1.Y < 0)?0:((P1.Y < Width)?P1.Y:Width); 
    ClipYMax = (P2.Y < Width)?((P2.Y < 0)?0:P2.Y):Width;
  } else {
    ClipYMin = (P2.Y < 0)?0:((P2.Y < Width)?P1.Y:Width); 
    ClipYMax = (P1.Y < Width)?((P1.Y < 0)?0:P1.Y):Width;
  }
}

/*******************************************************
*   Idem with clipping box defined by the opposites    *
*   corners (X1,Y1) (X2,Y2)                            *
*******************************************************/
VOID VECBuffer::SetClippingBox(UINT X1, UINT Y1, UINT X2, UINT Y2)
{
  if (X1 < X2)
  {
    ClipXMin = (X1<0)?0:((X1 < Width)?X1:Width); 
    ClipXMax = (X2<Width)?((X2 < 0)?0:X2):Width;
  } else {
    ClipXMin = (X2<0)?0:((X2 < Width)?X2:Width); 
    ClipXMax = (X1<Width)?((X1 < 0)?0:X1):Width;
  }
  if (Y1 < Y2)
  {
    ClipYMin = (Y1 < 0)?0:((Y2 < Width)?Y1:Width); 
    ClipYMax = (Y2 < Width)?((Y2 < 0)?0:Y2):Width;
  } else {
    ClipYMin = (Y2 < 0)?0:((Y2 < Width)?Y2:Width); 
    ClipYMax = (Y1 < Width)?((Y1 < 0)?0:Y1):Width;
  }
}
/*******************************************************
*   Function : VECBuffer::SetColorMode                 *
*------------------------------------------------------*
*                                                      *
*******************************************************/
VOID VECBuffer::SetColorMode(UBYTE Mode)
{
  switch(Mode)
  {
    case VEC_DITHER :  GetColor = VECColorMap::GetDitherColor;
                       ColorMode = Mode; 
                       break;
//    case VEC_GREY :    GetColor = VECColorMap::GetGreyColor; 
//                       ColorMode = Mode;
//                       break;
    case VEC_CLOSEST : GetColor = VECColorMap::GetClosestColor; 
                       ColorMode = Mode;
                       break;
    default : break; // PEY : erreur
  }
}

UBYTE VECBuffer::GetColorMode()
{
  return ColorMode;
}

/*******************************************************
*   Functions : VECBuffer::DrawPoint                   *
*------------------------------------------------------*
*                                                      *
*******************************************************/
VOID VECBuffer::DrawPoint(UINT X, UINT Y)
{
  if((X >= ClipXMin) && (X <= ClipXMax) &&
     (Y >= ClipYMin) && (Y <= ClipYMax))
  {
    Data[FastIdx[Y]+X*Depht] = CMap->GetClosestColor(FGColor, 0, 0);  
  }
}
VOID VECBuffer::DrawPoint(UINT X, UINT Y, VECColor &Col)
{
  if((X >= ClipXMin) && (X <= ClipXMax) &&
     (Y >= ClipYMin) && (Y <= ClipYMax))
  {
    Data[FastIdx[Y]+X*Depht] = CMap->GetClosestColor(Col, 0, 0);  
  }
}

VOID VECBuffer::DrawPoint(VECPixel &P)
{
  if((P.X >= ClipXMin) && (P.X <= ClipXMax) &&
     (P.Y >= ClipYMin) && (P.Y <= ClipYMax))
  {
    Data[FastIdx[P.X]+P.X*Depht] = CMap->GetClosestColor(FGColor, 0, 0);  
  }
}
VOID VECBuffer::DrawPoint(VECPixel &P, VECColor &Col)
{
  if((P.X >= ClipXMin) && (P.X <= ClipXMax) &&
     (P.Y >= ClipYMin) && (P.Y <= ClipYMax))
  {
    Data[FastIdx[P.Y]+P.Y*Depht] = CMap->GetClosestColor(Col, 0, 0);  
  }
}

/*******************************************************
*   Functions : VECBuffer::DrawLine                    *
*------------------------------------------------------*
*  Draw a line using                                   *
*        bresenham's integer based algorithme          *
*******************************************************/
VOID VECBuffer::DrawLine(VECPixel P1, VECPixel P2)
{
  INT dx, dy, long_d, short_d;
  INT d, add_dh, add_dl;
  INT inc_xh, inc_yh, inc_xl, inc_yl;
  register INT inc_ah, inc_al;
  register INT i;
  register UBYTE *adr = Data;

    dx = P2.X - P1.X; 
    dy = P2.Y - P1.Y;

    if(dx < 0)
    {
      dx = -dx;
      inc_xh = -1;
      inc_xl = -1;
    } else {
      inc_xh = 1;
      inc_xl = 1;
    }
    if(dy < 0)
    {
      dy = -dy;
      inc_yh = -Width;
      inc_yl = -Width;
    } else {
      inc_yh = Width;
      inc_yl = Width;
    }
    if(dx > dy)
    {
      long_d = dx;
      short_d = dy; 
      inc_yl = 0;
    } else {
      long_d = dy;
      short_d = dx; 
      inc_xl = 0;
    }

    inc_ah = inc_xh + inc_yh;
    inc_al = inc_xl + inc_yl;
    adr += FastIdx[P1.Y] + P1.X;

    d = (short_d<<1) - long_d;
    add_dl = short_d<<1;
    add_dh = (short_d<<1) - (long_d<<1);

    for(i=0; i<=long_d; i++)
    {
      *adr = CMap->GetClosestColor(FGColor, 0, 0);
      if(d >= 0)
      {
	adr += inc_ah; 
	d += add_dh;
      } else {
	adr += inc_al;
	d += add_dl;
      }
    }
  
}

VOID VECBuffer::DrawLine(VECPixel P1, VECPixel P2, VECColor &Col)
{
  INT dx, dy, long_d, short_d;
  INT d, add_dh, add_dl;
  INT inc_xh, inc_yh, inc_xl, inc_yl;
  register INT inc_ah, inc_al;
  register INT i;
  register UBYTE *adr = Data;


  if (LineClipping2D(P1, P2))
  {

    dx = P2.X - P1.X; 
    dy = P2.Y - P1.Y;

    if(dx < 0)
    {
      dx = -dx;
      inc_xh = -1;
      inc_xl = -1;
    } else {
      inc_xh = 1;
      inc_xl = 1;
    }
    if(dy < 0)
    {
      dy = -dy;
      inc_yh = -Width;
      inc_yl = -Width;
    } else {
      inc_yh = Width;
      inc_yl = Width;
    }
    if(dx > dy)
    {
      long_d = dx;
      short_d = dy; 
      inc_yl = 0;
    } else {
      long_d = dy;
      short_d = dx; 
      inc_xl = 0;
    }

    inc_ah = inc_xh + inc_yh;
    inc_al = inc_xl + inc_yl;
    adr += FastIdx[P1.Y] + P1.X;

    d = (short_d<<1) - long_d;
    add_dl = short_d<<1;
    add_dh = (short_d<<1) - (long_d<<1);

    for(i=0; i<=long_d; i++)
    {
      *adr = CMap->GetClosestColor(Col, 0, 0);
      if(d >= 0)
      {
	adr += inc_ah; 
	d += add_dh;
      } else {
	adr += inc_al;
	d += add_dl;
      }
    }
  }
}

/*******************************************************
*   Functions : VECBuffer::DrawRectangle               *
*------------------------------------------------------*
*                                                      *
*******************************************************/
VOID VECBuffer::DrawRectangle(UINT X1, UINT Y1, UINT X2, UINT Y2)
{
  UINT Tmp;

  if(X1 > X2)
  {
    Tmp = X1; X1 = X2; X2 = Tmp;
  }
  if(Y1 > Y2)
  {
    Tmp = Y1; Y1 = Y2; Y2 = Tmp;
  }
  DrawLine(X1, Y1, X2, Y1);
  DrawLine(X2, Y1, X2, Y2);
  DrawLine(X1, Y2, X2, Y2);
  DrawLine(X1, Y1, X1, Y2); 
}

VOID VECBuffer::DrawRectangle(UINT X1, UINT Y1, UINT X2, UINT Y2, VECColor &Col)
{  
  UINT Tmp;

  if(X1 > X2)
  {
    Tmp = X1; X1 = X2; X2 = Tmp;
  }
  if(Y1 > Y2)
  {
    Tmp = Y1; Y1 = Y2; Y2 = Tmp;
  }
  DrawLine(X1, Y1, X2, Y1, Col);
  DrawLine(X2, Y1, X2, Y2, Col);
  DrawLine(X1, Y2, X2, Y2, Col);
  DrawLine(X1, Y1, X1, Y2, Col); 
}

/*******************************************************
*   Function : VECBuffer::FillRectangle                *
*------------------------------------------------------*
*                                                      *
*******************************************************/
VOID VECBuffer::FillRectangle(UINT X1, UINT Y1, UINT X2, UINT Y2)
{
  UINT Swap;

  if(X1 > X2)
  {
    Swap = X1; X1 = X2; X2 = Swap;
  }

  if(Y1 > Y2)
  {
    Swap = Y1; Y1 = Y2; Y2 = Swap;
  }

  if ((X1 > ClipXMax) || (X2 < ClipXMin) ||
      (Y1 > ClipYMax) || (Y2 < ClipYMin))
  {
    return;
  } else {

    if (X1 < ClipXMin) X1 = ClipXMin;
    if (X2 > ClipXMax) X2 = ClipXMax;
    if (Y1 < ClipYMin) Y1 = ClipYMin;
    if (Y2 > ClipYMax) Y2 = ClipYMax;

    UINT Offset = Width - (X2 - X1) -1;

    UBYTE * PtrTmp = Data + FastIdx[Y1] + X1;

    for(UINT j=Y1; j<=Y2; j++)
    {
      for(UINT i=X1; i<=X2; i++)
      {
	*PtrTmp++ = (CMap->*GetColor)(FGColor, i, j);
      }
      PtrTmp += Offset;
    }
  }
}
VOID VECBuffer::FillRectangle(UINT X1, UINT Y1, UINT X2, UINT Y2, VECColor &Col)
{
  UINT Swap;

  if(X1 > X2)
  {
    Swap = X1; X1 = X2; X2 = Swap;
  }

  if(Y1 > Y2)
  {
    Swap = Y1; Y1 = Y2; Y2 = Swap;
  }

  if ((X1 > ClipXMax) || (X2 < ClipXMin) ||
      (Y1 > ClipYMax) || (Y2 < ClipYMin))
  {
    return;
  } else {

    if (X1 < ClipXMin) X1 = ClipXMin;
    if (X2 > ClipXMax) X2 = ClipXMax;
    if (Y1 < ClipYMin) Y1 = ClipYMin;
    if (Y2 > ClipYMax) Y2 = ClipYMax;

    UINT Offset = Width - (X2 - X1) -1;

    UBYTE * PtrTmp = Data + FastIdx[Y1] + X1;

    for(UINT j=Y1; j<=Y2; j++)
    {
      for(UINT i=X1; i<=X2; i++)
      {
	*PtrTmp++ = (CMap->*GetColor)(Col, i, j);
      }
      PtrTmp += Offset;
    }
  }
}

/*******************************************************
*   Function : VECBuffer::DrawPolygon                  *
*------------------------------------------------------*
*                                                      *
*******************************************************/
VOID VECBuffer::DrawPolygon(VECDrawablePolygon * Poly)
{
  if(Poly)
  {
    static VECPixel P1;
    static VECPixel P2;
    static VECVertex *V;

    V = Poly->Vertices.GetFirst();

    P1 = V->ScreenVertex;

    while(V = Poly->Vertices.GetNext())
    {
      P2 = V->ScreenVertex;

      if(LineClipping2D(P1, P2))
        DrawLine(P1, P2, FGColor);
      
      P1 = V->ScreenVertex;
    }
  }
}

VOID VECBuffer::DrawPolygon(VECDrawablePolygon * Poly, VECColor &Col)
{
   if(Poly)
  {
    static VECPixel P1;
    static VECPixel P2;
    static VECVertex *V;

    V = Poly->Vertices.GetFirst();

    P1 = V->ScreenVertex;

    while(V = Poly->Vertices.GetNext())
    {
      P2 = V->ScreenVertex;

      if(LineClipping2D(P1, P2))
        DrawLine(P1, P2, Col);

      P1 = V->ScreenVertex;
    }
  }

}

/*******************************************************
*   Function : VECBuffer::ComputeScanLine              *
*------------------------------------------------------*
*                                                      *
*******************************************************/
VOID VECBuffer::ComputeScanLinesF(VECDrawablePolygon * Poly)
{
  // Poly a minimum 3 points  

  VECVertex * V1 = Poly->Vertices.GetFirst();
  VECVertex * V2;

  INT IncY, Y, dY, dX;

  DOUBLE DeltaX, X, DeltaZ, Z;

  BOOL Horiz;

  // initialisation des scan lines
  for (int i=G_YMin; i<=G_YMax; i++)
  {
    G_Start[i].X = RESOLUTION_MAX_X;
    G_End[i].X = 0;
  }
  G_YMin = RESOLUTION_MAX_Y;
  G_YMax = 0;

  if(V1->ScreenVertex.Y > G_YMax) G_YMax = V1->ScreenVertex.Y;
  if(V1->ScreenVertex.Y < G_YMin) G_YMin = V1->ScreenVertex.Y;

  // boucle sur les arretes
  while(V2 = Poly->Vertices.GetNext())
  {
    if(V2->ScreenVertex.Y > G_YMax) G_YMax = V2->ScreenVertex.Y;
    if(V2->ScreenVertex.Y < G_YMin) G_YMin = V2->ScreenVertex.Y;

    dX = V2->ScreenVertex.X - V1->ScreenVertex.X;
    dY = V2->ScreenVertex.Y - V1->ScreenVertex.Y;

    if(dY)
    {
      IncY = dY>0?1:(dY=-dY,-1);

      Horiz = ( (dX<0?-dX:dX) > dY);

      X = (double)V1->ScreenVertex.X+0.5;
      Y = V1->ScreenVertex.Y;
      Z = V1->CameraVertex.Z;

      if(Horiz)
      {
        ++dY;
        DeltaX = (double)dX / (double)dY;
        DeltaZ = (V2->CameraVertex.Z - Z)/(double)dY;

	while(dY--)
	{
	  if(G_Start[Y].X > (INT)X)
	  {
	    G_Start[Y].X = (INT)X;
            G_Start[Y].CamVert.Z = Z;
	  }
	  if(G_End[Y].X < (INT)X)
	  {
	    G_End[Y].X = (INT)X;
            G_End[Y].CamVert.Z = Z;
	  }
	  X += DeltaX;
          Z += DeltaZ;

	  if(G_Start[Y].X > (INT)X)
	  {
	    G_Start[Y].X = (INT)X;
            G_Start[Y].CamVert.Z = Z;
	  }
	  if(G_End[Y].X < (INT)X)
	  {
	    G_End[Y].X = (INT)X;
            G_End[Y].CamVert.Z = Z;
	  }
	  Y += IncY;
	}
      } else {
        DeltaX = (double)dX / (double)dY;
        DeltaZ = (V2->CameraVertex.Z - Z)/(double)dY;
        ++dY;

        while(dY--)
	{
	  if(G_Start[Y].X > (INT)X)
	  {
	    G_Start[Y].X = (INT)X;
            G_Start[Y].CamVert.Z = Z;
	  }
	  if(G_End[Y].X < (INT)X)
	  {
	    G_End[Y].X = (INT)X;
            G_End[Y].CamVert.Z = Z;
	  }
          X += DeltaX;
	  Y += IncY;
          Z += DeltaZ;
	}
      }     
    } else {
      
      if(G_Start[V1->ScreenVertex.Y].X > V1->ScreenVertex.X)
      {
        G_Start[V1->ScreenVertex.Y].X = V1->ScreenVertex.X;
        G_Start[V1->ScreenVertex.Y].CamVert.Z = V1->CameraVertex.Z;
      }
      if(G_Start[V2->ScreenVertex.Y].X > V2->ScreenVertex.X)
      {
        G_Start[V2->ScreenVertex.Y].X = V2->ScreenVertex.X;
        G_Start[V2->ScreenVertex.Y].CamVert.Z = V2->CameraVertex.Z;
      }
      if(G_End[V1->ScreenVertex.Y].X < V1->ScreenVertex.X)
      {
        G_End[V1->ScreenVertex.Y].X = V1->ScreenVertex.X;
        G_End[V1->ScreenVertex.Y].CamVert.Z = V1->CameraVertex.Z;
      }
      if(G_End[V2->ScreenVertex.Y].X < V2->ScreenVertex.X)
      {
        G_End[V2->ScreenVertex.Y].X = V2->ScreenVertex.X;
        G_End[V2->ScreenVertex.Y].CamVert.Z = V2->CameraVertex.Z;
      }
    }
    V1 = V2;
  }
}

VOID VECBuffer::ComputeScanLinesG(VECDrawablePolygon * Poly)
{
  // Poly a minimum 3 points  

  VECVertex * V1 = Poly->Vertices.GetFirst();
  VECVertex * V2;

  INT IncY, Y, dY, dX;

  DOUBLE DeltaX, X, DeltaZ, Z;
  DOUBLE R, G, B, dR, dG, dB;

  BOOL Horiz;

  // initialisation des scan lines
  for (int i=G_YMin; i<=G_YMax; i++)
  {
    G_Start[i].X = RESOLUTION_MAX_X;
    G_End[i].X = 0;
  }
  G_YMin = RESOLUTION_MAX_Y;
  G_YMax = 0;

  if(V1->ScreenVertex.Y > G_YMax) G_YMax = V1->ScreenVertex.Y;
  if(V1->ScreenVertex.Y < G_YMin) G_YMin = V1->ScreenVertex.Y;

  // boucle sur les arretes
  while(V2 = Poly->Vertices.GetNext())
  {
    if(V2->ScreenVertex.Y > G_YMax) G_YMax = V2->ScreenVertex.Y;
    if(V2->ScreenVertex.Y < G_YMin) G_YMin = V2->ScreenVertex.Y;

    dX = V2->ScreenVertex.X - V1->ScreenVertex.X;
    dY = V2->ScreenVertex.Y - V1->ScreenVertex.Y;

    if(dY)
    {
      IncY = dY>0?1:(dY=-dY,-1);

      Horiz = ( (dX<0?-dX:dX) > dY);

      X = (double)V1->ScreenVertex.X+0.5;
      Y = V1->ScreenVertex.Y;
      Z = V1->CameraVertex.Z;

      R = V1->Color.R;
      G = V1->Color.G;
      B = V1->Color.B;

      if(Horiz)
      {
        ++dY;
        DeltaX = (double)dX / (double)dY;
        DeltaZ = (V2->CameraVertex.Z - Z)/(double)dY;

        dR = (V2->Color.R - R)/(double)dY;
        dG = (V2->Color.G - G)/(double)dY;
        dB = (V2->Color.B - B)/(double)dY;

	while(dY--)
	{
	  if(G_Start[Y].X > (INT)X)
	  {
	    G_Start[Y].X = (INT)X;
            G_Start[Y].CamVert.Z = Z;
            G_Start[Y].Col.R = R;
            G_Start[Y].Col.G = G;
            G_Start[Y].Col.B = B;
	  }
	  if(G_End[Y].X < (INT)X)
	  {
	    G_End[Y].X = (INT)X;
            G_End[Y].CamVert.Z = Z;
            G_End[Y].Col.R = R;
            G_End[Y].Col.G = G;
            G_End[Y].Col.B = B;
	  }
	  X += DeltaX;
          Z += DeltaZ;
          R += dR; G += dG; B += dB;

	  if(G_Start[Y].X > (INT)X)
	  {
	    G_Start[Y].X = (INT)X;
            G_Start[Y].CamVert.Z = Z;
            G_Start[Y].Col.R = R;
            G_Start[Y].Col.G = G;
            G_Start[Y].Col.B = B;
	  }
	  if(G_End[Y].X < (INT)X)
	  {
	    G_End[Y].X = (INT)X;
            G_End[Y].CamVert.Z = Z;
            G_End[Y].Col.R = R;
            G_End[Y].Col.G = G;
            G_End[Y].Col.B = B;
	  }
	  Y += IncY;
	}
      } else {
        DeltaX = (double)dX / (double)dY;
        DeltaZ = (V2->CameraVertex.Z - Z)/(double)dY;

        dR = (V2->Color.R - R)/(double)dY;
        dG = (V2->Color.G - G)/(double)dY;
        dB = (V2->Color.B - B)/(double)dY;
        ++dY;

        while(dY--)
	{
	  if(G_Start[Y].X > (INT)X)
	  {
	    G_Start[Y].X = (INT)X;
            G_Start[Y].CamVert.Z = Z;
            G_Start[Y].Col.R = R;
            G_Start[Y].Col.G = G;
            G_Start[Y].Col.B = B;
	  }
	  if(G_End[Y].X < (INT)X)
	  {
	    G_End[Y].X = (INT)X;
            G_End[Y].CamVert.Z = Z;
            G_End[Y].Col.R = R;
            G_End[Y].Col.G = G;
            G_End[Y].Col.B = B;
	  }
          X += DeltaX;
	  Y += IncY;
          Z += DeltaZ;
          R += dR; G += dG; B += dB;
	}
      }     
    } else {
      if(G_Start[V1->ScreenVertex.Y].X > V1->ScreenVertex.X)
      {
        G_Start[V1->ScreenVertex.Y].X = V1->ScreenVertex.X;
        G_Start[V1->ScreenVertex.Y].CamVert.Z = V1->CameraVertex.Z;
        G_Start[V1->ScreenVertex.Y].Col = V1->Color;
      }
      if(G_Start[V2->ScreenVertex.Y].X > V2->ScreenVertex.X)
      {
        G_Start[V2->ScreenVertex.Y].X = V2->ScreenVertex.X;
        G_Start[V2->ScreenVertex.Y].CamVert.Z = V2->CameraVertex.Z;
        G_Start[V2->ScreenVertex.Y].Col = V2->Color;
      }
      if(G_End[V1->ScreenVertex.Y].X < V1->ScreenVertex.X)
      {
        G_End[V1->ScreenVertex.Y].X = V1->ScreenVertex.X;
        G_End[V1->ScreenVertex.Y].CamVert.Z = V1->CameraVertex.Z;
        G_End[V1->ScreenVertex.Y].Col = V1->Color;
      }
      if(G_End[V2->ScreenVertex.Y].X < V2->ScreenVertex.X)
      {
        G_End[V2->ScreenVertex.Y].X = V2->ScreenVertex.X;
        G_End[V2->ScreenVertex.Y].CamVert.Z = V2->CameraVertex.Z;
        G_End[V2->ScreenVertex.Y].Col = V2->Color;
      }
    }
    V1 = V2;
  }
}

VOID VECBuffer::ComputeScanLinesT(VECDrawablePolygon * Poly)
{
  VECVertex * V1 = Poly->Vertices.GetFirst();
  VECVertex * V2;

  INT long_d, short_d;
  INT d, add_dh, add_dl;
  register INT inc_xh, inc_yh, inc_xl, inc_yl;
  register INT i, k;

  static INT dXscr, dYscr, Yscr, Xscr;
  static DOUBLE DeltaZ, DeltaU, DeltaV, CoeffU, CoeffV;
  static DOUBLE InvZ, dInvZ, Ztmp;
  static DOUBLE U, V, Z, dU, dV, dZ, lU, lV, lZ; 

  // initialisation des scan lines
  for (i=G_YMin; i<=G_YMax; i++)
  {
    G_Start[i].X = RESOLUTION_MAX_X;
    G_End[i].X = 0;
  }
  G_YMin = RESOLUTION_MAX_Y;
  G_YMax = 0;

  if(V1->ScreenVertex.Y > G_YMax) G_YMax = V1->ScreenVertex.Y;
  if(V1->ScreenVertex.Y < G_YMin) G_YMin = V1->ScreenVertex.Y;

  // boucle sur les arretes
  while(V2 = Poly->Vertices.GetNext())
  {
    if(V2->ScreenVertex.Y > G_YMax) G_YMax = V2->ScreenVertex.Y;
    if(V2->ScreenVertex.Y < G_YMin) G_YMin = V2->ScreenVertex.Y;

    dXscr = V2->ScreenVertex.X - V1->ScreenVertex.X; 
    dYscr = V2->ScreenVertex.Y - V1->ScreenVertex.Y;

    if(dXscr < 0)
    {
      dXscr = -dXscr;
      inc_xh = -1;
      inc_xl = -1;
    } else {
      inc_xh = 1;
      inc_xl = 1;
    }
    if(dYscr < 0)
    {
      dYscr = -dYscr;
      inc_yh = -1;
      inc_yl = -1;
    } else {
      inc_yh = 1;
      inc_yl = 1;
    }

    if (dXscr > dYscr)
    {
      long_d = dXscr;
      short_d = dYscr; 
      inc_yl = 0;
      
      d = (short_d<<1) - long_d;
      add_dl = short_d<<1;
      add_dh = (short_d<<1) - (long_d<<1);
    } else {
      
      long_d = dYscr;
      short_d = dXscr; 
      inc_xl = 0;
      
      d = (short_d<<1) - long_d;
      add_dl = short_d<<1;
      add_dh = (short_d<<1) - (long_d<<1);
    }

    Yscr = V1->ScreenVertex.Y;
    Xscr = V1->ScreenVertex.X;

    lU = U = V1->U;
    lV = V = V1->V;
    lZ = Z = V1->CameraVertex.Z;

    DeltaU = V2->U - U;
    DeltaV = V2->V - V;

    if((DeltaZ = V2->CameraVertex.Z - Z) > EPSILON)
    {
      // perspective texture mapping

      InvZ = 1.0 / Z;
      if( dXscr > dYscr)
      {
	dInvZ = (1.0 / V2->CameraVertex.Z - InvZ) / dXscr;
      } else {
	dInvZ = (1.0 / V2->CameraVertex.Z - InvZ) / dYscr;
      }

      CoeffU = DeltaU / DeltaZ;
      CoeffV = DeltaV / DeltaZ;

      for(i=0; i<=long_d; i++)
      {
	if(G_Start[Yscr].X > (INT)Xscr)
	{
	  G_Start[Yscr].X = (INT)Xscr;
	  G_Start[Yscr].CamVert.Z = lZ;
	  G_Start[Yscr].U = lU;
	  G_Start[Yscr].V = lV;
	}
	if(G_End[Yscr].X < (INT)Xscr)
	{
	  G_End[Yscr].X = (INT)Xscr;
	  G_End[Yscr].CamVert.Z = lZ;
	  G_End[Yscr].U = lU;
	  G_End[Yscr].V = lV;
	}
	if(d >= 0)
        {
	  Yscr += inc_yh;
	  Xscr += inc_xh;
	  d += add_dh;
	} else {
	  Yscr += inc_yl;
	  Xscr += inc_xl;
	  d += add_dl;
	}

	InvZ += dInvZ; 
        lZ = 1.0 / InvZ;
	Ztmp =  lZ - Z;
	lU = Ztmp * CoeffU + U; 
	lV = Ztmp * CoeffV + V;
      }
    } else {
      // Interpolated Texture mapping

      if( dXscr > dYscr)
      {
	dZ = DeltaZ / dXscr;
	dU = DeltaU / dXscr;
	dV = DeltaV / dXscr;
      } else {
	dZ = DeltaZ / dYscr;
	dU = DeltaU / dYscr;
	dV = DeltaV / dYscr;
      }

      for(i=0; i<=long_d; i++)
      {
	if(G_Start[Yscr].X > (INT)Xscr)
	{
	  G_Start[Yscr].X = (INT)Xscr;
	  G_Start[Yscr].CamVert.Z = Z;
	  G_Start[Yscr].U = U;
	  G_Start[Yscr].V = V;
	}
	if(G_End[Yscr].X < (INT)Xscr)
	{
	  G_End[Yscr].X = (INT)Xscr;
	  G_End[Yscr].CamVert.Z = Z;
	  G_End[Yscr].U = U;
	  G_End[Yscr].V = V;
	}
	if(d >= 0)
	{
	  Yscr += inc_yh;
	  Xscr += inc_xh;
	  d += add_dh;
	} else {
	  Yscr += inc_yl;
	  Xscr += inc_xl;
	  d += add_dl;
	}
        Z += dZ; U += dU; V += dV;
      }
    }
    V1 = V2;
  }
}

/*******************************************************
*   Function : VECBuffer::FillPolygon                  *
*------------------------------------------------------*
*                                                      *
*******************************************************/
VOID VECBuffer::FillPolygon(VECDrawablePolygon * Poly)
{
  static DOUBLE dZ, Z;  

  static UBYTE * PtrTmp;
  static DOUBLE * ZPtrTmp;

  register double Trans;

  if(Poly->Material)
  {
    Trans = Poly->Material->Kt;
  } else {
    Trans = 1.0;
  }

  if (PolygonXClipping(Poly) && PolygonYClipping(Poly))
  {
    ComputeScanLinesF(Poly);

    for(INT i=G_YMin; i<=G_YMax; i++)
    {
      PtrTmp = Data + FastIdx[i] + G_Start[i].X;
      ZPtrTmp = ZBuffer + FastIdx[i] + G_Start[i].X;

      Z = G_Start[i].CamVert.Z;
      dZ = (G_End[i].CamVert.Z - Z)/(DOUBLE)(G_End[i].X - G_Start[i].X);

      for(INT j=G_Start[i].X; j<=G_End[i].X; j++, PtrTmp++, ZPtrTmp++)
      {
        if((T8[j&7][i&7] > Trans) && (*ZPtrTmp > -Z))
	{
	  *PtrTmp = (CMap->*GetColor)(FGColor, j, i);
          *ZPtrTmp = -Z;
	}
        Z += dZ;
      }
    }  
  }
}

VOID VECBuffer::FillPolygon(VECDrawablePolygon * Poly, VECColor &Col)
{
  static DOUBLE dZ, Z;  

  static UBYTE * PtrTmp;
  static DOUBLE * ZPtrTmp;

  register double Trans;

  if(Poly->Material)
  {
    Trans = Poly->Material->Kt;
  } else {
    Trans = 1.0;
  }

  if (PolygonXClipping(Poly) && PolygonYClipping(Poly))
  {
    ComputeScanLinesF(Poly);

    for(INT i=G_YMin; i<=G_YMax; i++)
    {
      PtrTmp = &(Data[FastIdx[i]+G_Start[i].X]);
      ZPtrTmp = ZBuffer + FastIdx[i] + G_Start[i].X;

      Z = G_Start[i].CamVert.Z;
      dZ = (G_End[i].CamVert.Z - Z)/(DOUBLE)(G_End[i].X - G_Start[i].X);

      for(INT j=G_Start[i].X; j<=G_End[i].X; j++, PtrTmp++, ZPtrTmp++)
      {
        if((T8[j&7][i&7] > Trans) && (*ZPtrTmp > -Z))
	{
	  *PtrTmp = (CMap->*GetColor)(Col, j, i);
          *ZPtrTmp = -Z;
	}
        Z += dZ;
      }
    }  
  }
}

VOID VECBuffer::GouraudPolygon(VECDrawablePolygon * Poly)
{
  static DOUBLE R, G, B, dR, dG, dB;
  static DOUBLE dX, dZ, Z;

  static UBYTE * PtrTmp;
  static DOUBLE * ZPtrTmp;

  register double Trans;

  if(Poly->Material)
  {
    Trans = Poly->Material->Kt;
  } else {
    Trans = 1.0;
  }

  if (PolygonXClipping(Poly) && PolygonYClipping(Poly))
  {
    ComputeScanLinesG(Poly);

    for(INT i=G_YMin; i<=G_YMax; i++)
    {
      PtrTmp = Data + FastIdx[i] + G_Start[i].X;
      ZPtrTmp = ZBuffer + ZFastIdx[i] + G_Start[i].X;

      dX = (DOUBLE)(G_End[i].X - G_Start[i].X);

      Z = G_Start[i].CamVert.Z;
      dZ = (DOUBLE)(G_End[i].CamVert.Z - Z)/dX;

      R = G_Start[i].Col.R;
      dR = (DOUBLE)(G_End[i].Col.R - R)/dX;
      G = G_Start[i].Col.G;
      dG = (DOUBLE)(G_End[i].Col.G - G)/dX;
      B = G_Start[i].Col.B;
      dB = (DOUBLE)(G_End[i].Col.B - B)/dX;

      for(INT j=G_Start[i].X; j<=G_End[i].X; j++, PtrTmp++, ZPtrTmp++)
      {
        if ((T8[j&7][i&7] > Trans) && (*ZPtrTmp > -Z))
	{
	  *PtrTmp = (CMap->*GetColor)(VECColor(R, G, B), j, i); // PEY
	  *ZPtrTmp = -Z;
	} 
	R += dR;
	G += dG;
	B += dB;
        Z += dZ;
      }
    }  
  }
}

VOID VECBuffer::PhongPolygon(VECDrawablePolygon * Poly)
{
}

VOID VECBuffer::TexturedPolygon(VECDrawablePolygon * Poly)
{
  static DOUBLE U, V, Z, dU, dV, dZ, lU, lV, lZ;
  static DOUBLE InvZ, dInvZ, Ztmp;
  static DOUBLE DeltaU, DeltaV, DeltaZ, CoeffU, CoeffV; 

  static INT Xscr1, Xscr2, dXscr;
  register INT k;

  UBYTE * PtrTmp;
  DOUBLE * ZPtrTmp;

  if (PolygonXClipping(Poly) && PolygonYClipping(Poly))
  {
    ComputeScanLinesT(Poly);

    for(INT i=G_YMin; i<=G_YMax; i++)
    {

      if(i== 130)
	printf("toto\n");

      PtrTmp = Data + FastIdx[i] + G_Start[i].X;
      ZPtrTmp = ZBuffer + ZFastIdx[i] + G_Start[i].X;

      Xscr1 = G_Start[i].X; 
      Xscr2 = G_End[i].X;
      dXscr = Xscr2 - Xscr1;

      lZ = Z = G_Start[i].CamVert.Z;
      lU = U = G_Start[i].U;
      lV = V = G_Start[i].V;

      DeltaU = G_End[i].U - U;
      DeltaV = G_End[i].V - V;

      if((DeltaZ = G_End[i].CamVert.Z - Z) > EPSILON)
      {
	// Perspective texture mapping

	InvZ = 1.0 / Z;
	dInvZ = (1.0 / G_End[i].CamVert.Z - InvZ) / dXscr;

        CoeffU = DeltaU / DeltaZ;
        CoeffV = DeltaV / DeltaZ;

        for(k=Xscr1; k<=Xscr2; k++)
	{
          if (*ZPtrTmp > -lZ)
          {
            *PtrTmp = (CMap->*GetColor)(Poly->Material->Texture->GetColor(lU, lV), k, i); 
            *ZPtrTmp = -lZ;
          } 
	  InvZ += dInvZ; 
          lZ = 1.0 / InvZ;
	  Ztmp = lZ - Z;
	  lU = Ztmp * CoeffU + U; 
	  lV = Ztmp * CoeffV + V;
	  ZPtrTmp++; PtrTmp++;
	}
      } else {
	// Interpolated texture mapping

	dZ = DeltaZ / dXscr;
	dU = DeltaU / dXscr;
	dV = DeltaV / dXscr;

        for(k=Xscr1; k<=Xscr2; k++)
	{
          if (*ZPtrTmp > -Z)
          {
            *PtrTmp = (CMap->*GetColor)(Poly->Material->Texture->GetColor(U, V), k, i); 
            *ZPtrTmp = -Z;
          }
          Z += dZ; U += dU; V += dV; ZPtrTmp++; PtrTmp++;
	}
      }
    }
  }
}

/*******************************************************
*   Function : VECBuffer::PrintText                    *
*------------------------------------------------------*
*                                                      *
*******************************************************/
VOID VECBuffer::PrintText(UINT X, UINT Y, char * Text)
{
  char * CPtr = Text;
  char LnCh;

  UINT Column = 0, CurX = X, CurY = Y;

  UBYTE * PtrTmp;

  if ((X < ClipXMax) && (Y < ClipYMax))
  {

    while(*CPtr)
    {
      if(*CPtr == '\n') 
      {
        CurX = X;
        Y += 10;
        Column = 0;
      } else {

        CurX = X + Column;
        CurY = Y;
	PtrTmp = Data + FastIdx[Y] + CurX;

	for(int i=0; i<8; i++)
        {
	  LnCh = (FONT8x8[*CPtr])[i];

	  for(int j=0; j<8; j++)
	  {
	    if((LnCh & 0x80) &&
               (CurX >= ClipXMin) && (CurX <= ClipXMax) &&
               (CurY >= ClipYMin) && (CurY <= ClipYMax))
	    {
	      *PtrTmp = CMap->GetClosestColor(FGColor, 0, 0);
	    }
	    LnCh <<= 1;
	    PtrTmp++;
            CurX++;
	  }
          CurY++; CurX-=8;
	  PtrTmp += Width - 8;
	}
	Column+=9;
      }
      CPtr++;
    }
  }
}

/*******************************************************
*   Function : VECBuffer::Clear                        *
*------------------------------------------------------*
*                                                      *
*******************************************************/
VOID VECBuffer::Clear()
{
  if(Data)
    memset(Data,CMap->GetClosestColor(BGColor, 0, 0),Width*Height*Depht);
}

/*VOID VECBuffer::Clear()
{
  if(Data)
  {
    register LONG i;
    LONG BlockSize = Width*8;
    LONG Stop = BlockSize;
    UBYTE * Ptr = Data;

    for(i=0; i<Stop; i++)
    {
      *Ptr++ = CMap->GetDitherColor(BGColor, i%Width, i/Width);
    }

    Stop = (Height/8)-1;
    for(i=0; i<Stop; i++)
    {
      memcpy(Ptr, Data, BlockSize);
      Ptr += BlockSize;
    }

    BlockSize = (Height % 8) * Width;

    memcpy(Ptr, Data, BlockSize);
  }
}*/

/*VOID VECBuffer::Clear(VECColor &Col)
{
  DOUBLE R, G, B, dR, dG, dB;

  R = Col.R; G = Col.G;  B = Col.B;

  dR = (BGColor.R - R)/(DOUBLE)Height;
  dG = (BGColor.G - G)/(DOUBLE)Height;
  dB = (BGColor.B - B)/(DOUBLE)Height;

  if(Data)
  {
    register LONG i;
    LONG Stop = Width*Height*Depht;
    UBYTE * Ptr = Data;

    for(i=0; i<Stop; i++)
    {
      if(!(i%Width))
      {
        R += dR; G += dG; B += dB;
      }

      *Ptr++ = CMap->GetDitherColor(i%Width, i/Width, R, G, B);
    }
  }
}*/

VOID VECBuffer::Clear(VECColor &Col)
{
  if(Data)
  {
    register LONG i;
    LONG Stop = Width*Height*Depht;
    UBYTE * Ptr = Data;

    for(i=0; i<Stop; i++)
    {
      *Ptr++ = CMap->GetDitherColor(Col, i%Width, i/Width);
    }
  }
}

/*******************************************************
*   Function : VECBuffer::ResetZBuffer                 *
*------------------------------------------------------*
*                                                      *
*******************************************************/
VOID VECBuffer::ResetZBuffer()
{
  ULONG Sum = 0;

  memset(ZBuffer, 0x7F, Width*Height*sizeof(DOUBLE));

  for(INT i=0; i<Height; i++)
  {
    ZFastIdx[i] = Sum;
    Sum += Width;
  }
}

/*******************************************************
*   Function : VECBuffer::InitBackground               *
*------------------------------------------------------*
*                                                      *
*******************************************************/
/*VOID VECBuffer::InitBackground(VECImage *Image)
{
}*/

/*******************************************************
*   Function : VECBuffer::LineClipping                 *
*------------------------------------------------------*
*                                                      *
*******************************************************/
BOOL VECBuffer::LineClipping2D(VECPixel &P1, VECPixel &P2)
{  
  VECPixel *Pmin, *Pmax;

  static VECPixel Left, Midle, Right;

  BOOL Flag;

  // Clipping en X
  if (P1.X < P2.X)
  {
    Pmin = &P1; Pmax = &P2;
  } else {
    Pmin = &P2; Pmax = &P1;
  }

  if((Pmin->X > ClipXMax) || (Pmax->X < ClipXMin))
  {
    return FALSE;
  } else {
    if(Pmin->X < ClipXMin)
    {
      Left = *Pmin; Midle = *Pmax;     

      Flag = FALSE; 
      while(Midle.X != ClipXMin)
      {
        if (Flag) 
        { 
          Left = Midle;
        } else {
          Right = Midle;
	}
        Midle.X = (Left.X + Right.X)>>1;
        Midle.Y = (Left.Y + Right.Y)>>1;
        Flag = Midle.X < ClipXMin;
      }
      *Pmin = Midle;
    }

    if(Pmax->X > ClipXMax)
    {
      Left = *Pmin; Midle = *Pmax;     

      Flag = FALSE; 
      while(Midle.X != ClipXMax)
      {
        if (Flag) 
        { 
          Left = Midle; 
        } else {
          Right = Midle; 
	}
        Midle.X = (Left.X + Right.X)>>1;
        Midle.Y = (Left.Y + Right.Y)>>1;
        Flag = Midle.X < ClipXMax;
      }
      *Pmax = Midle;
    }
  }

  // Clipping en Y
  if (P1.Y < P2.Y)
  {
    Pmin = &P1; Pmax = &P2;
  } else {
    Pmin = &P2; Pmax = &P1;
  }

  if((Pmin->Y > ClipYMax) || (Pmax->Y < ClipYMin))
  {
    return FALSE;
  } else {
    if(Pmin->Y < ClipYMin)
    {
      Left = *Pmin; Midle = *Pmax;     

      Flag = FALSE; 
      while(Midle.Y != ClipYMin)
      {
        if (Flag) 
        { 
          Left=Midle;
        } else {
          Right=Midle;
	}
        Midle.X = (Left.X + Right.X)>>1;
        Midle.Y = (Left.Y + Right.Y)>>1;
        Flag = Midle.Y < ClipYMin;
      }
      *Pmin = Midle;
    }

    if(Pmax->Y > ClipYMax)
    {
      Left = *Pmin; Midle = *Pmax;     

      Flag = FALSE; 
      while(Midle.Y != ClipYMax)
      {
        if (Flag) 
        { 
          Left = Midle; 
        } else {
          Right = Midle; 
	}
        Midle.X = (Left.X + Right.X)>>1;
        Midle.Y = (Left.Y + Right.Y)>>1;
        Flag = Midle.Y < ClipYMax;
      }
      *Pmax = Midle;
    }
  }
  return TRUE;
}

/*******************************************************
*   Function : VECBuffer::EdgeClippingX                *
*------------------------------------------------------*
*                                                      *
*******************************************************/
BOOL VECBuffer::EdgeXClipping(VECVertex & V1, VECVertex & V2)
{ 
  VECVertex * Pmin, *Pmax;

  INT Swap;

  BOOL Flag;

  register VECVertex * Tmp, *Left, *Midle, *Right;
  static VECVertex Store0;
  static VECVertex Store1;
  static VECVertex Store2;
  static VECVertex Store3;
  static VECVertex Store4;
  static VECVertex Store5;

  // Clipping en X
  WhichClipped = 0;

  if (V1.ScreenVertex.X < V2.ScreenVertex.X)
  {
    Swap = 0; Pmin = &V1; Pmax = &V2;
  } else {
    Swap = 1; Pmin = &V2; Pmax = &V1;
  }

  if((Pmin->ScreenVertex.X > ClipXMax) || (Pmax->ScreenVertex.X < ClipXMin))
  {
    return FALSE;
  } else {
    if(Pmin->ScreenVertex.X < ClipXMin)
    {
      Store0 = *Pmin; Left = &Store0; 
      Store1 = *Pmax; Midle = &Store1;
      Right = &Store2;     

      Flag = FALSE; 
      while(Midle->ScreenVertex.X != ClipXMin)
      {
        if (Flag) 
        { 
          Tmp=Left; Left=Midle; Midle=Tmp;
        } else {
          Tmp=Right; Right=Midle; Midle=Tmp;
	}
        Midle->ScreenVertex.X = (Left->ScreenVertex.X + Right->ScreenVertex.X)/2;
        Midle->ScreenVertex.Y = (Left->ScreenVertex.Y + Right->ScreenVertex.Y)/2;

        Midle->ObjectVertex.X = (Left->ObjectVertex.X + Right->ObjectVertex.X)/2.0;
        Midle->ObjectVertex.Y = (Left->ObjectVertex.Y + Right->ObjectVertex.Y)/2.0;
        Midle->ObjectVertex.Z = (Left->ObjectVertex.Z + Right->ObjectVertex.Z)/2.0;

        Midle->CameraVertex.X = (Left->CameraVertex.X + Right->CameraVertex.X)/2.0;
        Midle->CameraVertex.Y = (Left->CameraVertex.Y + Right->CameraVertex.Y)/2.0;
        Midle->CameraVertex.Z = (Left->CameraVertex.Z + Right->CameraVertex.Z)/2.0;

        Midle->Normal.X = (Left->Normal.X + Right->Normal.X)/2.0;
        Midle->Normal.Y = (Left->Normal.Y + Right->Normal.Y)/2.0;
        Midle->Normal.Z = (Left->Normal.Z + Right->Normal.Z)/2.0;

        Midle->Color.R = (Left->Color.R + Right->Color.R) / 2.0;
        Midle->Color.G = (Left->Color.G + Right->Color.G) / 2.0;
        Midle->Color.B = (Left->Color.B + Right->Color.B) / 2.0;

        Midle->I = (Left->I + Right->I)/2.0;
        Midle->U = (Left->U + Right->U)/2.0;
        Midle->V = (Left->V + Right->V)/2.0;

        Flag = Midle->ScreenVertex.X < ClipXMin;
      }
      *Pmin = *Midle;
      WhichClipped = Swap ^ 1;
    }

    if(Pmax->ScreenVertex.X > ClipXMax)
    {
      Store0 = *Pmin; Left = &Store0; 
      Store1 = *Pmax; Midle = &Store1;
      Right = &Store2;   

      Flag = FALSE; 
      while(Midle->ScreenVertex.X != ClipXMax)
      {
        if (Flag) 
        { 
          Tmp=Left; Left=Midle; Midle=Tmp;
        } else {
          Tmp=Right; Right=Midle; Midle=Tmp;
	}

        Midle->ScreenVertex.X = (Left->ScreenVertex.X + Right->ScreenVertex.X)/2;
        Midle->ScreenVertex.Y = (Left->ScreenVertex.Y + Right->ScreenVertex.Y)/2;

        Midle->ObjectVertex.X = (Left->ObjectVertex.X + Right->ObjectVertex.X)/2.0;
        Midle->ObjectVertex.Y = (Left->ObjectVertex.Y + Right->ObjectVertex.Y)/2.0;
        Midle->ObjectVertex.Z = (Left->ObjectVertex.Z + Right->ObjectVertex.Z)/2.0;

        Midle->CameraVertex.X = (Left->CameraVertex.X + Right->CameraVertex.X)/2.0;
        Midle->CameraVertex.Y = (Left->CameraVertex.Y + Right->CameraVertex.Y)/2.0;
        Midle->CameraVertex.Z = (Left->CameraVertex.Z + Right->CameraVertex.Z)/2.0;

        Midle->Normal.X = (Left->Normal.X + Right->Normal.X)/2.0;
        Midle->Normal.Y = (Left->Normal.Y + Right->Normal.Y)/2.0;
        Midle->Normal.Z = (Left->Normal.Z + Right->Normal.Z)/2.0;

        Midle->Color.R = (Left->Color.R + Right->Color.R) / 2.0;
        Midle->Color.G = (Left->Color.G + Right->Color.G) / 2.0;
        Midle->Color.B = (Left->Color.B + Right->Color.B) / 2.0;

        Midle->I = (Left->I + Right->I)/2.0;
        Midle->U = (Left->U + Right->U)/2.0;
        Midle->V = (Left->V + Right->V)/2.0;

        Flag = Midle->ScreenVertex.X < ClipXMax;
      }
      *Pmax = *Midle;
      WhichClipped |= Swap & 1;
    }
  }

  return TRUE;
}

/*******************************************************
*   Function : VECBuffer::EdgeClippingY                *
*------------------------------------------------------*
*                                                      *
*******************************************************/
BOOL VECBuffer::EdgeYClipping(VECVertex &V1, VECVertex &V2)
{ 
  VECVertex * Pmin, *Pmax;

  INT Swap;

  BOOL Flag;

  register VECVertex * Tmp, *Left, *Midle, *Right;

  static VECVertex Store0;
  static VECVertex Store1;
  static VECVertex Store2;
  static VECVertex Store3;
  static VECVertex Store4;
  static VECVertex Store5;
 
  WhichClipped = 0;

  if (V1.ScreenVertex.Y < V2.ScreenVertex.Y)
  {
    Swap = 0; Pmin = &V1; Pmax = &V2;
  } else {
    Swap = 1; Pmin = &V2; Pmax = &V1;
  }

  if((Pmin->ScreenVertex.Y > ClipYMax) || (Pmax->ScreenVertex.Y < ClipYMin))
  {
    return FALSE;
  } else {
    if(Pmin->ScreenVertex.Y < ClipYMin)
    {
      Store0 = *Pmin; Left = &Store0; 
      Store1 = *Pmax; Midle = &Store1;
      Right = &Store2;     

      Flag = FALSE; 
      while(Midle->ScreenVertex.Y != ClipYMin)
      {
        if (Flag) 
        { 
          Tmp=Left; Left=Midle; Midle=Tmp;
        } else {
          Tmp=Right; Right=Midle; Midle=Tmp;
	}
        Midle->ScreenVertex.X = (Left->ScreenVertex.X + Right->ScreenVertex.X)/2;
        Midle->ScreenVertex.Y = (Left->ScreenVertex.Y + Right->ScreenVertex.Y)/2;

        Midle->ObjectVertex.X = (Left->ObjectVertex.X + Right->ObjectVertex.X)/2.0;
        Midle->ObjectVertex.Y = (Left->ObjectVertex.Y + Right->ObjectVertex.Y)/2.0;
        Midle->ObjectVertex.Z = (Left->ObjectVertex.Z + Right->ObjectVertex.Z)/2.0;

        Midle->CameraVertex.X = (Left->CameraVertex.X + Right->CameraVertex.X)/2.0;
        Midle->CameraVertex.Y = (Left->CameraVertex.Y + Right->CameraVertex.Y)/2.0;
        Midle->CameraVertex.Z = (Left->CameraVertex.Z + Right->CameraVertex.Z)/2.0;

        Midle->Normal.X = (Left->Normal.X + Right->Normal.X)/2.0;
        Midle->Normal.Y = (Left->Normal.Y + Right->Normal.Y)/2.0;
        Midle->Normal.Z = (Left->Normal.Z + Right->Normal.Z)/2.0;

        Midle->Color.R = (Left->Color.R + Right->Color.R) / 2.0;
        Midle->Color.G = (Left->Color.G + Right->Color.G) / 2.0;
        Midle->Color.B = (Left->Color.B + Right->Color.B) / 2.0;

        Midle->I = (Left->I + Right->I)/2.0;
        Midle->U = (Left->U + Right->U)/2.0;
        Midle->V = (Left->V + Right->V)/2.0;

        Flag = Midle->ScreenVertex.Y < ClipYMin;
      }
      *Pmin = *Midle;
      WhichClipped = Swap ^ 1;
    }

    if(Pmax->ScreenVertex.Y > ClipYMax)
    {
      Store0 = *Pmin; Left = &Store0; 
      Store1 = *Pmax; Midle = &Store1;
      Right = &Store2;   

      Flag = FALSE; 
      while(Midle->ScreenVertex.Y != ClipYMax)
      {
        if (Flag) 
        { 
          Tmp=Left; Left=Midle; Midle=Tmp;
        } else {
          Tmp=Right; Right=Midle; Midle=Tmp;
	}

        Midle->ScreenVertex.X = (Left->ScreenVertex.X + Right->ScreenVertex.X)/2;
        Midle->ScreenVertex.Y = (Left->ScreenVertex.Y + Right->ScreenVertex.Y)/2;

        Midle->ObjectVertex.X = (Left->ObjectVertex.X + Right->ObjectVertex.X)/2.0;
        Midle->ObjectVertex.Y = (Left->ObjectVertex.Y + Right->ObjectVertex.Y)/2.0;
        Midle->ObjectVertex.Z = (Left->ObjectVertex.Z + Right->ObjectVertex.Z)/2.0;

        Midle->CameraVertex.X = (Left->CameraVertex.X + Right->CameraVertex.X)/2.0;
        Midle->CameraVertex.Y = (Left->CameraVertex.Y + Right->CameraVertex.Y)/2.0;
        Midle->CameraVertex.Z = (Left->CameraVertex.Z + Right->CameraVertex.Z)/2.0;

        Midle->Normal.X = (Left->Normal.X + Right->Normal.X)/2.0;
        Midle->Normal.Y = (Left->Normal.Y + Right->Normal.Y)/2.0;
        Midle->Normal.Z = (Left->Normal.Z + Right->Normal.Z)/2.0;

        Midle->Color.R = (Left->Color.R + Right->Color.R) / 2.0;
        Midle->Color.G = (Left->Color.G + Right->Color.G) / 2.0;
        Midle->Color.B = (Left->Color.B + Right->Color.B) / 2.0;

        Midle->I = (Left->I + Right->I)/2.0;
        Midle->U = (Left->U + Right->U)/2.0;
        Midle->V = (Left->V + Right->V)/2.0;

        Flag = Midle->ScreenVertex.Y < ClipYMax;
      }
      *Pmax = *Midle;
      WhichClipped = Swap & 1;
    }
  }
  return TRUE;
}

/*******************************************************
*   Function : VECBuffer::PolygonXClipping             *
*------------------------------------------------------*
*                                                      *
*******************************************************/
BOOL VECBuffer::PolygonXClipping(VECDrawablePolygon * Poly)
{
  
  register INT i;

  INT NbV = Poly->NbVertices();

  VECVertex V1, V2;

  for(i=0; i < NbV-1; i++)
  {
    V1=Poly->Vertices[i];
    V2=Poly->Vertices[i+1];
    if(EdgeXClipping2(V1, V2))
    {
      if(WhichClipped)
      {
        Poly->Vertices += V1;
        Poly->Vertices += V2;
      } else {
        Poly->Vertices += V2;
      }
    } 
  }

  for(i=0; i < NbV; i++)
  {
    (Poly->Vertices) -= 0;
  }

  if (NbV = Poly->NbVertices())
  {
    Poly->Vertices += Poly->Vertices[0];
    ++NbV;
  }
  return NbV;
}

/*******************************************************
*   Function : VECBuffer::PolygonYClipping             *
*------------------------------------------------------*
*                                                      *
*******************************************************/
BOOL VECBuffer::PolygonYClipping(VECDrawablePolygon * Poly)
{
  register INT i;

  INT NbV = Poly->NbVertices();

  VECVertex V1, V2;

  for(i=0; i < NbV-1; i++)
  {
    if(EdgeYClipping2(V1=Poly->Vertices[i], V2=Poly->Vertices[i+1]))
    {
      if(WhichClipped)
      {
        Poly->Vertices += V1;
        Poly->Vertices += V2;
      } else {
        Poly->Vertices += V2;
      }
    } 
  }

  for(i=0; i < NbV; i++)
  {
    (Poly->Vertices) -= 0;
  }

  if (NbV = Poly->NbVertices())
  {
    Poly->Vertices += Poly->Vertices[0];
    ++NbV;
  }

  return NbV;
}

/*******************************************************
*   Function : VECBuffer::EdgeClippingY2               *
*------------------------------------------------------*
*                                                      *
*******************************************************/
BOOL VECBuffer::EdgeYClipping2(VECVertex &V1, VECVertex &V2)
{ 
  static VECVertex VC1, VC2;

  VECVertex *Pmin, *Pmax;

  INT Swap;

  WhichClipped = 0;

  if (V1.ScreenVertex.Y < V2.ScreenVertex.Y)
  {
    Swap = 0; Pmin = &V1; Pmax = &V2;
  } else {
    Swap = 1; Pmin = &V2; Pmax = &V1;
  }

  if((Pmin->ScreenVertex.Y > ClipYMax) || (Pmax->ScreenVertex.Y < ClipYMin))
  {
    return FALSE;
  } else {
    if(Pmin->ScreenVertex.Y < ClipYMin)
    {
      // First Vertex clipped
      WhichClipped = Swap ^ 1;

      DOUBLE Delta = (DOUBLE)(ClipYMin - Pmin->ScreenVertex.Y) 
                       / (DOUBLE)(Pmax->ScreenVertex.Y - Pmin->ScreenVertex.Y);

      VC1.ScreenVertex.X = Pmin->ScreenVertex.X 
                + (INT)((Pmax->ScreenVertex.X - Pmin->ScreenVertex.X) * Delta);
      VC1.ScreenVertex.Y = ClipYMin;

      VC1.ObjectVertex.X = Pmin->ObjectVertex.X 
                + (Pmax->ObjectVertex.X - Pmin->ObjectVertex.X) * Delta;
      VC1.ObjectVertex.Y = Pmin->ObjectVertex.Y 
                + (Pmax->ObjectVertex.Y - Pmin->ObjectVertex.Y) * Delta;
      VC1.ObjectVertex.Z = Pmin->ObjectVertex.Z 
                + (Pmax->ObjectVertex.Z - Pmin->ObjectVertex.Z) * Delta;

      VC1.CameraVertex.X = Pmin->CameraVertex.X 
                + (Pmax->CameraVertex.X - Pmin->CameraVertex.X) * Delta;
      VC1.CameraVertex.Y = Pmin->CameraVertex.Y 
                + (Pmax->CameraVertex.Y - Pmin->CameraVertex.Y) * Delta;
      VC1.CameraVertex.Z = Pmin->CameraVertex.Z 
                + (Pmax->CameraVertex.Z - Pmin->CameraVertex.Z) * Delta;

      VC1.Normal.X = Pmin->Normal.X 
                + (Pmax->Normal.X - Pmin->Normal.X) * Delta;
      VC1.Normal.Y = Pmin->Normal.Y 
                + (Pmax->Normal.Y - Pmin->Normal.Y) * Delta;
      VC1.Normal.Z = Pmin->Normal.Z 
                + (Pmax->Normal.Z - Pmin->Normal.Z) * Delta;

      VC1.Color.R = Pmin->Color.R + (Pmax->Color.R - Pmin->Color.R) * Delta;
      VC1.Color.G = Pmin->Color.G + (Pmax->Color.G - Pmin->Color.G) * Delta;
      VC1.Color.B = Pmin->Color.B + (Pmax->Color.B - Pmin->Color.B) * Delta;

      VC1.I = Pmin->I + (Pmax->I - Pmin->I) * Delta;
      VC1.U = Pmin->U + (Pmax->U - Pmin->U) * Delta;
      VC1.V = Pmin->V + (Pmax->V - Pmin->V) * Delta;

      *Pmin = VC1;
    }

    if(Pmax->ScreenVertex.Y > ClipYMax)
    {
      // First Vertex clipped
      WhichClipped |= Swap & 1;

      DOUBLE Delta = (DOUBLE)(ClipYMax - Pmin->ScreenVertex.Y) 
                       / (DOUBLE)(Pmax->ScreenVertex.Y - Pmin->ScreenVertex.Y);

      VC2.ScreenVertex.X = Pmin->ScreenVertex.X 
                + (INT)((Pmax->ScreenVertex.X - Pmin->ScreenVertex.X) * Delta);
      VC2.ScreenVertex.Y = ClipYMax;

      VC2.ObjectVertex.X = Pmin->ObjectVertex.X 
                + (Pmax->ObjectVertex.X - Pmin->ObjectVertex.X) * Delta;
      VC2.ObjectVertex.Y = Pmin->ObjectVertex.Y 
                + (Pmax->ObjectVertex.Y - Pmin->ObjectVertex.Y) * Delta;
      VC2.ObjectVertex.Z = Pmin->ObjectVertex.Z 
                + (Pmax->ObjectVertex.Z - Pmin->ObjectVertex.Z) * Delta;

      VC2.CameraVertex.X = Pmin->CameraVertex.X 
                + (Pmax->CameraVertex.X - Pmin->CameraVertex.X) * Delta;
      VC2.CameraVertex.Y = Pmin->CameraVertex.Y 
                + (Pmax->CameraVertex.Y - Pmin->CameraVertex.Y) * Delta;
      VC2.CameraVertex.Z = Pmin->CameraVertex.Z 
                + (Pmax->CameraVertex.Z - Pmin->CameraVertex.Z) * Delta;

      VC2.Normal.X = Pmin->Normal.X 
                + (Pmax->Normal.X - Pmin->Normal.X) * Delta;
      VC2.Normal.Y = Pmin->Normal.Y 
                + (Pmax->Normal.Y - Pmin->Normal.Y) * Delta;
      VC2.Normal.Z = Pmin->Normal.Z 
                + (Pmax->Normal.Z - Pmin->Normal.Z) * Delta;

      VC2.Color.R = Pmin->Color.R + (Pmax->Color.R - Pmin->Color.R) * Delta;
      VC2.Color.G = Pmin->Color.G + (Pmax->Color.G - Pmin->Color.G) * Delta;
      VC2.Color.B = Pmin->Color.B + (Pmax->Color.B - Pmin->Color.B) * Delta;

      VC2.I = Pmin->I + (Pmax->I - Pmin->I) * Delta;
      VC2.U = Pmin->U + (Pmax->U - Pmin->U) * Delta;
      VC2.V = Pmin->V + (Pmax->V - Pmin->V) * Delta;

      *Pmax = VC2;
    }
  }
  return TRUE;
}

/*******************************************************
*   Function : VECBuffer::EdgeClippingY2               *
*------------------------------------------------------*
*                                                      *
*******************************************************/
BOOL VECBuffer::EdgeXClipping2(VECVertex &V1, VECVertex &V2)
{ 
  static VECVertex VC1, VC2;

  VECVertex *Pmin, *Pmax;

  INT Swap;

  WhichClipped = 0;

  if (V1.ScreenVertex.X < V2.ScreenVertex.X)
  {
    Swap = 0; Pmin = &V1; Pmax = &V2;
  } else {
    Swap = 1; Pmin = &V2; Pmax = &V1;
  }

  if((Pmin->ScreenVertex.X > ClipXMax) || (Pmax->ScreenVertex.X < ClipXMin))
  {
    return FALSE;
  } else {
    if(Pmin->ScreenVertex.X < ClipXMin)
    {
      // First Vertex clipped
      WhichClipped = Swap ^ 1;

      DOUBLE Delta = (DOUBLE)(ClipXMin - Pmin->ScreenVertex.X) 
                       / (DOUBLE)(Pmax->ScreenVertex.X - Pmin->ScreenVertex.X);

      VC1.ScreenVertex.X = ClipXMin;
      VC1.ScreenVertex.Y = Pmin->ScreenVertex.Y 
                + (INT)((Pmax->ScreenVertex.Y - Pmin->ScreenVertex.Y) * Delta);

      VC1.ObjectVertex.X = Pmin->ObjectVertex.X 
                + (Pmax->ObjectVertex.X - Pmin->ObjectVertex.X) * Delta;
      VC1.ObjectVertex.Y = Pmin->ObjectVertex.Y 
                + (Pmax->ObjectVertex.Y - Pmin->ObjectVertex.Y) * Delta;
      VC1.ObjectVertex.Z = Pmin->ObjectVertex.Z 
                + (Pmax->ObjectVertex.Z - Pmin->ObjectVertex.Z) * Delta;

      VC1.CameraVertex.X = Pmin->CameraVertex.X 
                + (Pmax->CameraVertex.X - Pmin->CameraVertex.X) * Delta;
      VC1.CameraVertex.Y = Pmin->CameraVertex.Y 
                + (Pmax->CameraVertex.Y - Pmin->CameraVertex.Y) * Delta;
      VC1.CameraVertex.Z = Pmin->CameraVertex.Z 
                + (Pmax->CameraVertex.Z - Pmin->CameraVertex.Z) * Delta;

      VC1.Normal.X = Pmin->Normal.X 
                + (Pmax->Normal.X - Pmin->Normal.X) * Delta;
      VC1.Normal.Y = Pmin->Normal.Y 
                + (Pmax->Normal.Y - Pmin->Normal.Y) * Delta;
      VC1.Normal.Z = Pmin->Normal.Z 
                + (Pmax->Normal.Z - Pmin->Normal.Z) * Delta;

      VC1.Color.R = Pmin->Color.R + (Pmax->Color.R - Pmin->Color.R) * Delta;
      VC1.Color.G = Pmin->Color.G + (Pmax->Color.G - Pmin->Color.G) * Delta;
      VC1.Color.B = Pmin->Color.B + (Pmax->Color.B - Pmin->Color.B) * Delta;

      VC1.I = Pmin->I + (Pmax->I - Pmin->I) * Delta;
      VC1.U = Pmin->U + (Pmax->U - Pmin->U) * Delta;
      VC1.V = Pmin->V + (Pmax->V - Pmin->V) * Delta;

      *Pmin = VC1;
    }

    if(Pmax->ScreenVertex.X > ClipXMax)
    {
      // First Vertex clipped
      WhichClipped |= Swap & 1;

      DOUBLE Delta = (DOUBLE)(ClipXMax - Pmin->ScreenVertex.X) 
                       / (DOUBLE)(Pmax->ScreenVertex.X - Pmin->ScreenVertex.X);

      VC2.ScreenVertex.X = ClipYMax;
      VC2.ScreenVertex.Y = Pmin->ScreenVertex.Y 
                + (INT)((Pmax->ScreenVertex.Y - Pmin->ScreenVertex.Y) * Delta);

      VC2.ObjectVertex.X = Pmin->ObjectVertex.X 
                + (Pmax->ObjectVertex.X - Pmin->ObjectVertex.X) * Delta;
      VC2.ObjectVertex.Y = Pmin->ObjectVertex.Y 
                + (Pmax->ObjectVertex.Y - Pmin->ObjectVertex.Y) * Delta;
      VC2.ObjectVertex.Z = Pmin->ObjectVertex.Z 
                + (Pmax->ObjectVertex.Z - Pmin->ObjectVertex.Z) * Delta;

      VC2.CameraVertex.X = Pmin->CameraVertex.X 
                + (Pmax->CameraVertex.X - Pmin->CameraVertex.X) * Delta;
      VC2.CameraVertex.Y = Pmin->CameraVertex.Y 
                + (Pmax->CameraVertex.Y - Pmin->CameraVertex.Y) * Delta;
      VC2.CameraVertex.Z = Pmin->CameraVertex.Z 
                + (Pmax->CameraVertex.Z - Pmin->CameraVertex.Z) * Delta;

      VC2.Normal.X = Pmin->Normal.X 
                + (Pmax->Normal.X - Pmin->Normal.X) * Delta;
      VC2.Normal.Y = Pmin->Normal.Y 
                + (Pmax->Normal.Y - Pmin->Normal.Y) * Delta;
      VC2.Normal.Z = Pmin->Normal.Z 
                + (Pmax->Normal.Z - Pmin->Normal.Z) * Delta;

      VC2.Color.R = Pmin->Color.R + (Pmax->Color.R - Pmin->Color.R) * Delta;
      VC2.Color.G = Pmin->Color.G + (Pmax->Color.G - Pmin->Color.G) * Delta;
      VC2.Color.B = Pmin->Color.B + (Pmax->Color.B - Pmin->Color.B) * Delta;

      VC2.I = Pmin->I + (Pmax->I - Pmin->I) * Delta;
      VC2.U = Pmin->U + (Pmax->U - Pmin->U) * Delta;
      VC2.V = Pmin->V + (Pmax->V - Pmin->V) * Delta;

      *Pmax = VC2;
    }
  }
  return TRUE;
}

