Draw a Circle in Opengl Java
An Efficient Way to Draw Approximate Circles in OpenGL
Introduction
OpenGL is well known to not posses any method to rasterize non-straight curves. As such, whenever i is required to depict a curve, one has to either rasterize information technology himself (through GL_POINTS) which is slow, or approximate information technology using a number of line segments past drawing a many sided regular polygon. The latter method shall exist explored hither for drawing circles.
It is a well known fact that a regular polygon with a large number of sides looks approximately like a circle when rasterized on the screen. Every bit such, when given a challenge to draw a circle in OpenGL, people more often than not come up with something like this:
void DrawCircle( float cx, float cy, float r, int num_segments) { glBegin(GL_LINE_LOOP); for ( int ii = 0 ; 2 < num_segments; ii++) { bladder theta = 2.0f * 3.1415926f * float (ii) / float (num_segments); float x = r * cosf(theta); float y = r * sinf(theta); glVertex2f(x + cx, y + cy); } glEnd(); }
There are some uncomplicated optimizations that one can do, just nonetheless the general algorithm is as stated. This is very bad algorithm however, since for every signal we accept to perform expensive calls to the trigonometric functions. I propose a method that requires merely the bones arithmetic operations for every point to obtain the same result.
The Algorithm
The inspiration came to me from physics. As we know, when an object moves in a circle it does so entirely because of a constant centripetal acceleration acting on it. When this is the case the object moves at a abiding radial speed. Annotation that that means that we have two abiding length terms (radial speed and centripetal acceleration) which only modify direction, just non length. Since management is piece of cake to compute, it is just a vector, we are in concern.
Hither is the general motility of the point whose position we use to calculate the position of the vertices of our circumvolve. During each calculation, we move the point tangentially to the circle for a fixed distance (segments AK and BL) and so dorsum towards the centre of the circumvolve for a fixed distance (segments KB and LC). At this bespeak we store the location of our betoken (possibly through a call to glVertex) then repeat the process. Our problem, thus, is to calculate the magnitude of the tangential motility, the direction of the tangential move, the magnitude of the radial motion and the management of the radial motion.
We are helped in this task by the following vectors that we know. At the beginning of our calculation we know the vector AO. We can easily plow this vector xc degrees and so it becomes tangent to the circle by flipping the coordinates, while negating i of them. Now, we have a vector that is tangent to the circle, but notwithstanding the length of the radius. To make be the length of AK, we multiply it by a cistron, which is simply the tangent of the theta. This factor is the same for every calculation, and so it tin can be precalculated before entering the loop.
Now, we add the tangential vector to the vector AO and get the vector KO. Nosotros need to get the vector BO from that. Again, we can simply multiply it past a factor which turns out to exist the cosine of theta. Over again, this factor is constant and tin can be precalculated. At this indicate we are back where we started, so we tin just repeat this process once more, and continue to do so until we fill the unabridged circle. Thus, here is the code for this implementation:
void DrawCircle( bladder cx, float cy, float r, int num_segments) { float theta = two * three.1415926 / bladder (num_segments); float tangetial_factor = tanf(theta); float radial_factor = cosf(theta); float x = r; float y = 0 ; glBegin(GL_LINE_LOOP); for ( int two = 0 ; ii < num_segments; ii++) { glVertex2f(x + cx, y + cy); bladder tx = -y; float ty = ten; ten += tx * tangetial_factor; y += ty * tangetial_factor; x *= radial_factor; y *= radial_factor; } glEnd(); }
The algorithm tin can be modified to draw arcs instead of circles. We first calculate the starting indicate from the starting angle. Then we calculate the theta parameter based not on the full circle as before, simply rather the angular span of the arc.
Here is one mode to practice it:
void DrawArc( bladder cx, float cy, bladder r, bladder start_angle, bladder arc_angle, int num_segments) { bladder theta = arc_angle / float (num_segments - one ); float tangetial_factor = tanf(theta); float radial_factor = cosf(theta); float x = r * cosf(start_angle); bladder 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 = 10; x += tx * tangetial_factor; y += ty * tangetial_factor; x *= radial_factor; y *= radial_factor; } glEnd(); }
Other Considerations
Both this and the other algorithms can be greately sped up by using a vertex array, that is then passed as an statement to the glDrawArrays, I get out it upwardly to the reader to implement this picayune optimisation.
You volition notation that to draw the circle yous demand to pass the number of segments to draw. Is information technology possible to create a part to requite a adept estimate of that number so that you get a reasonably smooth circle? Yes there is. Ane way to practise this is to limit to the magnitude of the KB segment in the diagram higher up. The length of that segment is easily derived to be:
( ane - cos(theta)) * r
We gear up that to some minor value and solve for theta. I detect that setting it to 0.25 (i.eastward. a quarter of a pixel) mostly produces acceptable results. Y'all can vary that number as appropriate. The actual formula that yous get involves inverse trigonometric functions, but I establish that the following approximation matches their prediction quite well:
int GetNumCircleSegments( float r) { render 10 * sqrtf(r); }
Lastly, this lawmaking can be adjusted to draw ellipses as well. Simply scale the x and y variables by advisable factors, either using a matrix, or perhaps by modifying the algorithm itself. This extension is trivial.
Determination
This algorithm is significantly faster than any other circle approximation algorithm I have personally encountered on the web. I believe that if one chooses to draw a circle using OpenGL and using a polygonal approximation then my method is optimal. In theory, with a large number of segments the true circumvolve, i.e. compatible with the diamond-exit specification, can exist drawn, although at that betoken it may be 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 elementary repeated rotation. That is indeed correct. All you lot 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 leave it as the excersize to the reader to run across how you can transform the above algorithm into this one:
void DrawCircle( float cx, float cy, float r, int num_segments) { bladder theta = two * 3.1415926 / float (num_segments); float c = cosf(theta); bladder s = sinf(theta); float t; bladder x = r; float y = 0 ; glBegin(GL_LINE_LOOP); for ( int 2 = 0 ; two < num_segments; 2++) { glVertex2f(x + cx, y + cy); t = 10; 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 petty more numerically stable. I employ this version in my current projects, but in principle its your choice which one you like more.
This lawmaking is released equally public domain, but I volition appreciate an email if you do use 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 Abode 2006-2017. All rights reserved. ♫
Source: http://slabode.exofire.net/circle_draw.shtml
0 Response to "Draw a Circle in Opengl Java"
Postar um comentário