Draw a Circle Line_loop



An Efficient Style to Draw Judge Circles in OpenGL


Introduction

OpenGL is well known to not posses any method to rasterize non-directly curves. Every bit such, whenever one is required to depict a curve, i has to either rasterize it himself (through GL_POINTS) which is slow, or approximate it using a number of line segments by drawing a many sided regular polygon. The latter method shall be explored here for drawing circles.

It is a well known fact that a regular polygon with a big number of sides looks approximately like a circle when rasterized on the screen. Every bit such, when given a challenge to describe a circumvolve in OpenGL, people generally come up with something like this:


        void        DrawCircle(        float        cx,                  bladder        cy,                  float        r,                  int        num_segments)  {        glBegin(GL_LINE_LOOP);                  for        (        int        2          =                  0        ;        2          <        num_segments;        two++)  	{                  float        theta          =                  2.0f                  *                  3.1415926f                  *                  float        (ii) /                  bladder        (num_segments);                  bladder        x          =        r          *        cosf(theta);                  float        y          =        r          *        sinf(theta);        glVertex2f(x          +        cx,        y          +        cy);                  }        glEnd();  }      

At that place are some uncomplicated optimizations that one can do, but notwithstanding the general algorithm is every bit stated. This is very bad algorithm however, since for every signal we have to perform expensive calls to the trigonometric functions. I propose a method that requires only the basic arithmetics operations for every point to obtain the same event.

The Algorithm

The inspiration came to me from physics. Equally nosotros know, when an object moves in a circumvolve it does so entirely considering of a abiding centripetal acceleration acting on information technology. When this is the instance the object moves at a abiding radial speed. Note that that means that we have two constant length terms (radial speed and centripetal acceleration) which simply change direction, but not length. Since management is like shooting fish in a barrel to compute, information technology is simply a vector, nosotros are in business.

Here is the general motion of the point whose position we apply to summate the position of the vertices of our circle. During each calculation, we move the signal tangentially to the circle for a fixed distance (segments AK and BL) and then dorsum towards the centre of the circle for a stock-still distance (segments KB and LC). At this point we shop the location of our bespeak (perhaps through a call to glVertex) and and then repeat the procedure. Our problem, thus, is to calculate the magnitude of the tangential motion, the direction of the tangential motion, the magnitude of the radial motion and the management of the radial motion.

We are helped in this task by the following vectors that nosotros know. At the starting time of our calculation nosotros know the vector AO. We can easily plough this vector 90 degrees so information technology becomes tangent to the circle by flipping the coordinates, while negating one of them. Now, we have a vector that is tangent to the circle, but still the length of the radius. To make be the length of AK, we multiply it by a factor, which is just the tangent of the theta. This cistron is the same for every calculation, so it tin can be precalculated before entering the loop.

Now, nosotros add the tangential vector to the vector AO and get the vector KO. We need to get the vector BO from that. Once again, we can just multiply it by a factor which turns out to be the cosine of theta. Again, this factor is abiding and can be precalculated. At this point nosotros are back where we started, so nosotros tin can simply echo this process once again, and continue to do so until we fill the entire circle. Thus, here is the code for this implementation:


        void        DrawCircle(        float        cx,                  float        cy,                  float        r,                  int        num_segments)  {                  float        theta          =                  2                  *                  iii.1415926                  /                  float        (num_segments);                  float        tangetial_factor          =        tanf(theta);                  float        radial_factor          =        cosf(theta);                  float        10          =        r;                  float        y          =                  0        ;        glBegin(GL_LINE_LOOP);                  for        (        int        two          =                  0        ;        ii          <        num_segments;        ii++)  	{        glVertex2f(10          +        cx,        y          +        cy);                  bladder        tx          = -y;                  float        ty          =        ten;        ten          +=        tx          *        tangetial_factor;        y          +=        ty          *        tangetial_factor;        10          *=        radial_factor;        y          *=        radial_factor;  	}        glEnd();  }      

The algorithm can exist modified to draw arcs instead of circles. Nosotros first calculate the starting point from the starting bending. Then nosotros calculate the theta parameter based not on the full circumvolve equally before, simply rather the angular span of the arc.
Hither is one style to do information technology:
        void        DrawArc(        float        cx,                  float        cy,                  float        r,                  float        start_angle,                  float        arc_angle,                  int        num_segments)  {                  bladder        theta          =        arc_angle          /                  bladder        (num_segments          -                  i        );                  float        tangetial_factor          =        tanf(theta);                  float        radial_factor          =        cosf(theta);                  float        x          =        r          *        cosf(start_angle);                  float        y          =        r          *        sinf(start_angle);        glBegin(GL_LINE_STRIP);                  for        (        int        ii          =                  0        ;        ii          <        num_segments;        ii++) 	{        glVertex2f(x          +        cx,        y          +        cy);                  float        tx          = -y;                  float        ty          =        x;        x          +=        tx          *        tangetial_factor;        y          +=        ty          *        tangetial_factor;        x          *=        radial_factor;        y          *=        radial_factor;  	}        glEnd();  }      

Other Considerations

Both this and the other algorithms tin be greately sped up by using a vertex array, that is then passed as an argument to the glDrawArrays, I leave information technology up to the reader to implement this trivial optimisation.

Yous volition note that to draw the circle you lot demand to pass the number of segments to draw. Is it possible to create a function to requite a good guess of that number then that you get a reasonably polish circle? Yes in that location is. Ane way to practise this is to limit to the magnitude of the KB segment in the diagram above. The length of that segment is easily derived to be:


        (        ane                  -        cos(theta)) *        r

We set that to some small value and solve for theta. I discover that setting it to 0.25 (i.e. a quarter of a pixel) generally produces acceptable results. You lot tin vary that number as advisable. The bodily formula that you lot go involves inverse trigonometric functions, but I found that the following approximation matches their prediction quite well:


        int        GetNumCircleSegments(        float        r)  {                  return                  10                  *        sqrtf(r);        }      

Lastly, this code can exist adjusted to describe ellipses also. Just scale the x and y variables by advisable factors, either using a matrix, or perhaps past modifying the algorithm itself. This extension is petty.

Conclusion

This algorithm is significantly faster than any other circle approximation algorithm I have personally encountered on the spider web. I believe that if 1 chooses to describe a circle using OpenGL and using a polygonal approximation then my method is optimal. In theory, with a large number of segments the true circle, i.e. compatible with the diamond-exit specification, can be drawn, although at that point it may exist preferable to rasterize the circle using GL_POINTS manually.

Recasting the Algorithm as a Repeated Rotation

Someone suggested to me that this algorithm can be recast equally a simple repeated rotation. That is indeed correct. All yous take to do is multiply the whole algorithm by a few trig functions, and obtain something that just looks like the repeated application of the rotation matrix. I exit it as the excersize to the reader to run across how you lot can transform the in a higher place algorithm into this 1:

        void        DrawCircle(        float        cx,                  float        cy,                  float        r,                  int        num_segments)  {                  float        theta          =                  2                  *                  three.1415926                  /                  float        (num_segments);                  float        c          =        cosf(theta);                  bladder        s          =        sinf(theta);                  float        t;                  float        x          =        r;                  float        y          =                  0        ;        glBegin(GL_LINE_LOOP);                  for        (        int        ii          =                  0        ;        ii          <        num_segments;        ii++)  	{        glVertex2f(x          +        cx,        y          +        cy);        t          =        x;        x          =        c          *        x          -        s          *        y;        y          =        s          *        t          +        c          *        y; 	}        glEnd();  }      

The advantages of this version of the algorithm is that it is easier to understand (repeated rotation instead of the physics inspired mumbo jumbo) and at a glance it seems to be a little more numerically stable. I employ this version in my current projects, but in principle its your selection which ane you like more than.

This code is released equally public domain, but I volition appreciate an email if you lot practice utilise information technology somewhere. Feel free to email me for clarifications of the algorithm and to written report bugs and whatnot.




Original content Copyright SiegeLord's Dwelling house 2006-2017. All rights reserved.   ♫

web analytics

phillipsnestandmand.blogspot.com

Source: http://slabode.exofire.net/circle_draw.shtml

0 Response to "Draw a Circle Line_loop"

ارسال یک نظر

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel