/***********************************************************************/
/*                                                                     */
/*  NAME     : Terry Fleury            PROGRAM : mp3                   */
/*  NET ID   : cs318ta1                COURSE  : CS318 - Fall 1999     */
/*  DUE DATE : October 22, 1999                                        */
/*  PURPOSE  : In this MP, we implement the Nicholl-Lee-Nicholl line   */
/*             clipping algorithm.  The user can enter up to a maximum */
/*             of 20 line segments on the screen.  The user can also   */
/*             enter a clipping rectangle that all lines will be       */
/*             clipped to.  For 1-unit grad students, we can also use  */
/*             the space bar to toggle rubberbanding when drawing the  */
/*             lines and clipping rectangle.                           */
/*  INPUT    : Left Mouse Button - designates first and second         */
/*                  endpoints of the lines                             */
/*             Right Mouse Button - designates two corners of the      */
/*                  clipping rectangle                                 */
/*             <Backspace> - clears all lines on the screen            */
/*             <Spacebar> - toggles rubberbanding when drawing         */
/*             "q" - quit program                                      */
/*                                                                     */
/***********************************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <math.h>
#include <float.h>  /* #include <values.h> on sparcs */
#include <GL/glut.h>

#define MAXPOINTS 40         /* Maximum number of lines = 20 */
#define EPSILON   0.000001   /* A small number */

/*********************/
/*  DATA STRUCTURES  */
/*********************/
typedef struct PointStruct { int X,Y; } PointType;
typedef enum AreaEnum { CENTER,LEFT,RIGHT,TOP,BOTTOM,
                        LOWLEFT,UPLEFT,UPRIGHT,LOWRIGHT } AreaType;

/***********************/
/* FUNCTION PROTOTYPES */
/***********************/
void Initialize(void);
void SetCameraPosition(void);
void UpdateDisplay(void);
void DrawOneLine(PointType,PointType);
void DrawLines(void);
void DrawRectangle(void);
void DrawClippedLines(void);
void MouseButtonPressed(int,int,int,int);
void MouseMovement(int,int);
void KeyPressed(unsigned char,int,int);
void ReshapeWindow(int,int);
void QuitProgram(void);
void PrintUsage(void);

/***********************/
/*   GLOBAL VARIABLES  */
/***********************/
int WinWidth = 600;          /* Width of the main window */
int WinHeight = 600;         /* Height of the main window */
int RubberbandMode = 0;      /* Use Rubberbanding for lines/rectangle */
int RectPointsEntered = 0;   /* Number of clipping rectangle points entered */
int PointsEntered = 0;       /* Number of endpoints entered so far */
PointType Points[MAXPOINTS]; /* Array to hold endpoints of the lines */
PointType Rectangle[2];      /* Array to hold two corners of clip rectangle */

/***********************************************************************/
/*  Initialize is called at program invocation and sets up the main    */
/*  window.                                                            */
/***********************************************************************/

void Initialize(void)
{
  /* Initialize the window and color mode */
  glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
  glutInitWindowSize(WinWidth,WinHeight);
  glutCreateWindow("CS318 MP3 - Solution");
  glDrawBuffer(GL_FRONT); /* Draw to the front - emulate single buffer */
  glPointSize(3.0);       /* Always draw "bigger" points */

  glClearColor(0.0,0.0,0.0,0.0);   /* Background is black */
 
  SetCameraPosition();
}

/***********************************************************************/
/*  SetCameraPosition is called by several procedures to move the      */
/*  camera to the correct viewing position, and then set GL_MODELVIEW  */
/*  to allow drawing of objects on the screen.                         */
/***********************************************************************/

void SetCameraPosition(void)
{
  /* Set the viewing transformation */
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluOrtho2D(0.0,(GLdouble)WinWidth+1.0,(GLdouble)WinHeight+1.0,0.0);
  glMatrixMode(GL_MODELVIEW);
}

/***********************************************************************/
/*  UpdateDisplay is called by the main event loop to refresh the      */
/*  contents of the display and to show the squares.                   */
/***********************************************************************/

void UpdateDisplay(void)
{
  glClear(GL_COLOR_BUFFER_BIT);   /* Clear the main window */

  DrawLines();
  DrawRectangle();
  DrawClippedLines();

  if (RubberbandMode)
    glutSwapBuffers();   /* Swap back and front window buffers */
  else
    glFlush();
}

/***********************************************************************/
/*  DrawOneLine is called by DrawLines to draw a line from Point1      */
/*  to Point2.                                                         */
/***********************************************************************/

void DrawOneLine(PointType Point1, PointType Point2)
{
  glBegin(GL_LINES);
    glVertex2i(Point1.X,Point1.Y);
    glVertex2i(Point2.X,Point2.Y);
  glEnd();
}

/***********************************************************************/
/*  DrawLines is called by UpdateDisplay to draw the lines stored in   */
/*  the Points array to the screen.                                    */
/***********************************************************************/

void DrawLines(void)
{
  int CurrPoint;       /* Counter for current point to be drawn */
  
  glColor3f(0.0,1.0,0.0);

  for (CurrPoint = 1; CurrPoint <= PointsEntered; CurrPoint++)
    {
      /* If we started entering a line by clicking just the first */
      /* vertex, then draw that vertex as a large point in green. */
      if ((PointsEntered%2 == 1) && (CurrPoint == PointsEntered))
        {
          /* Since arrays start at 0, subtract 1 from CurrPoint */
          glBegin(GL_POINTS);
            glVertex2i(Points[CurrPoint-1].X,Points[CurrPoint-1].Y);
          glEnd();
        }

      /* We draw the actual lines connecting the endpoints */
      /* if there are two points entered for that line.     */
      if (CurrPoint%2 == 0)
        DrawOneLine(Points[CurrPoint-2],Points[CurrPoint-1]);

      /* If we are in rubberband mode and the last vertex in the */
      /* array is all by itself, then draw a line segment from   */
      /* it to the current mouse position.                       */
      if ((RubberbandMode) && 
          (PointsEntered%2 == 1) && (CurrPoint == PointsEntered))
        DrawOneLine(Points[CurrPoint-1],Points[CurrPoint]);
    }
}

/***********************************************************************/
/*  DrawRectangle is called by UpdateDisplay to draw the rectangular   */
/*  clipping region.                                                   */
/***********************************************************************/

void DrawRectangle(void)
{
  /* Check to make sure we have a box to draw */
  if (RectPointsEntered > 0)
    {
      glColor3f(0.0,0.0,1.0);    /* Color is blue */

      /* If we have just the first corner of the clipping rectangle */
      /* entered, then draw that corner as a large point.           */
      if (RectPointsEntered == 1)
        { 
          glBegin(GL_POINTS);
            glVertex2i(Rectangle[0].X,Rectangle[0].Y);
          glEnd();
        }

      /* If we have two corners entered for the clipping rectangle, */
      /* OR if we have one corner entered and we are in rubberband  */
      /* mode, draw the clipping rectangle.  We can assume that     */
      /* Rectangle[0] is the upper left corner, and Rectangle[1] is */
      /* the lower right corner of the clipping rectangle.          */
      if ((RectPointsEntered == 2) || 
           ((RubberbandMode) && (RectPointsEntered == 1)))
        {
          glBegin(GL_LINE_LOOP);
            glVertex2i(Rectangle[0].X,Rectangle[0].Y);
            glVertex2i(Rectangle[0].X,Rectangle[1].Y);
            glVertex2i(Rectangle[1].X,Rectangle[1].Y);
            glVertex2i(Rectangle[1].X,Rectangle[0].Y);
          glEnd();
        }
    }
}

/***********************************************************************/
/*  GetSlope takes in a DeltaX and DeltaY and returns the slope.  It   */
/*  checks to see if DeltaX is zero.  If so, it returns MAXDOUBLE or   */
/*  -MAXDOUBLE depending on the direction of DeltaY.                   */
/***********************************************************************/

double GetSlope(int DeltaX, int DeltaY)
{
  double m;

  if (DeltaX == 0)
    {
      if (DeltaY >= 0)
        m = DBL_MAX;    /* MAXDOUBLE for the sparcs */
      else
        m = -DBL_MAX;   /* MAXDOUBLE for the sparcs */
    }
  else
    m = (double)DeltaY / (double)DeltaX;

  return m;
}

/***********************************************************************/
/*  PInCenter is called when the first endpoint "P" is inside the      */
/*  clipping rectangle.  If the second endpoint "Q" is outside the     */
/*  clipping rectangle, then we clip that line against the appropriate */
/*  edge.                                                              */
/***********************************************************************/

void PInCenter(PointType P, PointType Q)
{
  int MidX, MidY;
  int DeltaX, DeltaY;
  int DeltaXLT, DeltaYLT;
  int DeltaXLB, DeltaYLB;
  int DeltaXRT, DeltaYRT;
  int DeltaXRB, DeltaYRB;
  double m, mLT, mLB, mRT, mRB;
  
  /* Check to see if we need to clip against an edge */
  if ((Q.X < Rectangle[0].X) || (Q.X > Rectangle[1].X) ||
      (Q.Y < Rectangle[0].Y) || (Q.Y > Rectangle[1].Y)) 
    {
      DeltaX = Q.X - P.X;
      DeltaY = Q.Y - P.Y;
      DeltaXLT = DeltaXLB = Rectangle[0].X - P.X;
      DeltaYLT = DeltaYRT = Rectangle[1].Y - P.Y;
      DeltaXRT = DeltaXRB = Rectangle[1].X - P.X;
      DeltaYLB = DeltaYRB = Rectangle[0].Y - P.Y;
      m = GetSlope(DeltaX,DeltaY);
      mLT = GetSlope(DeltaXLT,DeltaYLT);
      mRT = GetSlope(DeltaXRT,DeltaYRT);
      mLB = GetSlope(DeltaXLB,DeltaYLB);
      mRB = GetSlope(DeltaXRB,DeltaYRB);

      if (mLT > FLT_MAX) /* Fix slope for left edges */ 
             /* MAXFLOAT on sparcs */
        mLT = -mLT;
      if (mLB < -FLT_MAX) /* MAXFLOAT on sparcs */
        mLB = -mLB;

      if ((((m >= 0) && (m <= mRT)) ||  /* Clip against right edge */
           ((m < 0) && (m > mRB))) &&
           (Q.X > Rectangle[1].X))
        {
          Q.X = Rectangle[1].X;
          Q.Y = P.Y + (int)(m * DeltaXRB);
        }
      else if ((((m < 0) && (m > mLT)) ||  /* Clip against left edge */
                ((m >= 0) && (m <= mLB))) &&
                (Q.X < Rectangle[0].X))
        {
          Q.X = Rectangle[0].X;
          Q.Y = P.Y + (int)(m * DeltaXLB);
        }
      else if ((((m < 0) && (m <= mLT)) ||  /* Clip against top edge */
                ((m >= 0) && (m >= mRT))) &&
                (Q.Y > Rectangle[1].Y))
        {
          Q.X = P.X + (int)(DeltaYLT/m);
          Q.Y = Rectangle[1].Y;
        }
      else                                 /* Clip against bottom edge */
        {
          Q.X = P.X + (int)(DeltaYLB/m);
          Q.Y = Rectangle[0].Y;
        }
    }

  DrawOneLine(P,Q);
}

/***********************************************************************/
/*  PInCorner is called when the first endpoint "P" is in one of the   */
/*  four corner regions (LOWLEFT,UPLEFT,LOWRIGHT,UPRIGHT).  It takes   */
/*  in the two endpoints for the line and which corner P is in.  If    */
/*  P is not LOWLEFT, it "flips" both P and Q such that P is in the    */
/*  lower left corner.  Then, the clipping is performed.               */
/***********************************************************************/

void PInCorner(PointType P, PointType Q, AreaType Corner)
{
  int MidX, MidY;
  int DeltaX, DeltaY;
  int DeltaXLT, DeltaYLT;
  int DeltaXLB, DeltaYLB;
  int DeltaXRT, DeltaYRT;
  int DeltaXRB, DeltaYRB;
  double m, mLT, mLB, mRT, mRB;

  /* Calculate the midpoint of the clipping rectangle */
  MidX = (Rectangle[0].X + Rectangle[1].X) / 2;
  MidY = (Rectangle[0].Y + Rectangle[1].Y) / 2;

  /* If the corner is not the Lower Left, then do some "flips" about  */
  /* the center of the clipping region to make it the LOWLEFT corner. */
  if ((Corner == UPLEFT) || (Corner == UPRIGHT))
    {
      P.Y = 2 * MidY - P.Y;
      Q.Y = 2 * MidY - Q.Y;
    }

  if ((Corner == LOWRIGHT) || (Corner == UPRIGHT))
    {
      P.X = 2 * MidX - P.X;
      Q.X = 2 * MidX - Q.X;
    }

  /* Check to see if we trivially reject the line */
  if ((Q.X < Rectangle[0].X) || (Q.Y < Rectangle[0].Y))
    return;

  DeltaX = Q.X - P.X;
  DeltaY = Q.Y - P.Y;
  DeltaXLT = Rectangle[0].X - P.X;
  DeltaYLT = Rectangle[1].Y - P.Y;
  m = GetSlope(DeltaX,DeltaY);
  mLT = GetSlope(DeltaXLT,DeltaYLT);

  if (m > mLT)  /* Passes left of LT corner */
    return;

  if (fabs(m - mLT) < EPSILON)  /* LT corner intersection */
    {
      P.X = Q.X = Rectangle[0].X;
      P.Y = Q.Y = Rectangle[1].Y;
    }
  else
    {
      DeltaXLB = DeltaXLT;   /* Redundant */
      DeltaYLB = Rectangle[0].Y - P.Y;
      mLB = GetSlope(DeltaXLB,DeltaYLB);
      DeltaXRT = Rectangle[1].X - P.X;
      DeltaYRT = DeltaYLT;   /* Redundant */
      mRT = GetSlope(DeltaXRT,DeltaYRT);

      if ((mLB < m) && (m < mLT))    /* Clip Left Edge */
        {
          if ((mRT < mLB) ||            /* Clip Top Edge */
              ((mRT >= mLB) && (m > mRT)))
            {
              if (Q.Y > Rectangle[1].Y)
                {
                  Q.X = P.X + (int)(DeltaYLT/m);
                  Q.Y = Rectangle[1].Y;
                }
            }
          else                         /* Clip Right Edge */
            {
              if (Q.X > Rectangle[1].X)
                {
                  Q.Y = P.Y + (int)(m * DeltaXRT);
                  Q.X = Rectangle[1].X;
                }
            }
          P.Y += (int)(m * DeltaXLT);
          P.X = Rectangle[0].X;
        }
      else
        {
          if ((mRT < m) && (m <= mLB))    /* Bottom and Top */
            {
              if (Q.Y > Rectangle[1].Y)
                {
                  Q.X = P.X + (int)(DeltaYRT/m);
                  Q.Y = Rectangle[1].Y;
                }
              P.X += (int)(DeltaYLB/m);
              P.Y = Rectangle[0].Y;
            }
          else
            {
              DeltaXRB = DeltaXRT;   /* Redundant */
              DeltaYRB = DeltaYLB;   /* Redundant */
              mRB = GetSlope(DeltaXRB,DeltaYRB);
              if ((mRB < m) && (m < mRT))  /* Bottom and Right */
                {
                  if (Q.X > Rectangle[1].X)
                    {
                      Q.Y = P.Y + (int)(m * DeltaXRB);
                      Q.X = Rectangle[1].X;
                    }
                  P.X += (int)(DeltaYRB/m);
                  P.Y = Rectangle[0].Y;
                }
              else if (fabs(m - mRB) < EPSILON)  /* Right Bottom corner */
                {
                  P.X = Q.X = Rectangle[1].X;
                  P.Y = Q.Y = Rectangle[0].Y;
                }
              else if (m < mRB)    /* Passes below Right Bottom */
                return;
            }
        }
    }

  /* If we had to "flip" the points to the LOWLEFT corner, */
  /* "flip" them back to their original corners.           */
  if ((Corner == UPLEFT) || (Corner == UPRIGHT))
    {
      P.Y = 2 * MidY - P.Y;
      Q.Y = 2 * MidY - Q.Y;
    }

  if ((Corner == LOWRIGHT) || (Corner == UPRIGHT))
    {
      P.X = 2 * MidX - P.X;
      Q.X = 2 * MidX - Q.X;
    }

  /* Check to see if we trivially reject the line */
  DrawOneLine(P,Q);
}

/***********************************************************************/
/*  PInLREdge is called when the first endpoint "P" is in either the   */
/*  LEFT or RIGHT edges.  If P is to the RIGHT of the clipping         */
/*  rectangle, then both P and Q are "flipped" so that P is to the     */
/*  left of the clipping rectangle.  Then, the clipping is performed.  */
/***********************************************************************/

void PInLREdge(PointType P, PointType Q, AreaType Edge)
{
  int MidX;
  int DeltaX, DeltaY;
  int DeltaXLT, DeltaYLT;
  int DeltaXLB, DeltaYLB;
  int DeltaXRT, DeltaYRT;
  int DeltaXRB, DeltaYRB;
  double m, mLT, mLB, mRT, mRB;
  
  /* If the edge is not the Left Edge, then "flip" about          */
  /* the center of the clipping region to make it the LEFT edge.  */
  if (Edge == RIGHT)
    {
      /* Calculate the midpoint of the clipping rectangle */
      MidX = (Rectangle[0].X + Rectangle[1].X) / 2;
      P.X = 2 * MidX - P.X;
      Q.X = 2 * MidX - Q.X;
    }

  /* Check to see if we trivially reject the line */
  if (Q.X < Rectangle[0].X)
    return;

  DeltaX = Q.X - P.X;
  DeltaY = Q.Y - P.Y;
  DeltaXLT = Rectangle[0].X - P.X;
  DeltaYLT = Rectangle[1].Y - P.Y;
  DeltaXLB = DeltaXLT;   /* Redundant */
  DeltaYLB = Rectangle[0].Y - P.Y;
  m = GetSlope(DeltaX,DeltaY);
  mLT = GetSlope(DeltaXLT,DeltaYLT);
  mLB = GetSlope(DeltaXLB,DeltaYLB);

  if ((m > mLT) || (m < mLB))  /* Passes above LT corner or below LB corner */
    return;

  if (fabs(m - mLT) < EPSILON)       /* LT corner intersection */
    {
      P.X = Rectangle[0].X; 
      P.Y = Rectangle[1].Y;
      if (fabs(m) < EPSILON)         /* Horizontal line */
        {
          if (Q.X > Rectangle[1].X)
            {
              Q.X = Rectangle[1].X;
              Q.Y = Rectangle[1].Y;
            }
        }
      else
        {
          Q.X = Rectangle[0].X;
          Q.Y = Rectangle[1].Y;
        }
    }
  else if (fabs(m - mLB) < EPSILON)  /* LB corner intersection */
    {
      P.X = Rectangle[0].X; 
      P.Y = Rectangle[0].Y;
      if (fabs(m) < EPSILON)         /* Horizontal line */
        {
          if (Q.X > Rectangle[1].X)
            {
              Q.X = Rectangle[1].X;
              Q.Y = Rectangle[0].Y;
            }
        }
      else
        {
          Q.X = Rectangle[0].X;
          Q.Y = Rectangle[0].Y;
        }
    }
  else
    {
      DeltaXRT = Rectangle[1].X - P.X;
      DeltaYRT = DeltaYLT;   /* Redundant */
      mRT = GetSlope(DeltaXRT,DeltaYRT);
      if ((m < mLT) && (m >= mRT))  /* Might intersect top edge */
        {
          if (Q.Y > Rectangle[1].Y)
            {
              Q.X = P.X + (int)(DeltaYLT/m);
              Q.Y = Rectangle[1].Y;
            }
          P.X = Rectangle[0].X;
          P.Y += (int)(m * DeltaXLT);
        }
      else
        {
          DeltaXRB = DeltaXRT;   /* Redundant */
          DeltaYRB = DeltaYLB;   /* Redundant */
          mRB = GetSlope(DeltaXRB,DeltaYRB);
          if ((m < mRT) && (m > mRB))  /* Might intersect right edge */
            {
              if (Q.X > Rectangle[1].X)
                {
                  Q.Y = P.Y + (int)(m * DeltaXRB);
                  Q.X = Rectangle[1].X;
                }
              P.X = Rectangle[0].X;
              P.Y += (int)(m * DeltaXLT);
            }
          else   /* Might intersect bottom edge */
            {
              if (Q.Y < Rectangle[0].Y)
                {
                  Q.X = P.X + (int)(DeltaYLB/m);
                  Q.Y = Rectangle[0].Y;
                }
              P.X = Rectangle[0].X;
              P.Y += (int)(m * DeltaXLB);
            }
        }
    }

  /* If we had to "flip" the points to the LEFT edge, */
  /* "flip" them back to their original edges.        */
  if (Edge == RIGHT)
    {
      P.X = 2 * MidX - P.X;
      Q.X = 2 * MidX - Q.X;
    }

  DrawOneLine(P,Q);
}

/***********************************************************************/
/*  PInTBEdge is called when the first endpoint "P" is in either the   */
/*  TOP or BOTTOM edges.  If P is to the TOP of the clipping           */
/*  rectangle, then both P and Q are "flipped" so that P is "below"    */
/*  the clipping rectangle.  Then, the clipping is performed.          */
/***********************************************************************/

void PInTBEdge(PointType P, PointType Q, AreaType Edge)
{
  int MidY;
  int DeltaX, DeltaY;
  int DeltaXLT, DeltaYLT;
  int DeltaXLB, DeltaYLB;
  int DeltaXRT, DeltaYRT;
  int DeltaXRB, DeltaYRB;
  double m, mLT, mLB, mRT, mRB;
  
  /* If the edge is not the Bottom Edge, then "flip" about         */
  /* the center of the clipping region to make it the BOTTOM edge. */
  if (Edge == TOP)
    {
      /* Calculate the midpoint of the clipping rectangle */
      MidY = (Rectangle[0].Y + Rectangle[1].Y) / 2;
      P.Y = 2 * MidY - P.Y;
      Q.Y = 2 * MidY - Q.Y;
    }

  /* Check to see if we trivially reject the line */
  if (Q.Y < Rectangle[0].Y)
    return;

  DeltaX = Q.X - P.X;
  DeltaY = Q.Y - P.Y;
  DeltaXLB = Rectangle[0].X - P.X;
  DeltaYLB = Rectangle[0].Y - P.Y;
  DeltaXRB = Rectangle[1].X - P.X;
  DeltaYRB = DeltaYLB;  /* Redundant */
  m = GetSlope(DeltaX,DeltaY);
  mLB = GetSlope(DeltaXLB,DeltaYLB);
  mRB = GetSlope(DeltaXRB,DeltaYRB);

  if (((m < 0) && (m > mLB)) || /* Passes left of LB corner OR */
      ((m >= 0) && (m < mRB)))  /* Passes right of RB corner   */
    return;

  if (fabs(m - mLB) < EPSILON)       /* LB corner intersection */
    {
      P.X = Rectangle[0].X; 
      P.Y = Rectangle[0].Y;
      if (fabs(m - DBL_MAX) < EPSILON) /* Vertical line */ 
                /* MAXDOUBLE on sparcs */
        {
          if (Q.Y > Rectangle[1].Y)
            {
              Q.X = Rectangle[0].X;
              Q.Y = Rectangle[1].Y;
            }
        }
      else
        {
          Q.X = Rectangle[0].X;
          Q.Y = Rectangle[0].Y;
        }
    }
  else if (fabs(m - mRB) < EPSILON)  /* RB corner intersection */
    {
      P.X = Rectangle[1].X; 
      P.Y = Rectangle[0].Y;
      if (fabs(m - DBL_MAX) < EPSILON)    /* Vertical line */
                /* MAXDOUBLE on sparcs */
        {
          if (Q.Y > Rectangle[1].Y)
            {
              Q.X = Rectangle[1].X;
              Q.Y = Rectangle[1].Y;
            }
        }
      else
        {
          Q.X = Rectangle[1].X;
          Q.Y = Rectangle[0].Y;
        }
    }
  else
    {
      DeltaXLT = DeltaXLB;  /* Redundant */
      DeltaYLT = Rectangle[1].Y - P.Y;
      mLT = GetSlope(DeltaXLT,DeltaYLT);
      if ((m < mLB) && (m > mLT))   /* Might intersect the left edge */
        {
          if (Q.X < Rectangle[0].X)
            {
              Q.X = Rectangle[0].X;
              Q.Y = P.Y + (int)(m * DeltaXLB);
            }
          P.X += (int)(DeltaYLB/m);
          P.Y = Rectangle[0].Y;
        }
      else
        {
          DeltaXRT = DeltaXRB;   /* Redundant */
          DeltaYRT = DeltaYLT;   /* Redundant */
          mRT = GetSlope(DeltaXRT,DeltaYRT);
          if (((m < 0) && (m < mLT)) || 
              ((m >= 0) && (m > mRT)))   /* Might intersect top edge */
            {
              if (Q.Y > Rectangle[1].Y)
                {
                  if (m < 0)
                    Q.X = P.X + (int)(DeltaYLT/m);
                  else
                    Q.X = P.X + (int)(DeltaYRT/m);
                  Q.Y = Rectangle[1].Y;
                }
              P.X += (int)(DeltaYLB/m);
              P.Y = Rectangle[0].Y;
            }
          else   /* Might intersect right edge */
            {
              if (Q.X > Rectangle[1].X)
                {
                  Q.X = Rectangle[1].X;
                  Q.Y = P.Y + (int)(m * DeltaXRB);
                }
              P.X += (int)(DeltaYRB/m);
              P.Y = Rectangle[0].Y;
            }
        }
    }

  /* If we had to "flip" the points to the BOTTOM edge, */
  /* "flip" them back to their original edges.          */
  if (Edge == TOP)
    {
      P.Y = 2 * MidY - P.Y;
      Q.Y = 2 * MidY - Q.Y;
    }

  DrawOneLine(P,Q);
}

/***********************************************************************/
/*  DrawClippedLines is where we implement the Nicholl-Lee-Nicholl     */
/*  line clipping algorithm.  For each complete line segment in our    */
/*  list, we clip it against the clipping rectangle, and draw the      */
/*  resulting clipped line segment.                                    */
/***********************************************************************/

void DrawClippedLines(void)
{
  int CurrPoint;       /* Counter for current point to be drawn */
  PointType P,Q;

  /* Make sure we have some lines entered and that we  */
  /* have completely specified the clipping rectangle. */
  if ((RectPointsEntered != 2) || (PointsEntered < 2))
    return;

  glColor3f(1.0,0.0,0.0);    /* Color is red */

  for (CurrPoint = 2; CurrPoint <= PointsEntered; CurrPoint+=2)
    {
      P = Points[CurrPoint-2];
      Q = Points[CurrPoint-1];

      /* Check to see in which of the 9 regions P resides */

      /* Is P inside the clip rectangle? */
      if ((P.X >= Rectangle[0].X) && (P.X <= Rectangle[1].X) &&
          (P.Y >= Rectangle[0].Y) && (P.Y <= Rectangle[1].Y))
        PInCenter(P,Q);
      /* Is P to the left (edge) of the clip rectangle? */
      else if ((P.X < Rectangle[0].X) &&
               (P.Y >= Rectangle[0].Y) && (P.Y <= Rectangle[1].Y))
        PInLREdge(P,Q,LEFT);
      /* Is P to the right (edge) of the clip rectangle? */
      else if ((P.X > Rectangle[1].X) &&
               (P.Y >= Rectangle[0].Y) && (P.Y <= Rectangle[1].Y))
        PInLREdge(P,Q,RIGHT);
      /* Is P "below" (the edge) of the clip rectangle? */
      else if ((P.X >= Rectangle[0].X) && (P.X <= Rectangle[1].X) &&
               (P.Y < Rectangle[0].Y))
        PInTBEdge(P,Q,BOTTOM);
      /* Is P "above" (the edge) of the clip rectangle? */
      else if ((P.X >= Rectangle[0].X) && (P.X <= Rectangle[1].X) &&
               (P.Y > Rectangle[1].Y))
        PInTBEdge(P,Q,TOP);
      /* Is P to the left and "below" the clip rectangle? */
      else if ((P.X < Rectangle[0].X) && (P.Y < Rectangle[0].Y))
        PInCorner(P,Q,LOWLEFT);
      /* Is P to the left and "above" the clip rectangle? */
      else if ((P.X < Rectangle[0].X) && (P.Y > Rectangle[1].Y))
        PInCorner(P,Q,UPLEFT);
      /* Is P to the right and "below" the clip rectangle? */
      else if ((P.X > Rectangle[1].X) && (P.Y < Rectangle[0].Y))
        PInCorner(P,Q,LOWRIGHT);
      /* Is P to the right and "above" the clip rectangle? */
      else if ((P.X > Rectangle[1].X) && (P.Y > Rectangle[1].Y))
        PInCorner(P,Q,UPRIGHT);
    }
}

/***********************************************************************/
/*  MouseButtonPressed is the callback function for when the a mouse   *
/*  button is pressed.  If the Left Mouse Button is pressed, then      */
/*  the (x,y) position is saved as a line endpoint.  If the Right      */
/*  Mouse Button is pressed, then we enter a corner of the clipping    */
/*  rectangle.                                                         */
/***********************************************************************/

void MouseButtonPressed(int Button, int State, int X, int Y)
{
  if (State == GLUT_DOWN)
    {
      if (Button == GLUT_LEFT_BUTTON)
        {
          /* Check to see if we are entering points of the clipping    */
          /* rectangle.  If so, then don't enter line vertices.        */
          if (RectPointsEntered == 1)
            return;

          /* Make sure we haven't entered the maximum number of points */
          if (PointsEntered < MAXPOINTS)
            {
              PointsEntered++;
              /* Array starts at 0, so we need to subtract 1 */
              Points[PointsEntered-1].X = X;
              Points[PointsEntered-1].Y = Y;

              /* For rubberband mode, if we have a single endpoint */
              /* as the last point in our vertex array, then set   */
              /* the next endpoint to be the same point.           */
              if (PointsEntered%2 == 1)
                Points[PointsEntered] = Points[PointsEntered-1];
            }
        }
      else if (Button == GLUT_RIGHT_BUTTON)
        {
          /* Check to see if we have set the first vertex of a line */
          /* segment. If so, then don't let the user enter points   */
          /* for the clipping rectangle.                            */
          if (PointsEntered%2 == 1)
            return;

          /* Check to see if either neither or both points are defined. */
          if (RectPointsEntered != 1)
            {
              Rectangle[0].X = X;
              Rectangle[0].Y = Y;
              Rectangle[1] = Rectangle[0];
              RectPointsEntered = 1;
            }
          else   /* The first point has been entered.  Set the second point. */
            {
              /* Make sure that X1 < X2 */
              if (X < Rectangle[0].X)
                { /* Whoops!  We need to swap the two X values */
                  Rectangle[1].X = Rectangle[0].X;
                  Rectangle[0].X = X;
                }
              else /* We are okay - set the second X value */
                Rectangle[1].X = X;

              /* Make sure that Y1 < Y2 */
              if (Y < Rectangle[0].Y)
                { /* Whoops!  We need to swap the two Y values */
                  Rectangle[1].Y = Rectangle[0].Y;
                  Rectangle[0].Y = Y;
                }
              else /* We are okay - set the second Y value */
                Rectangle[1].Y = Y;

              RectPointsEntered = 2;
            }
        }
      glutPostRedisplay();
    }
}

/***********************************************************************/
/*  MouseMovement is called when we are in "rubberband mode" to track  */
/*  the mouse movement.  If we are entering a line (meaning we have    */
/*  entered the first vertex of a line, but not the second), then we   */
/*  want the line to be rubberbanded.  If we are entering the clipping */
/*  rectangle (meaning we have entered the first corner of the         */
/*  rectangle, but not the second), then we want the clipping          */
/*  rectangle to be rubberbanded.                                      */
/***********************************************************************/

void MouseMovement(int X, int Y)
{
  /* Check if we are entering a line segment */
  if (PointsEntered % 2 == 1)
    {
      Points[PointsEntered].X = X;
      Points[PointsEntered].Y = Y;
      glutPostRedisplay();
    }
  /* Otherwise, check to see if we are entering the clipping rectangle */
  else if (RectPointsEntered == 1)
    {
      Rectangle[1].X = X;
      Rectangle[1].Y = Y;
      glutPostRedisplay();
    }
}

/***********************************************************************/
/*  KeyPressed is called when a key is press on the keyboard.  If a    */
/*  'q' is pressed (lower or upper case), the program exits.  If the   */
/*  spacebar is pressed, then we toggle the Rubberband Mode for when   */
/*  entering lines and the clipping rectangle (1-unit grad students    */
/*  only).  Initially Rubberbanding is turned off.  If the backspace   */
/*  key is pressed, then we clear all lines and endpoints.             */
/***********************************************************************/

void KeyPressed(unsigned char Key, int X, int Y)
{
  if (toupper(Key) == 'Q')
    QuitProgram();
  else if (Key == ' ')
    {
      RubberbandMode = !RubberbandMode;
      if (RubberbandMode)
        {
          glutPassiveMotionFunc(MouseMovement);
          glutMotionFunc(MouseMovement);
          glDrawBuffer(GL_BACK);
          MouseMovement(X,Y);
        }
      else
        {
          glutPassiveMotionFunc(NULL);
          glutMotionFunc(NULL);
          glDrawBuffer(GL_FRONT);
          glutPostRedisplay();
        }
    }
  else if (Key == '\b')
    {
      PointsEntered = 0;
      glutPostRedisplay();
    }
}

/***********************************************************************/
/*  ReshapeWindow is called if the main window needs to be resized.    */
/***********************************************************************/

void ReshapeWindow(int Width, int Height)
{
  WinWidth = Width;   /* Update the global WinWidth/WinHeight variables */
  WinHeight = Height;
  SetCameraPosition();    /* Update the camera view to be full screen */
  glViewport(0,0,WinWidth,WinHeight);
}

/***********************************************************************/
/*  QuitProgram is called to exit gracefully.                          */
/***********************************************************************/

void QuitProgram(void)
{
  exit(0);
}

/***********************************************************************/
/*  PrintUsage is called at program startup to print out a short       */
/*  help message on how to use the program.                            */
/***********************************************************************/

void PrintUsage(void)
{
  printf("CS318 - MP3 - Nicholl-Lee-Nicholl Clipping Algorithm\n");
  printf("INPUTS:\n");
  printf("Left Mouse Button  - Enter up to 40 Endpoints for 20 Lines\n");
  printf("Right Mouse Button - Enter 2 Corners of Clipping Rectangle\n");
  printf("<Backspace>        - Clears all Endpoints and Lines\n");
  printf("<Spacebar>         - Toggle Rubberband Mode (1-unit only)\n");
  printf("'q' or 'Q'         - Quit Program\n");
  fflush(NULL);   /* Make sure output shows up */
}


/**************************/
/*   BEGIN MAIN PROGRAM   */
/**************************/

int main(int argc, char** argv)
{
  glutInit(&argc,argv);
  
  PrintUsage();

  Initialize();    /* Set up main window and variables */
  
  /* Set the Mouse / Keyboard / Reshape / Display callbacks */
  glutKeyboardFunc(KeyPressed);
  glutMouseFunc(MouseButtonPressed);
  glutReshapeFunc(ReshapeWindow);
  glutDisplayFunc(UpdateDisplay);
  glutMainLoop();
  
  return 0;   /* Required by ANSI C */
}

/**************************/
/*    END MAIN PROGRAM    */
/**************************/


