views:

57

answers:

3

I'm working a MS paint-like application on OpenGL using Bresenham's midpoint algorithm as homework. So far I can draw lines and ellipses. I lose them all when resizing the window. How can I keep them drawn?

Full code:

#include "GL/glut.h"

#include <stdio.h>
#include <math.h>
int i;

//int mainWindow, subWindow;
int X1, Y1, X2, Y2;

int modoDeDibujo; 

int W = 1000, H = 1000;
/*void menuApp (int value)
{
    if (value == 1) printf("Linea\n");
    if (value == 2) printf("Circulo\n");
    if (value == 3) printf("Elipsis\n");
    if (value == 4) exit(0);

}

void crearMenu()
{
    //inicio Creando el menu
    int submenu;
    submenu = glutCreateMenu(menuApp);
    glutAddMenuEntry("Linea", 1);

    glutAddMenuEntry("Elipse",3);
    glutAddMenuEntry("Salir",4);
    glutCreateMenu(menuApp);
    glutAddSubMenu("SubMenu", submenu);
    glutAttachMenu(GLUT_RIGHT_BUTTON);

    //fin Creando el menu
}*/

void renderPoint(void) /*REVISAR ESTO*/
{
    glClear (GL_COLOR_BUFFER_BIT);
    glBegin (GL_POINTS);
        glVertex2f  (-0.98, 0.98);
    glEnd ();
    glFlush ();
}





void renderPoint(double x, double y)
{

    //printf("BEFORE TRANSFORM %f\t%f\t# renderPoint\n", x, y);

    W = glutGet(GLUT_WINDOW_WIDTH);
    H = glutGet(GLUT_WINDOW_HEIGHT);

    float X;
    float Y;
    glBegin (GL_POINTS);
        X = (2*x/W) - 1;
        Y = (-2*y/H) + 1;
        glVertex2f (X, Y);
        //printf("TRANSFORMED POINT %f\t%f\t# renderPoint\n", X, Y);

    glEnd ();
    glFlush ();

}

/*wiki pseudo:

function line(x0, x1, y0, y1) //x1
     boolean steep := abs(y1 - y0) > abs(x1 - x0)//x2
     if steep then//x3
         swap(x0, y0) //x4
         swap(x1, y1) //x5 
     if x0 > x1 then //x6
         swap(x0, x1) //x7
         swap(y0, y1) //x8
     int deltax := x1 - x0 //x9
     int deltay := abs(y1 - y0) //x10
     int error := deltax / 2 //x11
     int ystep //x12
     int y := y0 //x13
     if y0 < y1 then ystep := 1 else ystep := -1 //x14
     for x from x0 to x1 //x15
         if steep then plot(y,x) else plot(x,y) //x16
         error := error - deltay //x17
         if error < 0 then //x18
             y := y + ystep //x19
             error := error + deltax //x20
*/


void bresenham1(GLint x0, GLint x1, GLint y0, GLint y1) //function line(x0, x1, y0, y1)
{


    //double result1 = fabs((double)y1 - y0); //abs(y1 - y0)
    //double result2 = fabs((double)x1 - x0); //abs(x1 - x0)


    int result1 = abs(y1-y0);
    int result2 = abs(x1-x0);

bool steep = (result1 > result2); //boolean steep := abs(y1 - y0) > abs(x1 - x0)

if (steep){ //if steep then

        GLint aux1 = x0; //swap(x0, y0)
        x0=y0;
        y0 = aux1;

        GLint aux2 = x1; // swap (x1,y1)
        x1=y1;
        y1=aux2;

}

if(x0>x1){ // if (x0>x1)
        GLint aux3=x0; //swap(x0,x1)
        x0=x1;
        x1=aux3;

        GLint aux4=y0;//swap(y0,y1)
        y0=y1;
        y1=aux4;

}

int deltax = x1-x0; // deltax = x1-x0
int deltay = abs(y1-y0); //  int deltay := abs(y1 - y0) - revisar 
int error = (deltax / 2); //int error := deltax / 2 
int ystep; // int ystep

int y = y0;  //int y := y0

    if (y0<y1){  //if y0 < y1 then ystep := 1 else ystep := -1 

        ystep=1;

    }

    else {ystep=-1;}

for (int x=x0; x<=x1; x++){ //for x from x0 to x1
  if (steep){  // if steep then plot(y,x) else plot(x,y)

       renderPoint(y,x);
   }
   else {

    renderPoint(x,y);
   }

error = error - deltay; //error := error - deltay

if (error<0) {  //if error < 0 then 
  y = y + ystep; // y := y + ystep
  error = error + deltax; //error := error + deltax

} // end if (error<0)


}// end for from x0 to x1


}// end bresenham


void Plot4EllipsePoints(int X, int Y,int CX,int CY){

      renderPoint(CX+X, CY+Y); //point in quadrant 1
      renderPoint(CX-X, CY+Y); // point in quadrant 2
      renderPoint(CX-X, CY-Y); // point in quadrant 3
      renderPoint(CX+X, CY-Y); // point in quadrant 4

}



void PlotEllipse (int CX, int CY, int XRadius, int YRadius) {


        int X, Y;
        int XChange, YChange;
        int EllipseError;
        int  TwoASquare, TwoBSquare;
        int  StoppingX, StoppingY;


    TwoASquare = 2 * XRadius * XRadius;
    TwoBSquare = 2 * YRadius * YRadius;

    X = XRadius;
    Y =0;

    XChange = YRadius*YRadius*(1-(2*XRadius));
    YChange = XRadius * XRadius;

    EllipseError =0;
    StoppingX = TwoBSquare*XRadius;
    StoppingY = 0;

    while(StoppingX >= StoppingY){

       Plot4EllipsePoints(X,Y,CX,CY);
       Y++;
       StoppingY=StoppingY + TwoASquare; 

       EllipseError= EllipseError+ YChange;

       YChange= YChange+ TwoASquare;

       if( ((2*EllipseError)  + XChange)>0)
       {
            X--;
            StoppingX = StoppingX - TwoBSquare;
                EllipseError= EllipseError + XChange;
                XChange = XChange + TwoBSquare;

    }


      }    

      //1st set of points done, start second set


     X=0;
     Y= YRadius;

     XChange= YRadius*YRadius;
     YChange = XRadius*XRadius*(1-2*YRadius);

     EllipseError=0;
     StoppingX =0;
     StoppingY= TwoASquare * YRadius;
     while(StoppingX <= StoppingY){ // 2nd set of points, y'<-1

    Plot4EllipsePoints(X,Y, CX,CY);
        X++;
        StoppingX = StoppingX + TwoBSquare;
        EllipseError = EllipseError + XChange;
        XChange = XChange + TwoBSquare;

       if (((2*EllipseError) + YChange)>0){

           Y--;
           StoppingY = StoppingY - TwoASquare;
           EllipseError = EllipseError + YChange;
           YChange = YChange + TwoASquare;


    }

     } 

}











void renderAll (void)
{
    /*glutSetWindow(mainWindow);
    glutPostRedisplay();
    glutSetWindow(subWindow);
    glutPostRedisplay();*/
}

void movimiento(int boton, int estado, int x, int y)
{
    if((estado == GLUT_DOWN) && (boton == GLUT_LEFT_BUTTON))//mouse down
    {
        X1 = x; Y1 = y;


        PlotEllipse (x, y, 200, 100);
        renderPoint(x,y);
    }
    if((estado == GLUT_UP) && (boton == GLUT_LEFT_BUTTON))//mouse up
    {
        //printf("  Up|x:%d, y:%d\n",x,y);
        X2 = x; Y2 = y;
        //renderLine();
        bresenham1(X1,X2,Y1,Y2);


       //PRUEBA USANDO LA PRIMITIVA DE OPENGL

         glBegin( GL_LINES );



         glEnd();


        //renderPoint(x, y);
    }
}

void MouseMove(int x, int y)
{
    //printf("x:%d  | y:%d\n", x,y);
    X2 = x; Y2 = y;
    //renderLine();
    //bresenham1(X1, Y1, X2, Y2);
}

void teclado(unsigned char key, int x, int y)
{
    if(key==1){
      modoDeDibujo=1; // dibuja lineas  
      printf("Modo de dibujo: linea");
    }

    if (key==2){
       modoDeDibujo=2; //dibuja elipses 
    }


    if(key == 27)exit(0);
}

void especiales(int key, int x, int y)
{
    if(key == GLUT_KEY_F1) exit(0);
}

static void
key(unsigned char k, int x, int y)
{
  switch (k) {
  case 27:  /* Escape */
    exit(0);
    break;
  default:
    return;
  }
  glutPostRedisplay();
}

int main (int argc, char *argv []) 
{
    i = 0;
    //inicializa las operaciones de OpenGL/GLUT, db cr antes de usar funciones GLUT
    glutInit (&argc, argv);

    glutInitDisplayMode (GLUT_SINGLE | GLUT_RGBA);
    glutInitWindowPosition (100, 100);
    glutInitWindowSize (W, H);
    //Crea una ventana  de Opengl
    glutCreateWindow ("tarea");
    glutDisplayFunc (renderPoint);
    glutMouseFunc(movimiento);
    glutKeyboardFunc(teclado);//teclas ASCII
    glutSpecialFunc(especiales);//captura las teclas [f1..f12]

    //glutPassiveMotionFunc(pasivo);
    glutKeyboardFunc(key);
    glutMotionFunc(MouseMove);
    //crearMenu();
    glutMainLoop ();


}
+3  A: 

Don't think of it as "keeping them drawn". Think instead in terms of events and "when do I need to redraw my objects".

For this you will need to handle the resize event and rerun your drawing code. I'm also guessing that your objects might not redraw themselves after another application is moved over them, or if it is moved off screen a little and then brought back. If this is the case you'll need to figure out what events to handle that will cause a redraw as well.

Paul Sasik
how can I rerun the drawing code? Shall I keep a stack with the operations I have done? If so, could you supply an example?
omgzor
@omgzor: My answer is very high-level and conceptual. Check BugKiller and VJo's answers for the detailed specifics. And one more conceptual idea: don't think of it as a stack of operations. Think of a collection (array) of primitives (drawing objects) that need to be redrawn frequently.
Paul Sasik
+3  A: 

First of all , you need to arrange the code. You must have one and only one display function, that clears the buffer,calls the other draw-functions and flushes them to the screen ( or swaps the buffer if you are using a double buffer).

On resizing the window,GLUT will call the Display Function which is renderPoint() as you know :

glutDisplayFunc (renderPoint);

renderPoint clears the buffer before redrawing the "points" , for example :

void renderPoint(void) /*REVISAR ESTO*/
{
    glClear (GL_COLOR_BUFFER_BIT);
    glBegin (GL_POINTS);
        glVertex2f  (-0.98, 0.98);
    glEnd ();
    glFlush ();
}

Since the buffer has been cleared , all the points that are drawn outside the function renderPoint ( the points of circles, the points of lines .. ) has no meaning , because you did not call them from the "main display function which is renderPoint.

How to keep the points on the screen ?

You must store the points ( any point you want to draw ) in a buffer such as an array , a dynamic array, std::vector or anything else. in the display function , write a loop statement to visit each point and extract the x and y .. then draw them.

for example , instead of the above function , replace it with :

class MyPoint {
    public:
    float x;
    float y;
    MyPoint(float x, float y)
    {
        this->x = x;
        this->y = y;
    } };

#include <vector> 
std::vector<MyPoint> testPoints;

void renderPoint(void) /*REVISAR ESTO*/ {
    testPoints.push_back(MyPoint(-0.58,0.58));
    testPoints.push_back(MyPoint(0.58,0.58));
    testPoints.push_back(MyPoint(0.58,-0.58));

    glClear (GL_COLOR_BUFFER_BIT);
    glPointSize(2);

    glBegin (GL_POINTS);
        for(int i=0;i<testPoints.size();i++)
        {
          glVertex2f  (testPoints[i].x, testPoints[i].y);
        }
    glEnd ();

    glFlush (); 
}

as you can see , by using a dynamic array such as (std::vector) to store the points and by using a for loop statement , we able to keep the three points visible.

what else ?

  • do the same method with other shapes , so that for each "mouse click " event , you may add or push_back two points represents a line end points in an array or std::vector called lineArray . in the display function , make a for loop statement to draw each line after extracting the two line points.

  • you should use glutReshapeFunc and glViewport to make sure that the viewport has the same dimenstions as the window after the resizing event. and I think that gluOrtho2d is more elegant than trying to mapping from Windows coordinates space to OpenGL coordinates space

  • arrange your code , so that you just use one display function.

Your program could be Something like this:

void drawTestPoints()
{
  for(int i=0;i<length;i++)
  {
      renderPoint(pointsArray[i].x,pointsArray[i].y);// vector of points/(MyPoint)

  }

}

void drawLines()
{
  for(int i=0;i<length;)
  {

      MyPoint startPoint = linesArray[i];
      MyPoint endPoint = linesArray[i+1];
      bresenham1(startPoint.x,endPoint.x,startPoint.y,endPoint.y);
      i+=2;
  }

}

void drawAll()
{
  glClear (GL_COLOR_BUFFER_BIT);

    drawTestPoints();
    drawLines();
    drawOtherShapes();

  glFlush();
}

.
.
.

// in the main func:
glutDisplayFunc (drawAll);

==

BugKiller
Thanks, very detailed, though I don't know how to arrange this code to use just one display function. What's wrong with the current approach I'm using?
omgzor
For any GLUT apps , there is only one display function that will be called after (resizing/repainting events occur ). so the buffer will be cleared and OpenGL will redraw the points on the buffer. In your way, the points would be removed if the repainting event occured, so use the display function as a main display function which call other functions that draw other shapes, those functions have a for loop statements to draw the points that is stored in an array.
BugKiller
I've added an example in last paragraph of the answer ( to clear what I meant by arrangement).
BugKiller
+2  A: 

Here is a quick example for windows : http://www.falloutsoftware.com/tutorials/gl/gl2.htm

VJo