11

I have a set of points and each one has an area of "influence" or essentially a radius. I would like to be able to draw each one of these influence circles for all the points as a simple circular line.

They will overlap however I wish to draw the outside of the shape formed. I know this would probably require me working out where they intersect and somehow forming a total shape to draw. The problem is that some points might not even touch others! So I need to be able to work that out too.

I have attempted to illustrate what I mean simply:

enter image description here

Note that I wish to draw simply the black line, no fill. This is because I wish background images and other geometry to show through.

I would be doing this in openGL so the circle would be probably made using GL_LINES or some such with various vertices forming the curves but I really just don't have any idea about how I would work out this perimeter.

If anyone has any advice or could point me at how I might go about working this out it would be greatly appreciated!

This might be more of a maths question, I am not looking for bits of code but actually how to go about working out these shapes. I just can't think about how to do it!

*****Edit: with the solution I came up with, hopefully might help someone else!

So I used the suggested ideas and basically decided the best way would be to draw using the stencil buffer. This now means that I loop through my points 3 times but I need to do some careful sorting of them to find only visible ones anyway.

So code wise I now have this:

private void stencilCircleAroundStars()
{
    //Lets try and draw something here using stencil
    glColorMask(false, false, false, false); //Disable colour mask
    glEnable(GL_STENCIL_TEST); // Enable Stencil Buffer For "marking" the outer circle
    glDisable(GL_DEPTH_TEST);// Disable Depth Testing

    for (Object value : stars.values())
    {
        Star star = (Star)value;

        glStencilFunc(GL_ALWAYS, 1, 1); // Always Passes, 1 Bit Plane, 1 As Mask
        glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); // We Set The Stencil Buffer To 1 Where We Draw Any Polygon

        //Draw the large circle
        starOb.location.copy(star.location);
        starOb.setScale(2000);
        starOb.draw();
    }

    for (Object value : stars.values())
    {
        Star star = (Star)value;
        //Now we change the functions and remove a slightly smaller circle from buffer.
        glStencilFunc(GL_ALWAYS, 0, 0); // Always passes, 0 bit plane, 0 as mask;
        glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); // We Set The Stencil Buffer To 0 Where We Draw Any Polygon
        starOb.location.copy(star.location);
        starOb.setScale(1900);
        starOb.draw();
    }

    //Now we enable the colour
    glColorMask(true, true, true, true);
    glStencilFunc(GL_EQUAL, 1, 1); // We Draw Only Where The Stencil Is 1
    glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // Don't Change The Stencil Buffer

    glColor4f(0.5f, 1.0f, 0.5f, 0.5f);
    for (Object value : stars.values())
    {
        Star star = (Star)value;
        starOb.location.copy(star.location);
        starOb.setScale(2000);
        starOb.draw();
    }

    //Now we are done .. disable
    glDisable(GL_STENCIL_TEST);  
}

My points are in essence entities called "stars" for the purpose of my program and StarOb is a collection of quads I have loaded from a file which form a nice smooth circle.

I disable colour mask and I loop through once, drawing the largest circle I can into the stencil buffer and setting a value of 1. I then loop around again drawing the smaller scaled circle into the stencil buffer but this time setting a value of 0. This should leave a border around any star that is not touching other stars and will effectively remove where they overlap.

I finally re-enable the colour mask and actually draw the coloured circles. the stencil buffer stops the insides from being rendered and I get what I wanted! I then disable the stencil buffer.

If you really wanted to see it, here's a video of generating several increasing amounts of points: Video of it running

Here is a low quality version of how it came out (background was not drawn while testing):

Overlapping circles with centre not drawn due to stencil

user1118321
  • 25,567
  • 4
  • 55
  • 86
iexus
  • 677
  • 11
  • 26

2 Answers2

6

First, imagine the background was not there. I'm pretty sure you'd know how to do it, draw each circle then draw their insides (as in a filled circle) to remove the arcs that are inside.

Now to do the same over an image, you could do either of these things. One thing you can do is to disable writing on the color buffer, do that procedure and change the stencil buffer. Then enable writing on the color buffer and draw a whole screen rectangle which consequently fills the pixels you have marked in the stencil buffer.

The stencil buffer may not be usable to you however for a couple of reasons, such as you are using it for something else. In this case, an alternative would be to do the same thing, but instead of rendering in the stencil buffer, you render in a texture. Then bind that texture and draw a rectangle on the screen.

I'm quite certain you could achieve this with the accumulation buffer too, but I have never used it so I can't really tell (if anyone knows about that, please edit my answer and tell us how)

Shahbaz
  • 46,337
  • 19
  • 116
  • 182
  • Thank you for your suggestion I've managed to do it with the stencil buffer and came back to update my question with my solution which is kinda what you were suggesting. Not sure who I should tick now! – iexus Sep 20 '11 at 23:17
  • 1
    Maybe give both of us a +1? :D – Shahbaz Sep 20 '11 at 23:22
  • Done :D I've updated my question to show what I ended up with. Was that the sort of way you were thinking of? I'm a bit lacking in opengl knowledge.. – iexus Sep 20 '11 at 23:26
  • OpenGL has a LOT of wonderful features. I have been writing OpenGL code on and off for 4~5 years now and still see things that make me wonder (I mean they are wonderful)! – Shahbaz Sep 20 '11 at 23:30
  • I just realised it would also probably be better instead of activate the colour mask and looping through one final time and drawing individual circles again I could just draw a full screen rectangle as the stencil buffer would only let it through in the right places? Might be quicker for it to do .. unsure. – iexus Sep 21 '11 at 11:19
  • That is true. In fact if you had read my answer carefully, this is what you would have found: _and draw a whole screen rectangle which consequently fills the pixels you have marked in the stencil buffer_ – Shahbaz Sep 21 '11 at 14:24
3

Two passes:

  1. Draw all your circle outlines via GL_LINES or GL_LINE_LOOP. Make sure you set your glLineWidth() to 3 or greater.
  2. Draw filled circles (GL_TRIANGLE_STRIP can be useful) with your "background" color and the same radius as the circles from step 1.

The filled circles in step 2 will overwrite all the outline pixels from step 1 but only where they overlap. That leaves you with non-overlapping outlines intact.

If you want a wider range of outline widths (i.e., greater than the ~10 that most OpenGL implementations allow glLineWidth()) you should reuse the filled-circle renderer from step 2 in step 1 except with a larger radius.

genpfault
  • 51,148
  • 11
  • 85
  • 139
  • The reason I wanted to only draw the outline was to allow a background image (and also various layers / previously drawn entities) to show through. If I give up on that then this would be the first choice I would have made! – iexus Sep 20 '11 at 19:26
  • 2
    I'd use the procedure above, but draw the circles to the stencil buffer, and enable stencil buffer testing when drawing the rest of your geometry. – Jerry Coffin Sep 20 '11 at 20:29
  • Ah I'll be honest I had not thought of that. I'm not very familiar with it so I will do a bit of reading. I assume you do something like, draw the larger circle a certain colour / value into stencil and the smaller to a different one also in stencil. If the value when stencil testing is the largeValue then colour the fragment and if not then do nothing? – iexus Sep 20 '11 at 21:29
  • Can't edit my comment, but I've edited my question to show what I ended up with! – iexus Sep 20 '11 at 23:27