CS 4465 - Lab (4)
Picking, Menu, Keeping the Drawing on the screen


Our goals in this lab are:
Preparation
Use the same computer you have used before.  You are required to send one program as an e-mail attachment at the end of this lab.  Please make sure that at the Subject line you write,  Lab 4 - Activity.
 
Picking, Menu and Sub-menu in one sample program
Below is a a program that simulates a Drawing application.  In this program, we have used mouse control to do the drawing and to perform some selections from menus or sub-menus. 

Create a Draw object and cut and paste the program in that project.  Compile and run it.  Upon correct compilation, you will get a screen the looks like the one shown below.  To draw different shapes, first select the desired shape from the top by left clicking on the proper icon, then draw:
/* This program illustrates the use of the glut library for
interfacing with a window system */
/*Link  glut32.lib  opengl32.lib  glu32.lib */

#include <stdlib.h>
#include <GL/glut.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;

    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;

}

Create a project and cut and paste the program and run it, initially you will get a screen that looks like this:

BlankDraw

Lab Activity (1) -Trying picking, menu, and sub-menu
Create a screen that looks like this and show me the results.  Note that the Dots are in random color that are set by the program automatically.
Draw2

In this program, you can use the right mouse click to get a menu that allows you to either clear the screen or quit.  The middle mouse button provides you a more powerful menu with sub-menus for changing color, changing pixel size, and filling the polygons.  Create a new screen, where you have tried each of the above options, except quit of course.  Once you are done, show me your screen. 

Now let me ask you to conduct an activity so I can address the main problem.  Assuming you have a drawing on the screen, follow these steps:
1) Click on triangle,
2) Draw a triangle,
3) Move the mouse outside the drawing screen and click somewhere outside,
4) Go back to the drawing screen and left click at the top or somewhere in the screen..

You will notice that everything you had drawn gets erased.   This is a problem.  How can we fix this problem such that when we can go back and forth between screens without loosing what we have drawn before?

Lab Activity (2) -Making the drawing to stay
To help you solve this problem, I have included a much simpler version of a Draw program below.  Create a new Project, call it DrawGetsErased, and cut and paste the program below there and run it.

//  A very simple Draw program that gets erased when mouse moves out
#include <iostream>
#include <GL/glut.h>

using namespace std;

int anchorx, anchory;

int size = 4;
double R = 1.0, G = 0.0, B = 0.0;

void myinit(void)
{


        /* attributes */
        glClearColor(1.0, 1.0, 1.0, 1.0); /* white background */
        glColor3f(R, G, B); /* draw in black */
        /* set up viewing */
        /* 50 x 50 window with origin lower left */
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        gluOrtho2D(0.0, 500.0, 0.0, 500.0);
        glMatrixMode(GL_MODELVIEW);
}

void display( void )
{
        glClear(GL_COLOR_BUFFER_BIT);  /*clear the window */
        glFlush(); /* clear buffers */
}

void mouse(int button, int state,int x, int y)
{
        anchorx = x;
        anchory = 500-y;  //Mouse y coordinate converted
}

void mousemove(int x,int y)
{
        glColor3f(R, G, B); /* draw in black */
        glLineWidth(size);
        glBegin(GL_LINES);
                glVertex2f(anchorx,anchory);
                glVertex2f(x,500-y);
        glEnd();

        anchorx = x;
        anchory = 500-y;
        glFlush(); /* clear buffers */

}

void main(int argc, char** argv)
{
        /* Standard GLUT initialization */
        glutInit(&argc,argv);
        glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB); /* default, not needed */
        glutInitWindowSize(500,500); /* 500 x 500 pixel window */
        glutInitWindowPosition(0,0); /* place window top left on display */
        glutCreateWindow("Draw Screen"); /* window title */
        glutDisplayFunc(display); /* display callback invoked when window opened */
        glutMouseFunc(mouse);
        glutMotionFunc(mousemove);

        myinit(); /* set attributes */
        glutMainLoop(); /* enter event loop */
}

Lab Activity
Once you run the program you will get a blank screen.  The color is set to red, so you can draw on this screen in red by pressing the left button and by moving the mouse around while the left button is pressed down.  The drawing will stay on the screen for as long as you keep the mouse in the drawing screen and for as long as you haven't register any other event by clicking outside this screen.  But whatever you draw will disappear once you go to another screen, click on that screen, and then come back to this one.  To fix this problem, you need to remember all the points you had drawn and then redraw those points every time you get back to the screen by displaying a drawing mechanism in the Display function.  One way to do this is to create a fix size (maybe 1000 points) global array to keep track of the location (x,y) for every point and also to keep track of the total number of points. Then you can use a loop in the display function to go through all these points and draw what was last seen on the screen.  E-mail me your last program as an attachment.  At the subject line please write: Lab 4, Activity.

More to come on this soon.

Now you can add a menu option to change the color of your drawing and then make sure your drawing stays on the screen. This is for you to do and you do not need to submit it.