This sub draws an ellipse with considerable less cpu consumption than normal brute-force methods. For drawing an ellipse with - say - 128 vertices an un-optimised algorithm spends 258 calls to sin() or cos(). This algorithm only spends 32 trig calls to do the exact same job!!!
Cheers,
Mike
Code: Select all
'' Very fast ellipse drawing function by Michael "h4tt3n" Nissen
'' version 1.0, june 2009
declare sub ellipse(byval x as single, byval y as single, byval a as single, _
byval b as single, byval angle as single, byval col as uinteger)
screenres 800, 600, 16
'' red ellipse
ellipse (400, 300, 399, 299, 0, rgb(255, 32, 32))
'' green ellipse
ellipse (400, 300, 350, 100, -0.2, rgb(32, 255, 32))
'' blue ellipse
ellipse (400, 300, 420, 50, 0.6, rgb(32, 32, 255))
'' yellow circle
ellipse (400, 300, 100, 100, 0.0, rgb(255, 255, 32))
sleep
end
sub ellipse(byval x as single, byval y as single, byval a as single, _
byval b as single, byval angle as single, byval col as uinteger)
'' Very fast ellipse drawing function by Michael "h4tt3n" Nissen
'' version 1.0, june 2009
''
'' Spends less than one eigths as many trig calls as the brute-force method.
''
'' This algorithm requires number-of-vertices / 4 trig calls, whereas the
'' normal brute-force method requires 2 + number-of-vertices * 2 trig calls.
'' these constants decide the graphic quality of the ellipse
const as single pi = 4*atn(1) '' pi
const as single twopi = 2*pi '' two pi (radians in a circle)
const as integer face_length = 8 '' approx. face length in pixels
const as integer max_faces = 4096 '' maximum number of faces in ellipse
const as integer min_faces = 32 '' minimum number of faces in ellipse
'' approx. ellipse circumference (Hudson's method)
dim as single h = (a-b*a-b)/(a+b*a+b)
dim as single circumference = 0.25*pi*(a+b)*(3*(1+h/4)+1/(1-h/4))
'' number of faces in ellipse
dim as integer num_faces = circumference\face_length
'' clamp number of faces
if num_faces > max_faces then num_faces = max_faces
if num_faces < min_faces then num_faces = min_faces
'' keep number of faces divisible by 4
num_faces -= num_faces mod 4
'' get sine and cosine to ellipse angle
dim as single sinangle = sin(angle)
dim as single cosangle = cos(angle)
'' theta (the angle from the ellipse center)
dim as single theta = 0
'' deltatheta (angular step per iteration)
dim as single deltatheta = twopi/num_faces
'' number of iterations
'' (optimised to just 1/4 of the ellipse circumference)
dim as integer num_iterations = num_faces\4
'' dim array for holding cosine theta
dim as single costheta(1 to num_iterations-1)
'' precalc cosine theta
for i as integer = 1 to num_iterations-1
theta += deltatheta
costheta(i) = cos(theta)
next
'' dim arrays to hold cartesian (x, y) coordinates
dim as single _X(num_faces-1), _Y(num_faces-1)
'' iterate
for i as integer = 1 to num_iterations-1
'' predefine handy index numbers
dim as integer j = 2*num_iterations - i
dim as integer k = 2*num_iterations + i
dim as integer l = 4*num_iterations - i
'' precalc a few values
dim as single a_costheta_cosangle = a*costheta(i)*cosangle
dim as single a_costheta_sinangle = a*costheta(i)*sinangle
'' Check this: we get sine theta from already precalced cosine theta!
'' since sin(angle) = cos(0.5*pi - angle). This halves the number of trig calls!
dim as single b_sintheta_sinangle = b*costheta(num_iterations - i)*sinangle
dim as single b_sintheta_cosangle = b*costheta(num_iterations - i)*cosangle
'' convert angles to cartesian (x, y) coordinates
'' Here we reuse the same cos and sin theta values four times.
'' This reduces the already halved number of trig calls to one fourth - or one eighths all in all!
_X(i) = x+a_costheta_cosangle-b_sintheta_sinangle: _Y(i) = y+a_costheta_sinangle+b_sintheta_cosangle
_X(j) = x-a_costheta_cosangle-b_sintheta_sinangle: _Y(j) = y-a_costheta_sinangle+b_sintheta_cosangle
_X(k) = x-a_costheta_cosangle+b_sintheta_sinangle: _Y(k) = y-a_costheta_sinangle-b_sintheta_cosangle
_X(l) = x+a_costheta_cosangle+b_sintheta_sinangle: _Y(l) = y+a_costheta_sinangle-b_sintheta_cosangle
next
'' get coordinates of the four "right angles"
'' (we don't need to call cos / sin theta since it'll always be 0 or 1)
'' This reduces the number of trig calls by two. Not much but it's there.
_X(0*num_iterations) = x+a*cosangle: _Y(0*num_iterations) = y+a*sinangle '' 0.0*pi
_X(1*num_iterations) = x-b*sinangle: _Y(1*num_iterations) = y+b*cosangle '' 0.5*pi
_X(2*num_iterations) = x-a*cosangle: _Y(2*num_iterations) = y-a*sinangle '' 1.0*pi
_X(3*num_iterations) = x+b*sinangle: _Y(3*num_iterations) = y-b*cosangle '' 1.5*pi
'' draw ellipse
for i as integer = 0 to num_faces-1
dim as single j = (i+1) mod num_faces
line (_X(i), _Y(i))-(_X(j), _Y(j)), col '' draw ellipse as closed line loop
'draw string (_X(i), _Y(i)), str(i) '' draw ellipse point numbers
'pset (_X(i), _Y(i)), col '' draw ellipse as set of points
next
end sub