Home > Software engineering >  Exclude points in overlapping area of two circles in OpenGL
Exclude points in overlapping area of two circles in OpenGL

Time:06-20

I want to draw tow circles with the same radii but exclude the overlapped area when drawing.

enter image description here

I want to draw or set dots on gray area.

I implement the mathematical aspect behind it and here is my code:

void draw_venn(){   
        
float radian_to_degree_theta=2 * 3.14 / 360,
          r = 0.5,
          distance=0.3,
          theta=0.0,
          theta2=0.0,
          xR=0.0,
          yR=0.0,
          xG=0.0,
          yG=0.0,
          sum_radii=0,
          dis=0.0;

    sum_radii=r r;
    for (r = 0.5; r >=0; r-=0.001)
    {
        for (float degree = 0; degree < 361; degree =0.1)   
        {   
            theta =degree*radian_to_degree_theta;   
            xR=r*cos(theta) distance;
            yR=r*sin(theta);
            
            xG=r*cos(theta)-distance;
            yG=r*sin(theta);

            dis=sqrt(pow(xR-xG,2)   pow(yR-yG,2));  
            if (dis <= sum_radii)   
            {
                set_point(xR,yR,0.1,1,0,0);
                set_point(xG,yG,0.1,0,1,0);
            }           
            
        }
    }
}
void set_point(float x,float y,float size,float R,float G,float B){
    glPointSize(size);
    glBegin (GL_POINTS);
    glColor3f (R, G, B);
    glVertex2f(x, y);   
    glEnd ();
}
void draw(void)
{
    glClearColor (1, 1, 1, 0);
    glClear (GL_COLOR_BUFFER_BIT);
   
    glPushMatrix(); 

    draw_ven();

    glPopMatrix ();     

    glFlush();
}

int main(int argc, char** argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_SINGLE);
    glutInitWindowSize(1400, 1400);
    glutInitWindowPosition(700, 500);
    glutCreateWindow("GL Sample");
    glutDisplayFunc(draw);
    glutMainLoop();
    return 0;
}

and here is the result:

enter image description here

How can I find if a point is inside the overlapping area?

CodePudding user response:

I reviewed and tested out your code. Trigonometry can get a bit tricky. Following is the "draw_venn" function with some refinements to produce an overlap effect.

void draw_venn()
{
    float   radian_to_degree_theta=2 * 3.141 / 360, 
            r = 0.5, 
            distance=0.3, 
            theta=0.0,
            theta2 = 0.0, 
            xR=0.0, 
            yR=0.0,
            xG=0.0, 
            yG=0.0,
            dis=0.0;

    glPointSize(1);
    glColor3f(1,0,0);
    glBegin(GL_POINTS);
    
    for (r = 0.5; r >=0; r-=0.001)
    {
        for (float degree = 0; degree < 361; degree =0.1)
        {
            theta =degree*radian_to_degree_theta;
            theta2 = (180.0 - degree) * radian_to_degree_theta;
            xR=r*cos(theta2) distance;
            yR=r*sin(theta2);

            xG=r*cos(theta)-distance;
            yG=r*sin(theta);

            dis = sqrt(pow((distance - xG), 2)   pow(yG, 2));
 
            if (dis < 0.5)
            {
                set_point(xR,yR,0.1,0,0,1); /* Color the overlap blue */
                set_point(xG,yG,0.1,0,0,1); /* This works due to symmetry */
            }
            else
            {
                set_point(xR,yR,0.1,1,0,0); /* Set the symmetrical circle colors */
                set_point(xG,yG,0.1,0,1,0);
            }
        }
    }
    glEnd();
}

Pointing out the two significant revisions, first I derive a mirror image value for "theta" and place that into variable "theta2". That is used to draw the red circle. This assures that the circle images are being built in equal but opposite directions so that the coordinates are symmetrical. Second, I revised the formula for checking if the green image coordinates fall within the red circle's outermost radius. Using the Pythagorean theorem calculation for the hypotenuse, the formula determines if the hypotenuse value is smaller than the outermost radius length (0.5). If it is smaller make that point for the green circle blue, and since the circle points are being built and colored symmetrically, also make the corresponding point for the red circle blue.

The result of those revisions is a Venn Diagram showing an overlap.

Venn Diagram

I hope that helps and gives you a springboard to proceed.

Regards.

CodePudding user response:

  1. Add 8bit Stencil Buffer to your context

    I do it by setting up pixelformat like this:

     PIXELFORMATDESCRIPTOR pfd;
     ZeroMemory( &pfd, sizeof( pfd ) );      // set the pixel format for the DC
     pfd.nSize = sizeof( pfd );
     pfd.nVersion = 1;
     pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
     pfd.iPixelType = PFD_TYPE_RGBA;
     pfd.cColorBits = 24;
     pfd.cDepthBits = 24;
     pfd.cStencilBits = 8;
     pfd.iLayerType = PFD_MAIN_PLANE;
     SetPixelFormat(hdc,ChoosePixelFormat(hdc, &pfd),&pfd);
    
  2. Enable and Clear Stencil with 0 and setup it for incrementation

  3. Disable color and depth output

  4. Render circles

  5. Enable color and depth output

  6. Set up Stencil test to not equal 2

    You can also use equal to 1 in case you overlap more than just 2 objects

  7. Render circles

  8. Disable Stencil test

I see it like this:

//---------------------------------------------------------------------------
void glCircle(float x,float y,float r)
    {
    int e;
    float a,da=2.0*M_PI/72.0;
    glBegin(GL_TRIANGLE_FAN);
    glVertex2f(x,y);
    for (e=1,a=0.0;e;a =da)
        {
        if (a>=2.0*M_PI) { e=0; a=0.0; }
        glVertex2f(x (r*sin(a)),y (r*cos(a)));
        }
    glEnd();
    }
//---------------------------------------------------------------------------
void gl_draw()
    {
    glClearColor(1.0,1.0,1.0,1.0);
    glClear(GL_COLOR_BUFFER_BIT);

    float aspect=float(xs)/float(ys);   //xs,ys is screen resolution
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glScalef(1.0,aspect,1.0);
    glMatrixMode(GL_TEXTURE);
    glLoadIdentity();
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glDisable(GL_DEPTH_TEST);
    glDisable(GL_TEXTURE_2D);

    // turn off color,depth
    glStencilMask(0xFF);
    glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
    glDepthMask(GL_FALSE);
    // clear stencil and setup it for increment
    glEnable(GL_STENCIL_TEST);
    glClearStencil(0);
    glClear(GL_STENCIL_BUFFER_BIT);
    glStencilFunc(GL_ALWAYS,1,0xFF);
    glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
    // render stencil
    glCircle(-0.3,0.0,0.6);
    glCircle( 0.3,0.0,0.6);
    // turn on color,depth
    glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
    glDepthMask(GL_TRUE);
    // render screen (where Stencil is not 2)
    glStencilFunc(GL_NOTEQUAL,2,0xFF);
    glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
    glColor3f(1.0,0.0,0.0); glCircle(-0.3,0.0,0.6);
    glColor3f(0.0,1.0,0.0); glCircle( 0.3,0.0,0.6);
    glDisable(GL_STENCIL_TEST);

    glFlush();
    SwapBuffers(hdc);
    }
//---------------------------------------------------------------------------

And output:

preview

In case you also want to know if pixel is inside both circles you can use:

GLint a;
glReadPixels(X,ys-1-Y,1,1,GL_STENCIL_INDEX,GL_INT,&a);
if (a==2); // X,Y is inside both circles
else;      // X,Y is not inside both circles

In case You insist on rendering the stuff pixel by pixel then do it at least properly As your current approach is horibly slow for many reasons... For example you can do this like this:

glClearColor(1.0,1.0,1.0,1.0);
glClear(GL_COLOR_BUFFER_BIT);
glDisable(GL_DEPTH_TEST);
glDisable(GL_TEXTURE_2D);

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glTranslatef(-1.0,-1.0,0.0);
glScalef(2.0/xs,2.0/ys,1.0);
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

int x0=(4*xs)/10,y0=ys/2,r0=xs/4;   // circle0
int x1=(6*xs)/10,y1=ys/2,r1=xs/4;   // circle1
int x,y,xx0,xx1,yy0,yy1,rr0=r0*r0,rr1=r1*r1;
glBegin(GL_POINTS);
glColor3f(1.0,0.0,0.0);
for (x=-r0;x<=r0;x  ){ xx0=x; xx0*=xx0; xx1=x x0-x1; xx1*=xx1;
 for (y=-r0;y<=r0;y  ){ yy0=y; yy0*=yy0; yy1=y y0-y1; yy1*=yy1;
  if (xx0 yy0<=rr0)
   if (xx1 yy1>=rr1)
    glVertex2i(x0 x,y0 y); }}
glColor3f(0.0,1.0,0.0);
for (x=-r1;x<=r1;x  ){ xx1=x; xx1*=xx1; xx0=x x1-x0; xx0*=xx0;
 for (y=-r1;y<=r1;y  ){ yy1=y; yy1*=yy1; yy0=y y1-y0; yy0*=yy0;
  if (xx1 yy1<=rr1)
   if (xx0 yy0>=rr0)
    glVertex2i(x1 x,y1 y); }}
glEnd();

glFlush();
SwapBuffers(hdc);

Where xs,ys is GL screen resolution.

See related:

  • Related