/* Affichage d'un segment de droite                */
/* par algorithme de Bresenham                     */
/* Utilisation d'une variante de cet algorithme    */
/* pour implanter le remplissage d'un triangle 2D  */
/*                                                 */
/* Auteur: Nicolas JANEY                           */
/* nicolas.janey@univ-fcomte.fr                    */
/* Avril 2020                                      */

#include <stdlib.h>
#include <stdio.h>

#include <GL/glut.h>
#include <GL/gl.h>
#include <GL/glu.h>

/* Variables et constantes globales             */

static const float blanc[]        = { 1.0F,1.0F,1.0F,1.0F };
static const float jaune[]        = { 1.0F,1.0F,0.0F,1.0F };
static const float cyan[]         = { 0.0F,1.0F,1.0F,1.0F };
static const float magenta[]      = { 1.0F,0.0F,1.0F,1.0F };
static const float rouge[]        = { 1.0F,0.0F,0.0F,1.0F };
static const float vert[]         = { 0.0F,0.7F,0.0F,1.0F };
static const float bleu[]         = { 0.0F,0.0F,1.0F,1.0F };
static const float cyanFonce[]    = { 0.0F,0.8F,0.8F,1.0F };
static const float magentaFonce[] = { 0.8F,0.0F,0.8F,1.0F };

static int taillePixel = 16;
static int aff = 0;

/* Affichage d'un carre materialisant un pixel        */
/* place en coordonnees (x,y).                        */
/* Le pixel de coordonnees (0,0) est en bas a gauche. */
/* L'axe x est oriente vers la droite.                */
/* L'axe y est oriente vers le haut.                  */

static void pixel(int x,int y) {
  glBegin(GL_QUADS);
  glVertex2i(5+x*taillePixel,5+y*taillePixel);
  glVertex2i(5+x*taillePixel+taillePixel-1,5+y*taillePixel);
  glVertex2i(5+x*taillePixel+taillePixel-1,5+y*taillePixel+taillePixel-1);
  glVertex2i(5+x*taillePixel,5+y*taillePixel+taillePixel-1);
  glEnd();
}

/* Rasterisation du cote de sommet initial (xi,yi)    */
/* et de sommet final (xf,yf) en affichant            */
/* seulement les pixels correspondant au dernier x    */
/* calcule pour chaque y                              */

static void coteRasterise(int xi,int yi,int xf,int yf) {
  int x = xi;
  int y = yi;
  int dx = xf - xi;
  int dy = yf - yi;
  int xinc = ( dx > 0 ) ? 1 : -1;
  int yinc = ( dy > 0 ) ? 1 : -1;
  dx = abs(dx);
  dy = abs(dy);
  if ( dx > dy ) {
    int cumul = dx >> 1;
    for ( int i = 1 ; i <= dx ; i++ ) {
      x += xinc;
      cumul += dy;
      if ( cumul >= dx ) {
        pixel(x-xinc,y);
        cumul -= dx;
        y += yinc; } }
    pixel(x,y); }
    else {
    pixel(x,y);
    int cumul = dy >> 1;
    for ( int i = 1 ; i <= dy ; i++ ) {
      y += yinc;
      cumul += dx;
      if ( cumul >= dy ) {
        cumul -= dy;
        x += xinc; }
      pixel(x,y); } }
}

////////////////////////////////////////////////////////

/* Trace d'un segment de droite entre le pixel        */
/* de coordonnees (xi,yi) et le pixel                 */
/* de coordonnees (xf,yf) au moyen de l'algorithme    */
/* de Bresenham                                       */

static void ligne(int xi,int yi,int xf,int yf) {
  int x = xi;
  int y = yi;
  int dx = xf - xi;
  int dy = yf - yi;
  int xinc = ( dx > 0 ) ? 1 : -1;
  int yinc = ( dy > 0 ) ? 1 : -1;
  dx = abs(dx);
  dy = abs(dy);
  pixel(x,y);
  if ( dx > dy ) {
    int cumul = dx >> 1;
    for ( int i = 1 ; i <= dx ; i++ ) {
      x += xinc;
      cumul += dy;
      if ( cumul >= dx ) {
        cumul -= dx;
        y += yinc; }
      pixel(x,y); } }
    else {
    int cumul = dy >> 1;
    for ( int i = 1 ; i <= dy ; i++ ) {
      y += yinc;
      cumul += dx;
      if ( cumul >= dy ) {
        cumul -= dy;
        x += xinc; }
      pixel(x,y); } }
}

/* Rasterisation du cote de sommet initial (xi,yi)    */
/* et de sommet final (xf,yf) en stockant             */
/* dans le tableau px le dernier x                    */
/* calcule pour chaque y                              */

static void cote(int xi,int yi,int xf,int yf,int *px) {
  int x = xi;
  int y = yi;
  int dx = xf - xi;
  int dy = yf - yi;
  int xinc = ( dx > 0 ) ? 1 : -1;
  int yinc = ( dy > 0 ) ? 1 : -1;
  dx = abs(dx);
  dy = abs(dy);
  px[y] = x;
  if ( dx > dy ) {
    int cumul = dx >> 1;
    for ( int i = 1 ; i <= dx ; i++ ) {
      x += xinc;
      cumul += dy;
      if ( cumul >= dx ) {
        cumul -= dx;
        y += yinc; }
      px[y] = x; } }
    else {
    int cumul = dy >> 1;
    for ( int i = 1 ; i <= dy ; i++ ) {
      y += yinc;
      cumul += dx;
      if ( cumul >= dy ) {
        cumul -= dy;
        x += xinc; }
      px[y] = x; } }
}

/* Rasterisation d'un triangle 2D                     */
/* Les coordonnees des trois sommets sont (x1,y1),    */
/* (x2,y2) et (x3,y3) et doivent verifier y1<=y2<=y3. */
/*                                                    */
/* Solution imparfaite car ne gerant pas le probleme  */
/* des cotes ou plus d'un pixel existe pour chaque y  */
/* avec l'obligation de choisir un et un seul         */
/* de ces pixels par y                                */
/* Consequences possibles :                           */
/* Non affichage des pixels correspondant aux sommets */
/* du triangle                                        */
/* Non affichage sur les cotes de pixels qui auraient */
/* ete affiches si les cotes avaient ete rasterises   */
/* par affichage de segments de droite rasterises.    */
/* Apparition de trous entre deux facettes adjacentes */
/* car des pixels peuvent ne pas etre allumes         */
/* a la limite entre les deux facettes car allumes    */
/* pour aucune des deux facettes.                     */

static void triangle(int x1,int y1,int x2,int y2,int x3,int y3) {
  int xd[4096];
  int xg[4096];
  if ( (x2-x1)*(y3-y1)-(x3-x1)*(y2-y1) > 0 ) {
    cote(x1,y1,x2,y2,xd);
    cote(x2,y2,x3,y3,xd);
    cote(x1,y1,x3,y3,xg); }
    else {
    cote(x1,y1,x2,y2,xg);
    cote(x2,y2,x3,y3,xg);
    cote(x1,y1,x3,y3,xd); }
  for ( int y = y1 ; y <= y3 ; y++ ) {
    for ( int x = xg[y] ; x <= xd[y] ; x++ ) {
      pixel(x,y); } }
} 

////////////////////////////////////////////////////////

/* Affichage d'un quadrillage mimant un ecran bitmap  */
/* Les pixels sont carres et ont taillePixel pixels   */
/* de cote.                                           */

static void quadrillage(void) {
  int tx = (glutGet(GLUT_WINDOW_WIDTH)-10)/taillePixel;
  int ty = (glutGet(GLUT_WINDOW_HEIGHT)-10)/taillePixel;
  glBegin(GL_LINES);
  for ( int x = 0 ; x <= tx ; x++ ) {
    glVertex2i(5+x*taillePixel,5);
    glVertex2i(5+x*taillePixel,5+ty*taillePixel); }
  for ( int y = 0 ; y <= ty ; y++ ) {
    glVertex2i(5,5+y*taillePixel);
    glVertex2i(5+tx*taillePixel,5+y*taillePixel); }
  glEnd();
}

/* Affichage du triangle reel defini par les sommets  */
/* de coordonnees (x1,y1), (x2,y2) et (x3,y3)         */

static void triangleReel(int x1,int y1,int x2,int y2,int x3,int y3) {
  glBegin(GL_TRIANGLES);
  glVertex2i(5+x1*taillePixel+taillePixel/2,5+y1*taillePixel+taillePixel/2);
  glVertex2i(5+x2*taillePixel+taillePixel/2,5+y2*taillePixel+taillePixel/2);
  glVertex2i(5+x3*taillePixel+taillePixel/2,5+y3*taillePixel+taillePixel/2);
  glEnd();
}

/* Affichage des cotes rasterises du triangle défini  */
/* par les sommets de coordonnees                     */
/* (x1,y1), (x2,y2) et (x3,y3)                        */

static void triangleCotesSegments(int x1,int y1,int x2,int y2,int x3,int y3) {
  ligne(x1,y1,x2,y2);
  ligne(x2,y2,x3,y3);
  ligne(x3,y3,x1,y1);
}

/* Affichage des cotes rasterises du triangle défini  */
/* par les sommets de coordonnees                     */
/* (x1,y1), (x2,y2) et (x3,y3)                        */

static void triangleCotesRasterises(int x1,int y1,int x2,int y2,int x3,int y3) {
  if ( (x2-x1)*(y3-y1)-(x3-x1)*(y2-y1) > 0 ) {
    coteRasterise(x1,y1,x2,y2);
    coteRasterise(x2,y2,x3,y3);
    coteRasterise(x1,y1,x3,y3); }
    else {
    coteRasterise(x1,y1,x2,y2);
    coteRasterise(x2,y2,x3,y3);
    coteRasterise(x1,y1,x3,y3); }
}

/* Fonction executee lors d'un rafraichissement       */
/* de la fenetre de dessin                            */

static void display(void) {
  glClearColor(0.8F,0.8F,0.8F,1.0F);
  glClear(GL_COLOR_BUFFER_BIT);
  glPushMatrix();
  glColor3fv(bleu);
  quadrillage();
  switch(aff) {
    case 0 :
      glColor3fv(jaune);
      pixel(9,2);
      glColor3fv(rouge);
      pixel(4,19);
      glColor3fv(vert);
      pixel(17,13);
      break;
    case 1 :
      glColor3fv(jaune);
      ligne(15,2,4,19);
      glColor3fv(vert);
      ligne(2,5,17,15);
      break;
    case 2 :
      glColor3fv(cyan);
      triangleReel(15,2,2,4,19,14);
      glColor3fv(magenta);
      triangleReel(1,6,18,16,4,19);
      break;
    case 3 :
      glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
      glLineWidth(5.0F);
      glColor3fv(cyan);
      triangleReel(15,2,2,4,19,14);
      glColor3fv(magenta);
      triangleReel(1,6,18,16,4,19);
      glLineWidth(1.0F);
      glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
      break;
    case 4 :
      glColor3fv(cyan);
      triangleCotesSegments(15,2,2,4,19,14);
      glColor3fv(magenta);
      triangleCotesSegments(1,6,18,16,4,19);
      break;
    case 5 :
      glColor3fv(cyan);
      triangleCotesRasterises(15,2,2,4,19,14);
      glColor3fv(magenta);
      triangleCotesRasterises(1,6,18,16,4,19);
      break;
    case 6 :
      glColor3fv(cyan);
      triangleCotesSegments(15,2,2,4,19,14);
      glColor3fv(magenta);
      triangleCotesSegments(1,6,18,16,4,19);
      glColor3fv(cyanFonce);
      triangleCotesRasterises(15,2,2,4,19,14);
      glColor3fv(magentaFonce);
      triangleCotesRasterises(1,6,18,16,4,19);
      break;
    case 7 :
      glColor3fv(cyan);
      triangle(15,2,2,4,19,14);
      glColor3fv(magenta);
      triangle(1,6,18,16,4,19);
      break;
    case 8 :
      glColor3fv(cyan);
      triangle(15,2,2,4,19,14);
      glColor3fv(magenta);
      triangle(1,6,18,16,4,19);
      glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
      glLineWidth(3.0F);
      glColor3fv(cyanFonce);
      triangleReel(15,2,2,4,19,14);
      glColor3fv(magentaFonce);
      triangleReel(1,6,18,16,4,19);
      glLineWidth(1.0F);
      glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
      break; }
  glPopMatrix();
  glFlush();
  glutSwapBuffers();
  int error = glGetError();
  if ( error != GL_NO_ERROR )
    printf("Erreur OpenGL: %d\n",error);
}

/* Fonction executee lors d'un changement             */
/* de la taille de la fenetre OpenGL                  */
/* -> Ajustement de la camera de visualisation        */

static void reshape(int x,int y) {
  glViewport(0,0,x,y); 
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glOrtho(0.0,x,0.0,y,-1000.0,1000.0);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
}

/* Fonction executee lors de la frappe                */
/* d'une touche du clavier                            */

static void keyboard(unsigned char key,int x,int y) {
  switch ( key ) {
    case 0x0D :
      aff = (aff+1)%9;
      glutPostRedisplay(); 
      break;
    case 0x1B :
      exit(0); 
      break; }
}

/* Fonction principale                                */

int main(int argc,char **argv) {
  glutInit(&argc,argv);
  glutInitDisplayMode(GLUT_RGBA|GLUT_DOUBLE);
  glutInitWindowSize(350,350); 
  glutInitWindowPosition(50,50); 
  glutCreateWindow("Tracé segment & remplissage triangle"); 
  glutKeyboardFunc(keyboard);
  glutReshapeFunc(reshape);
  glutDisplayFunc(display);
  glutMainLoop();
  return(0);
}
