Question

Does anyone know some algorithm to calculate the number of sides required to approximate a circle using polygon, if radius, r of the circle and maximum departure of the polygon from circularity, D is given? I really need to find the number of sides as I need to draw the approximated circle in OpenGL.

Also, we have the resolution of the screen in NDC coordinates per pixel given by P and solving D = P/2, we could guarantee that our circle is within half-pixel of accuracy.

Was it helpful?

Solution

What you're describing here is effectively a quality factor, which often goes hand-in-hand with error estimates.

A common way we handle this is to calculate the error for a a small portion of the circumference of the circle. The most trivial is to determine the difference in arc length of a slice of the circle, compared to a line segment joining the same two points on the circumference. You could use more effective measures, like difference in area, radius, etc, but this method should be adequate.

Think of an octagon, circumscribed with a perfect circle. In this case, the error is the difference in length of the line between two adjacent points on the octagon, and the arc length of the circle joining those two points.

enter image description here

The arc length is easy enough to calculate: PI * r * theta, where r is your radius, and theta is the angle, in radians, between the two points, assuming you draw lines from each of these points to the center of the circle/polygon. For a closed polygon with n sides, the angle is just (2*PI/n) radians. Let the arc length corresponding to this value of n be equal to A, ie A=2*PI*r/n.

The line length between the two points is easily calculated. Just divide your circle into n isosceles triangles, and each of those into two right-triangles. You know the angle in each right triangle is theta/2 = (2*PI/n)/2 = (PI/n), and the hypotenuse is r. So, you get your equation of sin(PI/n)=x/r, where x is half the length of the line segment joining two adjacent points on your circumscribed polygon. Let this value be B (ie: B=2x, so B=2*r*sin(PI/n)).

Now, just calculate the relative error, E = |A-B| / A (ie: |TrueValue-ApproxValue|/|TrueValue|), and you get a nice little percentage, represented in decimal, of your error vector. You can use the above equations to set a constraint on E (ie: it cannot be greater than some value, say, 1.05), in order for it to "look good".

So, you could write a function that calculates A, B, and E from the above equations, and loop through values of n, and have it stop looping when the calculated value of E is less than your threshold.

OTHER TIPS

I would say that you need to set the number of sides depending on two variables the radius and the zoom (if you allow zoom)

A circle or radius 20 pixels can look ok with 32 to 56 sides, but if you use the same number of sided for a radios of 200 pixels that number of sides will not be enough

numberOfSides = radius * 3

If you allow zoom in and out you will need to do something like this

numberOfSides = radiusOfPaintedCircle * 3

When you zoom in radiusOfPaintedCircle will be bigger that the "property" of the circle being drawn

I've got an algorithm to draw a circle using fixed function opengl, maybe it'll help?

It's hard to know what you mean when you say you want to "approximate a circle using polygon"

You'll notice in my algorithm below that I don't calculate the number of lines needed to draw the circle, I just iterate between 0 .. 2Pi, stepping the angle by 0.1 each time, drawing a line with glVertex2f to that point on the circle, from the previous point.

void Circle::Render()
{   
glLoadIdentity();

glPushMatrix();

    glBegin(GL_LINES);
        glColor3f(_vColour._x, _vColour._y, _vColour._z);
        glVertex3f(_State._position._x, _State._position._y, 0);
        glVertex3f( 
            (_State._position._x + (sinf(_State._angle)*_rRadius)), 
            (_State._position._y + (cosf(_State._angle)*_rRadius)),
            0
        );
    glEnd();

    glTranslatef(_State._position._x, _State._position._y, 0);
    glBegin(GL_LINE_LOOP);
        glColor3f(_vColour._x, _vColour._y, _vColour._z);
        for(float angle = 0.0f; angle < g_k2Pi; angle += 0.1f)
            glVertex2f(sinf(angle)*_rRadius,  cosf(angle)*_rRadius);
    glEnd();                        

glPopMatrix();

}

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top