Viewports and User Interaction

Tom Kelliher, CS 320

Mar. 29, 2000

Administrivia

Announcements

Project 1 due in one week. Written final?

Assignment

Read paint.c.

From Last Time

Projections.

Dealing with window resizing: viewports, aspect ratios.

Outline

  1. Viewports, viewport.c, window resizing.

  2. User interaction, paint.c

Coming Up

Continuing interactive graphics.

Controlling the Viewport

  1. The viewport is used to control what part of the window you render into. It is used to maintain aspect ratio.

  2. Aspect ratio: ratio of rectangle's width to height.

    The aspect ratio's of the clipping rectangle and viewing window should match. Otherwise, distortion results.

  3. Controlling the viewport:
    void glViewport(GLint x, GLint y, GLsizei w, GLsizei h);
    
    where , is the lower-left corner of the viewport, and w and h are its width and height, respectively.

    Example. Assume you have a viewing window:

    glViewport(100, 100, 50, 50)
    
    This sets the viewport to the upper righthand quarter of the viewing window.

Discovering the Window Size

  1. The program initially specifies the window size:
    glutInitWindowSize(gww, gwh);
    
    But, the user can resize the window at will.

  2. Register a reshape callback (get the students to discover the reshape code):
    #define WINDOW_WIDTH 200
    #define WINDOW_HEIGHT 200
    
    void reshape(int, int);
    
    int maintainAspectRatio = 1;
    int gww = WINDOW_WIDTH;
    int gwh = WINDOW_HEIGHT;
    
    int main()
    {
       /* ... */
    
       glutInitWindowSize(gww, gwh);
       glutReshapeFunc(reshape);
    
       /* ... */
    }
    
    
    void reshape(int w, int h)
    {
       if (maintainAspectRatio)
          gww = gwh = (w < h) ? w : h;   /* aspect ratio = 1 */
       else
       {
          gww = w;
          gwh = h;
       }
    }
    

viewport.c

Points to note:

  1. Does not maintain aspect ratio.

  2. The display list glues together multiple polygons.

  3. display() sets the viewport to several portions of the window, and then renders the display list.

    Note the overlap and observe the result.

  4. The rendering of the text is delayed until idle() is called.

  5. The position to begin rendering text is specified in world coordinates, and then bitblt ops are used to copy the bitmap characters.

    The user could easily write a small function to position and render strings.

Experimentation with viewport.c

  1. Modify the program so that it maintains a 1:1 aspect ratio.

  2. Modify the program so that it starts with and maintains a 1:2 (h:w) aspect ratio.

paint.c

  1. Stripped-down paint program: lines, rectangles, triangles, points. Color, fill, clear canvas.

  2. User interaction: right-button menus, left-button selections.

  3. Pick determination.

  4. Text and Time display in the idle function.

  5. Window reshaping.

Coordinate systems

  1. Rendering origin for OpenGL.

  2. Window and mouse coordinate origin for the window system.

  3. Translating.

Menus and Sub-Menus

int main()
{

    // ...

    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);
    glutAddSubMenu("Colors", c_menu);
    glutAddSubMenu("Pixel Size", p_menu);
    glutAddSubMenu("Fill", f_menu);
    glutAddMenuEntry("clear",2);
    glutAddMenuEntry("quit",1);
    glutAttachMenu(GLUT_RIGHT_BUTTON);

    // ...

}


void color_menu(int id)
{
   glutIdleFunc(NULL);
   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;}
   glutIdleFunc(idle);
}

Pick Determination and Drawing States

  1. Mouse function called into play here.

  2. Note that right button taken out of action.

  3. Canvas icons: four in row, upper-left. Dimensions?

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

void mouse(int btn, int state, int x, int y)
{
    static int draw_mode = 0; /* drawing mode */
    static int count;
    int where;
    static int xp[2],yp[2];
    if(btn==GLUT_LEFT_BUTTON && state==GLUT_DOWN) 
    {
       glPushAttrib(GL_ALL_ATTRIB_BITS);
       glutIdleFunc(NULL);
       
       where = pick(x,y);
       glColor3f(r, g, b);
       if(where != 0)
       {
          count = 0;
          draw_mode = where;
       }
       else if(draw_mode == 1  &&  count == 0)
       {
              count = 1;
              xp[0] = x;
              yp[0] = y;
       }
       else if(draw_mode == 1  &&  count != 0)
       {
              glBegin(GL_LINES); 
                 glVertex2i(x,wh-y);
                 glVertex2i(xp[0],wh-yp[0]);
              glEnd();
              draw_mode=0;
              count=0;
       }
       else if(draw_mode == 2  &&  count == 0)
       {
              count = 1;
              xp[0] = x;
              yp[0] = y;
       }
       else if(draw_mode == 2  &&  count != 0) 
       {
              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;
       }
       else if(draw_mode == 3  &&  count == 0)
       {
              count = 1;
              xp[0] = x;
              yp[0] = y;
       }
       else if(draw_mode == 3  &&  count == 1)
       {
              count = 2;
              xp[1] = x;
              yp[1] = y;
       }
       else if(draw_mode == 3  &&  count == 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;
       }
       else if(draw_mode == 4 )
       {
          drawSquare(x,y);
          count++;
       }

       glutIdleFunc(idle);
       glPopAttrib();
       glFlush();
     }
}

Text Display

Idea:

  1. Specify starting location of text (world coordinates).

  2. Start writing, specifying font and character.

Example:

void renderString(GLdouble x, GLdouble y, void *font, char *text)
{
   glRasterPos2d(x, y);

   while (text)
   {
      glutBitmapCharacter(font, *text);
      ++text;
   }
}
See man page for glutBitmapCharacter for list of available bitmap fonts.

Idea similar to font cache: display lists. Program example:

base = glGenLists(128);

for(i=0;i<128;i++)
{
   glNewList(base+i, GL_COMPILE);
   glutBitmapCharacter(GLUT_BITMAP_9_BY_15, i);
   glEndList();
}

glListBase(base);
Use:
// Dump time string into out.

glRasterPos2i(ww-80, wh-15);
glColor3f(0.0,0.0,0.0);
glBegin(GL_QUADS);
   glVertex2i(ww-80, wh-15);
   glVertex2i(ww, wh-15);
   glVertex2i(ww, wh);
   glVertex2i(ww-80, wh);
glEnd();
glColor3f(1.0,1.0,1.0);
glCallLists( strlen(out) , GL_BYTE, out);

Time Display in the Idle Function

  1. ``Watery'' appearance.

  2. Constant updating.

  3. Fixes?

  4. Note use of NULL to disable idle function and subsequent re-registration.

Window and Canvas Re-Shaping

  1. Reshape function registered and called on reshape event.

  2. Is passed new window size.

  3. Adjusts clipping rectangle to reflect new canvas size.

  4. Sets viewport to entire window.

  5. Clears canvas.

  6. Calls display() to paint the buttons. (Will display() get the button sizes right?)

  7. Adjusts global canvas size.



Thomas P. Kelliher
Wed Mar 29 12:42:44 EST 2000
Tom Kelliher