Back to July Calendar 

Previous Entry Next Entry→

August 15, 2005

 

The chapter 3 programming exercises in Angel suggest, firstly, creating a simple drawing program that uses the mouse to create simple shapes such as line segments, rectangles and triangles.  Use menus to select modes and to allow the user to change the drawing color.

 

This seems pretty ambitious to my trepiditious self.  Squarely facing the problem I emulate the great artists and learn by first borrowing from others.  I borrowed this from Michael Glass at Valparaiso University, which claims to be somewhere in Indiana.  I love this guy's approach with the skeleton code.

 

How it does it
 

The attached OpenGL/GLUT skeleton program (also available from the class web page) contains an array of pixels called canvas. Each pixel in the array contains three bytes of type rgb_pixel for the three colors. The program sets up OpenGL so that it uses OpenGL pixel mode to draw the entire canvas from this pixel array. 

 

Note: Functions include glPixelStorei(GL_UNPACK_ALIGNMENT, 1); ll

The parts marked with question marks allow you to insert your own code. The screen is initialized, the viewing camera is set for 2D orthogonal, and callbacks are installed for you. If you update the pixel array, it will automatically appear on the screen.


The array is one dimensional, but you can index the pixel array using the LOC macro. The x and y coordinates range from 0 to screenWidth and screenHeight respectively. The following illustrates setting a single pixel element at x0 and y0
to pure white:

canvas[LOC(x0,y0)].r = 255;
canvas[LOC(x0,y0)].g = 255;
canvas[LOC(x0,y0)].b = 255;

Alternatively you could have a single variable that contains pure white:

rgb_pixel white = {255, 255, 255};
...
canvas[LOC(x0,y0)] = white;

At the beginning of the program, immediately after InitGL(), set the entire canvas to light blue with

   for(i = 0; i < screenWidth*screenHeight; i++) {
      canvas[i] = bgcolor;
.

In the mouse-click left-button handler, you keep track of triangles as they are being drawn. First click sets the first point for the first triangle. Next sets the second point and draws a line. Third click set the third point (finishing the triangle), and draws two more lines, then colors the triangle. The triangle is saved away in a data structure, and subsequent clicks start the next triangle.


In keyboard E key handler, you remove the last-added triangle and call a routine to
re-draw the entire stack of triangles. The triangles are re-drawn in the order they
were entered.


In the mouse-click right-button-down handler, you set a flag saying that a triangle
is being dragged (assuming the coordinates of the click are within a triangle—you
need to scan the stack from most recent to oldest triangle to find out). You also save these coordinates. In the button-up handler you clear the flag, so the triangle
will not be dragged any more.
In the mouse-move handler, you see whether a triangle is being dragged. If so, you
compute the difference between the current mouse coordinates and the previous
coordinates (originally saved saved on mouse-down, updated as you move) and
use the delta-x and delta-y differences to move the whole triangle. Since triangles
can be on top of each other, this means redrawing the whole shebang.
A warning about y coordinates: it is usual for drawing programs to have y increase
from 0 at the bottom of the screen to its maximum value at the top. It is also usual
for mouse locations to be counted from 0 at the top of the screen, and increasing
going down. This means that the mouse ym coordinate and the screen-drawing y
coordinates are related by y = screenHeight −ym.

 

/* CS 365 Lab 1 OpenGL Template by Michael Glass
* Look for ??? to see where your code goes!
* (Cribbed and modified from one used at Utah, written by Kristi Potter) */


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

/*------------------------------------------
* Standard functions and callbacks
*------------------------------------------*/


/* Initialize GL (defined below) */
void initGL();

/* Called while program is idle */
void idle();

/* Put your draw functions here */
void redraw();

/* All functions for display, calls redraw */
void display();

/* Called when a key is pressed */
void keyEvent(unsigned char key,int x,int y);

/* Called when mouse button pressed */
void mouse(int button,int state,int x,int y);

/* Called when the mouse moves */
void mouseMotion(int x,int y);


/*----------------------------------------------------------
* USER DATA TYPES
*----------------------------------------------------------*/

/* The location of a single point */
typedef struct {
   int x, y;
} point;

/* Struct to hold a single pixel (as three color bytes) */
   typedef struct {
   unsigned char r, g, b; /* use 256 colors */
} rgb_pixel;

/*--------------------------------------
* USER FUNCTION FORWARD DECLARATIONS
*--------------------------------------*/
void draw_line(point p0, point p1);
void fill_triangle(point a, point b, point c);

/*-----------------------------------------------------------
* GLOBAL VARIABLES
*-----------------------------------------------------------*/

/* Dimensions of the screen */
#define SCREENWIDTH 600
#define SCREENHEIGHT 500
const int screenWidth = SCREENWIDTH;
const int screenHeight = SCREENHEIGHT;

/*-------------------------------------
* USER GLOBAL VARIABLES */
const rgb_pixel bgcolor = {210, 210, 255}; /* Light blue */
/* ??? other user global variables, such as */
rgb_pixel black = {0, 0, 0};
/*-------------------------------------------------
* one-dimensional Pixel buffer array representing the whole picture
* The 2D LOC macro converts (x,y) to an index into this buffer */

rgb_pixel canvas[SCREENWIDTH * SCREENHEIGHT];
#define LOC(X,Y) ((Y)*screenWidth + (X))

/*---------------------------------------------------
* MAIN PROGRAM
* draws model, calls phantom code */
int main(int argc, char **argv)
{
   int i;
   /* Initialize glut */
   glutInit(&argc,argv);

   /* Initialize GL (display cycle, mouse and keyboard)

      Note that display calls redraw*/
   initGL();

   /* Makes each pixel the background color. */
   for(i = 0; i < screenWidth*screenHeight; i++) {
      canvas[i] = bgcolor;
   }

   // event loop starts here
   glutMainLoop();

   return 0;
}

/*----------------------------------------------------------------------
* init */
void initGL() {
   // use red/green/blue, double buffered
   glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB );

   // Set Main Graphics Window Size
   glutInitWindowSize(screenWidth, screenHeight);

   // Set the main Graphics Window Position.
   glutInitWindowPosition(200,100);

   // the window name is an unsigned int that works like a handle
   glutCreateWindow("Howdy Pardner");

   // initializes callbacks for glut
   glutDisplayFunc(display);
   glutMotionFunc(mouseMotion);
   glutIdleFunc(idle);
   glutMouseFunc(mouse);
   glutKeyboardFunc(keyEvent);

}

//+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+
// Idle
// Sets up the idle function for glui stuff.
//+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+
void idle() {
   // Redraw the contents of the display window
   glutPostRedisplay();
}

//+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+
// redraw
// Called to refresh and draw onto the screen.
//+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+
void redraw() {
   //glPushMatrix();
   /* Put all drawing stuff here.
   * For this assignment we just dump our pixel map to the window  */

   /* Rows of pixel data are all packed together, no word-alignment */
   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

   /* Draws the canvas array to the image buffer. */
   glDrawPixels((GLsizei)screenWidth, (GLsizei)screenHeight, GL_RGB,
   GL_UNSIGNED_BYTE, canvas);
   // glPopMatrix();
}

//+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+
// display
// Sets and refreshes the window and swaps buffers.
//+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+
void display() {
   // Clear the buffers
   glClear(GL_COLOR_BUFFER_BIT );

   //Load the projection matrix
   glMatrixMode( GL_PROJECTION );

   // Set the projection matrix to the identity matrix
   glLoadIdentity();

   /* Simplest Orthographic 2D projection */
   gluOrtho2D(-1.0, 1.0, -1.0, 1.0);

   redraw();

   // Swap the double buffers
   glutSwapBuffers();

   // Flush all drawing to the screen
   glFlush();
}

//+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+
// keyEvent
// Registers specific input characters from the keyboard.
//+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+
void keyEvent(unsigned char key,int x,int y)  {

   switch (key) {
      // Quit
      case 'Q':
      case 'q':
      exit(0);
      break;
      default:
      //do nothing on the default
      break;
   }
   /* ??? Add other keyboard events to the above switch as needed */

   glutPostRedisplay();
}

//+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+
// mouse
// Registers when the mouse buttons are pressed.
//+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+
int i = 0;
int triangle[3][2];
void mouse(int button,int state,int x,int y) {
   if(button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) {

      /* ??? Here is where you accumulate triangle points
  
   * and draw a triangle when you get all three points.*/
     

if(i < 3) {

         ++i;
      }
      else {
         i = 0;
         canvas[LOC(x0,y0)] = black;glColor3f(1.0,1.0,0.0);
         glBegin(GL_TRIANGLES);
            glVertex2iv(triangle[0]);
            glVertex2iv(triangle[1]);
            glVertex2iv(triangle[2]);
         glEnd();
      }

   }

   if(button == GLUT_LEFT_BUTTON && state == GLUT_UP)  {
   }

   if(button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN) {
      /* ??? Make a note that this button is down */
   }
   if(button == GLUT_RIGHT_BUTTON && state == GLUT_UP)  {
      /* ??? Make a note that this button is back up */
   }
}

//+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+
// mouseMotion
// Registers when the mouse is moved.
//+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+
void mouseMotion(int x,int y)
{
   if ((x < 0) | (x >= screenWidth) | (y < 0) | (y >= screenHeight)) return;
   /* ??? Here insert code to move triangle if (x,y) is within
   * a triangle and the proper mouse button is down*/
}