02/16/04

We tallked about the solution to the snow flakes problem.  Also we discussed the solution to Quiz(4).  
For the snow flakes, the main thing was to modify the draw_triangle in the Gasket program.

void snow(point2 a, point2 b, int m)
{

/* triangle subdivision using vertex numbers */

    point2 c;
    point2 d;
    point2 e;
    point2 f;
    if( m >0)
    {
      glBegin(GL_LINE_STRIP);

        c[0] = a[0] + (1.0/3)*(b[0]-a[0]);
        c[1] = a[1] + (1.0/3)*(b[1]-a[1]);

        f[0] = a[0]+(2.0/3)*(b[0]-a[0]);
        f[1] = a[1]+(2.0/3)*(b[1]-a[1]);
        d[0] = f[0] - c[0];
        d[1] = f[1] - c[1];
        e[0] = c[0]+ 0.5* d[0] - sqrt(3)*d[1]/2; 
        e[1] = c[1] + sqrt(3)*d[0]/2 + 0.5*d[1];
        snow(a,c,m-1);
        snow(c,e,m-1);
        snow(e,f,m-1);
        snow(f,b,m-1);
        glVertex2fv(a);
        glVertex2fv(c);
        glVertex2fv(e);
        glVertex2fv(f);
        glVertex2fv(b);

      glEnd();
    }
}

The initial points also will change to:

const double PI = 3.141593;
typedef float point2[2];

/* initial triangle */
point2 v[]={{400.0, 100.0}, {100, 100}};
point2 v1[]={{250, 359.807621135331}, {400.0, 100.0}};
point2 v2[]={{100,100}, {250, 359.807621135331}};


The 359.80762....  is the top point of the triangle that I used to build the snow flakes.
The 0th iteration looks like this:
0th

First iteration:
1st

Fith iteration:
5th


For Quiz(4), I asked you to modify the program that drew the big O to draw a square using the same procedure.

This is the program that we used to create the Big O:
static void myinit (void)
{


    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(0.0, 20, 0, 20);

    glMatrixMode(GL_MODELVIEW);

    glClearColor (1, 1.0, 1.0, 1.0);


   float angle;
   listName = glGenLists (1);
   glNewList (listName, GL_COMPILE);
      glTranslatef(5, 10, 0.0);
      glColor3f (1.0, 0.0, 0.0);  /*  current color red  */
      glBegin (GL_QUAD_STRIP);
      for(int i = 0; i <= 12; i++)
      {
          angle = 3.14159/6.0 * i;
          glVertex2f (4*cos(angle), 4*sin(angle));
            glVertex2f (5*cos(angle), 5*sin(angle));
      }
      glEnd ();
//      glTranslatef (5, 10, 0.0); /*  move position  */
   glEndList ();
   glShadeModel (GL_FLAT);

}


If we make two changes:

      for(int i = 0; i <= 4; i++)
      {
          angle = 3.14159/2 * i;
          glVertex2f (4*cos(angle), 4*sin(angle));
          glVertex2f (5*cos(angle), 5*sin(angle));
      }

And the display title to:
   glutCreateWindow("Square from Big O");

Then we will get:
sq

Picking
We talked about picking. Picking is an input operation that allows the user to identify an object on the display.  Although, the picking is done by a pointing device, the information returned to the application program is not a position.
Why picking is more difficult in modern systems?
Does a point on the display reversible to its origin?
 A pick device is more difficult to implement than the locator device.  There are two ways to do this:
1) selection, involves adjusting the clipping region and viewport such that we can keep track of which primitives in a small clipping region are rendered into a region near the cursor. Creates a hit list. OpenGL supports it.
2) bounding rectangles or extents, this is the smallest rectangle, aligned with the coordinates axes, that contains the object.
3) Use back buffer and an extra rendering.  Involves 4 steps: 1) draw the object in the back buffer with the pick color, 2) Get the position of the mouse using the mouse callback, 3) Use glReadPixels() to find the color at the position in the frame buffer corresponding to the mouse position, 4) Search a table of colors to find which object corresponds to the color read.  Follow these steps by a normal rendering into the back buffer.

Here is an example of how picking can be done using one of these three methods:
/* pick.c    */
// Minor Modification by Rahman Tashakkori - Spring 2004
/* E. Angel, Interactive Computer Graphics */
/* A Top-Down Approach with OpenGL, Third Edition */
/* Addison-Wesley Longman, 2003 */

/* demonstrates picking used selection mode */

#include <stdlib.h>
#include <stdio.h>
#include <GL/glut.h>


void init()
{
   glClearColor (0.0, 0.0, 0.0, 0.0);
}

void drawObjects(GLenum mode)
{
    if(mode == GL_SELECT) glLoadName(1);
    glColor3f(1.0, 0.0, 0.0);
    glRectf(-0.5, -0.5, 1.0, 1.0);
//    glRectf(-0.5, -0.5, 0.5, 0.5);

    if(mode == GL_SELECT) glLoadName(2);
    glColor3f(0.0, 0.0, 1.0);
    glRectf(-1.0, -1.0, 0.5, 0.5);
}

void display()
{
    glClear(GL_COLOR_BUFFER_BIT);
    drawObjects(GL_RENDER);
    glFlush();
}

/*  processHits prints out the contents of the
 *  selection array.
 */
void processHits (GLint hits, GLuint buffer[])
{
   unsigned int i, j;
   GLuint ii, jj, names, *ptr;

   printf ("hits = %d\n", hits);
   ptr = (GLuint *) buffer;
   for (i = 0; i < hits; i++) {    /*  for each hit  */
      names = *ptr;
      ptr+=3;
      for (j = 0; j < names; j++) { /*  for each name */
         if(*ptr==1) printf ("red rectangle\n");
         else printf ("blue rectangle\n");
         ptr++;
      }
      printf ("\n");
   }
}

#define SIZE 512

void mouse(int button, int state, int x, int y)
{
   GLuint selectBuf[SIZE];
   GLint hits;
   GLint viewport[4];

   if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
   {
   glGetIntegerv (GL_VIEWPORT, viewport);

   glSelectBuffer (SIZE, selectBuf);
   glRenderMode(GL_SELECT);

   glInitNames();
   glPushName(0);

   glMatrixMode (GL_PROJECTION);
   glPushMatrix ();
   glLoadIdentity ();
/*  create 5x5 pixel picking region near cursor location    */
   gluPickMatrix ((GLdouble) x, (GLdouble) (viewport[3] - y),
                  5.0, 5.0, viewport);
   gluOrtho2D (-2.0, 2.0, -2.0, 2.0);
   drawObjects(GL_SELECT);


   glMatrixMode (GL_PROJECTION);
   glPopMatrix ();
   glFlush ();

   hits = glRenderMode (GL_RENDER);
   processHits (hits, selectBuf);

   glutPostRedisplay();
   }
}


void reshape(int w, int h)
{
   glViewport(0, 0, w, h);
   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();
   gluOrtho2D (-2.0, 2.0, -2.0, 2.0);
   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();
}

void keyboard(unsigned char key, int x, int y)
{
   switch (key) {
      case 'q':
      case 'Q':
         exit(0);
         break;
   }
}

/* Main Loop */
int main(int argc, char** argv)
{
   glutInit(&argc, argv);
   glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);
   glutInitWindowSize (500, 500);
   glutInitWindowPosition (100, 100);
   glutCreateWindow (argv[0]);
   init ();
   glutReshapeFunc (reshape);
   glutDisplayFunc(display);
   glutMouseFunc (mouse);
   glutKeyboardFunc (keyboard);
   glutMainLoop();
   return 0;

}

The result would be the screen show below and the data provided representing the locations where the click was made on the left mouse button.
pick1

And tha Data screen:
pick2

That shows that we have red area, red area again, blue, then 5 times outside both (black area), then red, red, and finally a blue.

We start talking about the simple paint program.  Such a paint program should utilize menues and submenus heavily.  Here is a possible schema:
paint1

An implementation of this program would be:


/* This program illustrates the use of the glut library for
interfacing with a window system */
/*Link  glut32.lib  opengl32.lib  glu32.lib */


#include <GL/glut.h>
#include<math.h>

void mouse(int, int, int, int);
void key(unsigned char, int, int);
void display(void);
void drawSquare(int, int);
void myReshape(GLsizei, GLsizei);

void myinit(void);

void screen_box(int, int, int);
void right_menu(int);
void middle_menu(int);
void color_menu(int);
void pixel_menu(int);
void fill_menu(int);
int pick(int, int);

/* globals */

GLsizei wh = 500, ww = 500; /* initial window size */
GLfloat size = 3.0;   /* half side length of square */
int draw_mode = 0; /* drawing mode */
int rx, ry; /*raster position*/
int base;

GLfloat r = 1.0, g = 1.0, b = 1.0; /* drawing color */
int fill = 0; /* fill flag */

void drawSquare(int x, int y)
{

        y=wh-y;
        glColor3ub( (char) rand()%256, (char) rand()%256, (char) rand()%256);
        glBegin(GL_POLYGON);
                glVertex2f(x+size, y+size);
                glVertex2f(x-size, y+size);
                glVertex2f(x-size, y-size);
                glVertex2f(x+size, y-size);
        glEnd();
}


/* rehaping routine called whenever window is resized or moved */

void myReshape(GLsizei w, GLsizei h)
{

/* adjust clipping box */

        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        glOrtho(0.0, (GLdouble)w, 0.0, (GLdouble)h, -1.0, 1.0);
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();

/* adjust viewport and  clear */

        glViewport(0,0,w,h);
        glClearColor (0.8, 0.8, 0.8, 1.0);
        glClear(GL_COLOR_BUFFER_BIT);
        display();
        glFlush();

/* set global size for use by drawing routine */

        ww = w;
        wh = h;
}

void myinit(void)
{

// char
    int i;
/*
    base = glGenLists(256);
    for(i = 0; i < 255; i++)
    {
        glNewList(base+i, GL_COMPILE);
        glutBitmapCharacter(GLUT_BITMAP_8_BY_13, i);
    //    glutStrokeCharacter(GLUT_STROKE_ROMAN, i);
        glEndList();
    }
    glListBase(base);

*/

    glViewport(0,0,ww,wh);


/* Pick 2D clipping window to match size of X window
This choice avoids having to scale object coordinates
each time window is resized */

        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        glOrtho(0.0, (GLdouble) ww , 0.0, (GLdouble) wh , -1.0, 1.0);

/* set clear color to black and clear window */

        glClearColor (0.8, 0.8, 0.8, 1.0);
        glClear(GL_COLOR_BUFFER_BIT);
        glFlush();
}



void mouse(int btn, int state, int x, int y)
{
    static int count;
    int where;
    static int xp[2],yp[2];
    if(btn==GLUT_LEFT_BUTTON && state==GLUT_DOWN)
    {
       glPushAttrib(GL_ALL_ATTRIB_BITS);
      
       where = pick(x,y);
       glColor3f(r, g, b);
       if(where != 0)
       {
          count = 0;
          draw_mode = where;
       }
       else switch(draw_mode)
       {
         case(1): //line
          if(count==0)
          {
              count++;
              xp[0] = x;
              yp[0] = y;
          }
          else
          {
              glBegin(GL_LINES);
                 glVertex2i(x,wh-y);
                 glVertex2i(xp[0],wh-yp[0]);
              glEnd();
              draw_mode=0;
              count=0;
          }
          break;
        case(2): //rectangle
          if(count == 0)
          {
              count++;
              xp[0] = x;
              yp[0] = y;
          }
          else
          {
              if(fill) glBegin(GL_POLYGON);
              else glBegin(GL_LINE_LOOP);
                 glVertex2i(x,wh-y);
                 glVertex2i(x,wh-yp[0]);
                 glVertex2i(xp[0],wh-yp[0]);
                 glVertex2i(xp[0],wh-y);
              glEnd();
              draw_mode=0;
              count=0;
          }
          break;
        case (3): //Triangle
          switch(count)
          {
            case(0):
              count++;
              xp[0] = x;
              yp[0] = y;
              break;
            case(1):
              count++;
              xp[1] = x;
              yp[1] = y;
              break;
            case(2):
              if(fill) glBegin(GL_POLYGON);
              else glBegin(GL_LINE_LOOP);
                 glVertex2i(xp[0],wh-yp[0]);
                 glVertex2i(xp[1],wh-yp[1]);
                 glVertex2i(x,wh-y);
              glEnd();
              draw_mode=0;
              count=0;
          }
          break;
          case(4)://Point
          {
             drawSquare(x,y);
             count++;
          }
          break;
          case(5)://TEXT
          {
             rx=x;
             ry=wh-y;
             glRasterPos2i(rx,ry);
             count=0;
             break;
          }
          default:
          {
             rx=x;
             ry=wh-y;
             glRasterPos2i(rx,ry);
             count=0;
         }
       }

       glPopAttrib();
       glFlush();
     }
}

int pick(int x, int y)
{
    y = wh - y;
    if(y < wh-ww/10) return 0;
    else if(x < ww/10) return 1; //line
    else if(x < ww/5) return 2; //rectangle
    else if(x < 3*ww/10) return 3; //triangle
    else if(x < 2*ww/5) return 4; //point
    else if(x < ww/2) return 5; //text
    else return 0;
}

void screen_box(int x, int y, int s )
{
    glBegin(GL_QUADS);
      glVertex2i(x, y);
      glVertex2i(x+s, y);
      glVertex2i(x+s, y+s);
      glVertex2i(x, y+s);
    glEnd();
}

void right_menu(int id)
{
   if(id == 1) exit(1);
   else display();
}

void middle_menu(int id)
{

}

void color_menu(int id)
{
   if(id == 1) {r = 1.0; g = 0.0; b = 0.0;}
   else if(id == 2) {r = 0.0; g = 1.0; b = 0.0;}
   else if(id == 3) {r = 0.0; g = 0.0; b = 1.0;}
   else if(id == 4) {r = 0.0; g = 1.0; b = 1.0;}
   else if(id == 5) {r = 1.0; g = 0.0; b = 1.0;}
   else if(id == 6) {r = 1.0; g = 1.0; b = 0.0;}
   else if(id == 7) {r = 1.0; g = 1.0; b = 1.0;}
   else if(id == 8) {r = 0.0; g = 0.0; b = 0.0;}
}


void pixel_menu(int id)
{
   if (id == 1) size = 2 * size;
   else if (size > 1) size = size/2;
}

void fill_menu(int id)
{
   if (id == 1) fill = 1;
   else fill = 0;
}

void key(unsigned char k, int xx, int yy)
{
   if(draw_mode!= 5) return;  //text
    glColor3f(0.0,0.0,0.0);
    glRasterPos2i(rx,ry);
//    glCallList(k);
     glutBitmapCharacter(GLUT_BITMAP_9_BY_15, k);
//     glutBitmapCharacter(GLUT_BITMAP_8_BY_13, k);

    /*glutStrokeCharacter(GLUT_STROKE_ROMAN,i); */
     rx += glutBitmapWidth(GLUT_BITMAP_9_BY_15,k);
//   rx += 10;

}

void display(void)
{
    int shift=0;
    glPushAttrib(GL_ALL_ATTRIB_BITS);
    glClearColor (0.8, 0.8, 0.8, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);
    glColor3f(1.0, 1.0, 1.0);
    screen_box(0,wh-ww/10,ww/10);
    glColor3f(1.0, 0.0, 0.0);
    screen_box(ww/10,wh-ww/10,ww/10);
    glColor3f(0.0, 1.0, 0.0);
    screen_box(ww/5,wh-ww/10,ww/10);
    glColor3f(0.0, 0.0, 1.0);
    screen_box(3*ww/10,wh-ww/10,ww/10);
    glColor3f(1.0, 1.0, 0.0);
    screen_box(2*ww/5,wh-ww/10,ww/10);
    glColor3f(0.0, 0.0, 0.0);
    screen_box(ww/10+ww/40,wh-ww/10+ww/40,ww/20);
    glBegin(GL_LINES);
       glVertex2i(wh/40,wh-ww/20);
       glVertex2i(wh/40+ww/20,wh-ww/20);
    glEnd();
    glBegin(GL_TRIANGLES);
       glVertex2i(ww/5+ww/40,wh-ww/10+ww/40);
       glVertex2i(ww/5+ww/20,wh-ww/40);
       glVertex2i(ww/5+3*ww/40,wh-ww/10+ww/40);
    glEnd();
    glPointSize(3.0);
    glBegin(GL_POINTS);
       glVertex2i(3*ww/10+ww/20, wh-ww/20);
    glEnd();
    glRasterPos2i(2*ww/5,wh-ww/20);
    glutBitmapCharacter(GLUT_BITMAP_9_BY_15, 'A');
    shift=glutBitmapWidth(GLUT_BITMAP_9_BY_15, 'A');
    glRasterPos2i(2*ww/5+shift,wh-ww/20);
    glutBitmapCharacter(GLUT_BITMAP_9_BY_15, 'B');
    shift+=glutBitmapWidth(GLUT_BITMAP_9_BY_15, 'B');
    glRasterPos2i(2*ww/5+shift,wh-ww/20);
    glutBitmapCharacter(GLUT_BITMAP_9_BY_15, 'C');
    glFlush();
    glPopAttrib();
}


int main(int argc, char** argv)
{
    int c_menu, p_menu, f_menu;

    glutInit(&argc,argv);
    glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);
    glutInitWindowSize(500, 500);
    glutCreateWindow("paint");
    glutDisplayFunc(display);
    c_menu = glutCreateMenu(color_menu);
    glutAddMenuEntry("Red",1);
    glutAddMenuEntry("Green",2);
    glutAddMenuEntry("Blue",3);
    glutAddMenuEntry("Cyan",4);
    glutAddMenuEntry("Magenta",5);
    glutAddMenuEntry("Yellow",6);
    glutAddMenuEntry("White",7);
    glutAddMenuEntry("Black",8);
    p_menu = glutCreateMenu(pixel_menu);
    glutAddMenuEntry("increase pixel size", 1);
    glutAddMenuEntry("decrease pixel size", 2);
    f_menu = glutCreateMenu(fill_menu);
    glutAddMenuEntry("fill on", 1);
    glutAddMenuEntry("fill off", 2);
    glutCreateMenu(right_menu);
    glutAddMenuEntry("quit",1);
    glutAddMenuEntry("clear",2);
    glutAttachMenu(GLUT_RIGHT_BUTTON);
    glutCreateMenu(middle_menu);
    glutAddSubMenu("Colors", c_menu);
    glutAddSubMenu("Pixel Size", p_menu);
    glutAddSubMenu("Fill", f_menu);
    glutAttachMenu(GLUT_MIDDLE_BUTTON);
    myinit ();
    glutReshapeFunc (myReshape);
    glutKeyboardFunc(key);
    glutMouseFunc (mouse);
    glutMainLoop();

    return 0;

}

This produces the following screens:

paint2


And on drawing you may get an screen that looks like this one:
paint3