Drawing Rotated Ellipses 
 
Disclaimer:  All of the algorithms here are not original with me.  I have collected them from various internet web sites several years ago. 
 
  For those of you that have played my game "Polygon War" may have noticed that some of the objects use rotating ellipses.   Anyone that is familiar with programming windows and the GDI realize  there's no function to draw a rotated ellipse.  That is unfortunate because drawing an ellipse oriented at any angle is really awesome but the solution is to create our own function.  Instead of jumping right into it, let's start by drawing a line. 
 
  There's two different kind of computer graphics that I know about which is vector graphics and raster graphics.  Back in the 80's there were a few arcade games that used vector graphics, such as asteroids, star wars, battle zone, etc.....  The graphics were composed entirely of straight lines;  just like the lines that you draw on paper using a pen and ruler.   I've  seen some computer systems in industrial companies use vector graphics and I think ocillascopes use vector graphics.  But the vast majority of computers use raster graphics. 
   
  Raster graphics is just like graph paper.  The image or picture is composed of rows and columns of individual squares called pixels.  Each pixel represents a single unit of the image which has its own unique color.  And each pixel represents a byte, or up to four bytes of computer memory called a double word, depending on the color depth of the image. 
  
   Of course the advantage of raster graphics is being able to draw solid detailed objects but it makes it tricky to program.  Since raster graphics are composed of rows and columns of squares you can't draw an exact line or a curve shown in the following picture.  
 
    
 
 As you can see, a line in raster graphics is composed of many short line segments and squares which approximates a line.  In low resolutions it looks very obvious but in high resolution a raster line will more closely resemble a real line.  So how do you program a computer to draw lines in raster graphics?  A college professor back in the early 60's came up with a slick algorithm called the Bresenham line drawing algorithm, named after him.   
 
 Here it is coded in PowerBasic.   
 
SUB DrawLine (dl AS lines) 
 
   LOCAL a AS LONG, b AS LONG 
   LOCAL g AS LONG 
   LOCAL delt1 AS LONG, delt2 AS LONG 
   LOCAL nxt AS LONG, nyt AS LONG 
   LOCAL lp AS LONG 
   LOCAL adr AS LONG 
   LOCAL x AS LONG,y AS LONG,enx AS LONG,eny AS LONG,color AS DWORD 
 
   x=dl.x               'Prepare the line 
   y=dl.y 
   enx=dl.enx 
   eny=dl.eny 
   color=dl.color 
   IF eny<y THEN 
        a=y-eny 
     ELSE 
        a=eny-y 
   END IF 
   IF enx<x THEN 
       b=x-enx 
     ELSE 
       b=enx-x 
   END IF 
   IF a<=b THEN 
       g=2*a-b 
       delt1=2*(a-b) 
       delt2=2*a 
     ELSE 
       g=2*b-a 
       delt1=2*(b-a) 
       delt2=2*b 
   END IF 
   IF enx<x THEN 
       nxt=-1 
    ELSE 
       nxt=1 
   END IF 
   IF eny<y THEN 
       nyt=-1 
    ELSE 
       nyt=1 
   END IF 
 
   IF a<b THEN                  'Draw the line. 
       FOR lp=0 TO b-1 
           IF g>0 THEN 
               g=g+delt1 
               x=x+nxt 
               y=y+nyt 
             ELSE 
               g=g+delt2 
               x=x+nxt 
           END IF 
           CALL pixel(x,y,color)    'Draw single pixel of the line. 
       NEXT lp 
    ELSE 
       FOR lp=0 TO a-1 
           IF g>0 THEN 
               g=g+delt1 
               y=y+nyt 
               x=x+nxt 
             ELSE 
               g=g+delt2 
               y=y+nyt 
           END IF 
           CALL pixel(x,y,color)  'Draw single pixel of the line. 
       NEXT lp 
   END IF 
END SUB                           
 
 As you can see,  the line drawing Bresenham algorithm uses only integer addition and subtraction making it very fast.  Now on to circles. 
 
                                                   
 
 As with drawing lines using raster graphics,  raster circles are represented as a number of short line segments and squares producing just an approximation of a true circle.  Of  course the bigger the circle, the better it represents a true circle.   A very small circle will look more like a diamond.    
  In raster graphics the circle consists of eight symmetrical arcs (each arc is 45 degrees).  So we need to only calculate the pixels for 1/8th of the circle which is 45 degrees which is done in the following program which makes the circle come out better anyway in raster graphics. 
  If you were to calculate all the pixels for the circle (which I tried), the circle wouldn't look perfectly symmetrical which is due to the finite resolution of raster graphics. 
  Here is the Bresenham algorithm for a circle coded in PowerBasic. 
 
 
SUB circle (BYVAL cx AS LONG,BYVAL cy AS LONG,BYVAL radius AS DWORD, color AS DWORD) 
 
   LOCAL x AS LONG, y AS LONG, p AS LONG 
   LOCAL lp AS LONG 
 
   x=-1 
   y=radius 
   p=(5-radius*4)/4 
 
   WHILE x<=y 
       INCR x 
       IF p<0 THEN 
           p=p+2*x+1 
        ELSE 
           DECR y 
           p=p+2*(x-y)+1 
       END IF 
 
       SELECT CASE x 
           CASE 0 
               CALL pixel(cx,cy+y,color) 
               CALL pixel(cx,cy-y,color) 
               CALL pixel(cx+y,cy,color) 
               CALL pixel(cx-y,cy,color) 
               EXIT SELECT 
           CASE y 
               CALL pixel(cx+x,cy+y,color) 
               CALL pixel(cx-x,cy+y,color) 
               CALL pixel(cx+x,cy-y,color) 
               CALL pixel(cx-x,cy-y,color) 
               EXIT SELECT 
           CASE <y 
               CALL pixel(cx+x,cy+y,color) 
               CALL pixel(cx-x,cy+y,color) 
               CALL pixel(cx+x,cy-y,color) 
               CALL pixel(cx-x,cy-y,color) 
               CALL pixel(cx+y,cy+x,color) 
               CALL pixel(cx-y,cy+x,color) 
               CALL pixel(cx+y,cy-x,color) 
               CALL pixel(cx-y,cy-x,color) 
 
       END SELECT 
   WEND 
END SUB       
 
 Moving on,  next is drawing an axis aligned ellipse. 
 
 
 
 The axis aligned ellipse looks like a circle that has been streched or flattened, however you look at it.  This makes it a bit more complicated to draw since the X-axis is longer or shorter than the Y-axis.  But using the Bresenham algorithm, we can avoid using any slow, high level math functions and stick with integer addition, subtraction and mutliplication.   
  The axis aligned ellipse consists of four symmetrical arcs so the following algorithm coded in PowerBasic only computes one quarter of the ellipse and uses symmetry (mirror images or reflection) to draw the remaining three arcs.       
 
 
SUB Ellipse1(BYVAL xc AS LONG,BYVAL yc AS LONG,_ 
           BYVAL a AS LONG,BYVAL b AS LONG,BYVAL color AS DWORD) 
 
           LOCAL a2 AS DWORD, b2 AS DWORD 
           LOCAL x AS LONG,y AS LONG 
           LOCAL S AS LONG,T AS LONG 
 
           a2=a*a 
           b2=b*b 
           x=0 
           y=b 
           s=a2*(1-2*b)+2*b2 
           t=b2-2*a2*(2*b-1) 
 
           CALL pixel(xc+x,yc+y,color) 
           CALL pixel(xc-x,yc+y,color) 
           CALL pixel(xc-x,yc-y,color) 
           CALL pixel(xc+x,yc-y,color) 
           DO 
               IF s<0 THEN 
                   s=s+2*b2*(2*x+3) 
                   t=t+4*b2*(x+1) 
                   INCR x 
                ELSE 
                   IF t<0 THEN 
                       s=s+2*b2*(2*x+3)-4*a2*(y-1) 
                       t=t+4*b2*(x+1)-2*a2*(2*y-3) 
                       INCR x 
                       DECR y 
                    ELSE 
                       s=s-4*a2*(y-1) 
                       t=t-2*a2*(2*y-3) 
                       DECR y 
                   END IF 
               END IF 
               CALL pixel(xc+x,yc+y,color) 
               CALL pixel(xc-x,yc+y,color) 
               CALL pixel(xc-x,yc-y,color) 
               CALL pixel(xc+x,yc-y,color) 
           LOOP UNTIL y=0 
 
END SUB        
 
 Now onto what  you've been waiting for;  drawing ellipses oriented at any angle! 
 
 
 
 
  This is an ellipse rotated twenty degrees from the X-axis or 0.34 radians.  Looking at the source code you'll notice it uses four trig functions but only once.  These aren't used to draw the actual ellipse but just to find the two points where the major and minor axis of the ellipse ends which of course determines the shape of the ellipse as in an axis aligned ellipse.  
  This ellipse algorithm was originally coded  to use 64 bit integers.  The numbers involded in drawing a rotated ellipse get too big for 32 bit integers to hold.  Well, I quickly found out that 64 bit intergers isn't sufficient either if you're drawing fairly large ellipses in high resolution graphic modes.  I switched to 32 bit floating point or in PowerBasic it's called single precision.  A 32bit CPU handles 32 bit floating point numbers much faster than 64 bit integers anyway and can hold huge numbers due to an exponent.   
  This algorithm will draw a circle or an axis aligned ellipse.  Circles and axis aligned ellipses are just special cases of a general ellipse but since this algorithm is more mathematically intensive, it will take a little longer.  Plus, a rotated ellipse drawn in raster graphics has only two symmetrical arcs,  so half of the points in the ellipse have to be calculated. 
  So here it is.  The rotated ellipse algorithm coded in PowerBasic.  Just a note here....  these programs will only draw an outline of a circle, ellipse, and a rotated ellipse.  In my Polygon War source code near the end, there is code that draws solid circles & ellipses.  The source code is included in the download.     
 
 
SUB GeneralEllipse (BYVAL iXC AS LONG,BYVAL iYC AS LONG,_ 
                   BYVAL A AS LONG,BYVAL B AS LONG,_ 
                   BYVAL angle AS SINGLE,BYVAL color AS DWORD) 
 
   LOCAL ixa AS SINGLE, iya AS SINGLE 
   LOCAL ixb AS SINGLE, iyb AS SINGLE 
   LOCAL ixa2 AS SINGLE, iya2 AS SINGLE 
   LOCAL ixb2 AS SINGLE, iyb2 AS SINGLE 
   LOCAL ixaya AS SINGLE, ixbyb AS SINGLE 
   LOCAL ila2 AS SINGLE, ila4 AS SINGLE 
   LOCAL ilb2 AS SINGLE, ilb4 AS SINGLE 
   LOCAL ia AS SINGLE, ib AS SINGLE, ic AS SINGLE, id AS SINGLE 
   LOCAL idx AS SINGLE, idy AS SINGLE, isigma AS SINGLE 
   LOCAL ixp1 AS SINGLE 
   LOCAL temp AS SINGLE 
   LOCAL xflag AS DWORD 
   LOCAL pi AS SINGLE 
 
   pi=3.1415926 
   IF angle <pi/2 THEN 
         xflag=1 
   ELSEIF angle>pi/2 AND angle<pi THEN 
         temp=angle-pi/2 
         angle=pi/2-temp 
         xflag=-1 
   ELSEIF angle>=pi AND angle<pi*1.5 THEN 
         angle=angle-pi 
         xflag=1 
   ELSEIF angle>=pi*1.5 AND angle<pi*2 THEN 
         angle=angle-pi 
         temp=angle-pi/2 
         angle=pi/2-temp 
         xflag=-1 
   END IF 
 
   ixa=COS(angle)*A 
   iya=SIN(angle)*A 
   ixb=COS(angle+(pi/2))*B 
   iyb=SIN(angle+(pi/2))*B 
 
   ixa2=ixa*ixa 
   iya2=iya*iya 
   ixb2=ixb*ixb 
   iyb2=iyb*iyb 
   ixaya=ixa*iya 
   ixbyb=ixb*iyb 
   ila2=ixa2+iya2 
   ila4=ila2*ila2 
   ilb2=ixb2+iyb2 
   ilb4=ilb2*ilb2 
   ia=ixa2*ilb4+ixb2*ila4 
   ib=ixaya*ilb4+ixbyb*ila4 
   ic=iya2*ilb4+iyb2*ila4 
   id=ila4*ilb4 
 
   LOCAL ix AS LONG,iy AS LONG, iym1 AS LONG,iyp1 AS LONG 
 
 
   IF ( iYA <= iXA ) THEN 
 
       ' start AT (-xA,-yA) 
       iX = -iXA 
       iY = -iYA 
       iDx = -(iB*iXA+iC*iYA) 
       iDy = iA*iXA+iB*iYA 
 
       ' arc FROM (-xA,-yA) TO point (x0,y0) where dx/dy = 0 
       WHILE ( iDx <= 0 ) 
 
           CALL pixel(iXC+iX*xflag,iYC+iY,color) 
           CALL pixel(iXC-iX*xflag,iYC-iY,color) 
           INCR iY 
           iSigma = iA*iX*iX+2*iB*iX*iY+iC*iY*iY-iD 
           IF ( iSigma < 0 ) THEN 
 
               iDx=iDx - iB 
               iDy=iDy + iA 
               DECR iX 
           END IF 
           iDx=iDx + iC 
           iDy=iDy - iB 
       WEND 
 
       ' arc FROM (x0,y0) TO point (x1,y1) where dy/dx = 1 
       WHILE ( iDx <= iDy ) 
 
           CALL pixel(iXC+iX*xflag,iYC+iY,color) 
           CALL pixel(iXC-iX*xflag,iYC-iY,color) 
           INCR iY 
           iXp1 = iX+1 
           iSigma = iA*iXp1*iXp1+2*iB*iXp1*iY+iC*iY*iY-iD 
           IF ( iSigma >= 0 ) THEN 
 
               iDx=iDx + iB 
               iDy=iDy - iA 
               iX = iXp1 
           END IF 
           iDx=iDx + iC 
           iDy=iDy - iB 
       WEND 
 
       ' arc FROM (x1,y1) TO point (x2,y2) where dy/dx = 0 
       WHILE ( iDy >= 0 ) 
 
           CALL pixel(iXC+iX*xflag,iYC+iY,color) 
           CALL pixel(iXC-iX*xflag,iYC-iY,color) 
           INCR iX 
           iSigma = iA*iX*iX+2*iB*iX*iY+iC*iY*iY-iD 
           IF ( iSigma < 0 ) THEN 
 
               iDx=iDx + iC 
               iDy=iDy - iB 
               INCR iY 
           END IF 
           iDx=iDx + iB 
           iDy=iDy - iA 
       WEND 
 
       ' arc FROM (x2,y2) TO point (x3,y3) where dy/dx = -1 
       WHILE ( iDy >= -iDx ) 
 
           CALL pixel(iXC+iX*xflag,iYC+iY,color) 
           CALL pixel(iXC-iX*xflag,iYC-iY,color) 
           INCR iX 
           iYm1 = iY-1 
           iSigma = iA*iX*iX+2*iB*iX*iYm1+iC*iYm1*iYm1-iD 
           IF ( iSigma >= 0 ) THEN 
 
               iDx=iDx - iC 
               iDy=iDy + iB 
               iY = iYm1 
           END IF 
           iDx=iDx + iB 
           iDy=iDy - iA 
       WEND 
 
       ' arc FROM (x3,y3) TO (xa,ya) 
       WHILE ( iY >= iYA ) 
 
           CALL pixel(iXC+iX*xflag,iYC+iY,color) 
           CALL pixel(iXC-iX*xflag,iYC-iY,color) 
           DECR iY 
           iSigma = iA*iX*iX+2*iB*iX*iY+iC*iY*iY-iD 
           IF ( iSigma < 0 ) THEN 
 
               iDx=iDx + iB 
               iDy=iDy - iA 
               INCR iX 
           END IF 
           iDx=iDx - iC 
           iDy=iDy + iB 
       WEND 
 
   ELSE 
 
       ' start AT (-xa,-ya) 
       iX = -iXA 
       iY = -iYA 
       iDx = -(iB*iXA+iC*iYA) 
       iDy = iA*iXA+iB*iYA 
 
       ' arc FROM (-xa,-ya) TO point (x0,y0) where dy/dx = -1 
       WHILE ( -iDx >= iDy ) 
 
           CALL pixel(iXC+iX*xflag,iYC+iY,color) 
           CALL pixel(iXC-iX*xflag,iYC-iY,color) 
           DECR iX 
           iYp1 = iY+1 
           iSigma = iA*iX*iX+2*iB*iX*iYp1+iC*iYp1*iYp1-iD 
           IF ( iSigma >= 0 ) THEN 
 
               iDx=iDx + iC 
               iDy=iDy - iB 
               iY = iYp1 
           END IF 
           iDx=IDx - iB 
           iDy=Idy +iA 
       WEND 
 
       ' arc FROM (x0,y0) TO point (x1,y1) where dx/dy = 0 
       WHILE ( iDx <= 0 ) 
 
           CALL pixel(iXC+iX*xflag,iYC+iY,color) 
           CALL pixel(iXC-iX*xflag,iYC-iY,color) 
           INCR iY 
           iSigma = iA*iX*iX+2*iB*iX*iY+iC*iY*iY-iD 
           IF ( iSigma < 0 ) THEN 
 
               iDx=iDx - iB 
               iDy=iDy + iA 
               DECR iX 
           END IF 
           iDx=iDx + iC 
           iDy=iDy - iB 
       WEND 
 
       ' arc FROM (x1,y1) TO point (x2,y2) where dy/dx = 1 
       WHILE ( iDx <= iDy ) 
 
           CALL pixel(iXC+iX*xflag,iYC+iY,color) 
           CALL pixel(iXC-iX*xflag,iYC-iY,color) 
           INCR iY 
           iXp1 = iX+1 
           iSigma = iA*iXp1*iXp1+2*iB*iXp1*iY+iC*iY*iY-iD 
           IF ( iSigma >= 0 ) THEN 
 
               iDx=IDx + iB 
               iDy=Idy - iA 
               iX = iXp1 
           END IF 
           iDx=iDx + iC 
           iDy=iDy - iB 
       WEND 
 
       ' arc FROM (x2,y2) TO point (x3,y3) where dy/dx = 0 
       WHILE ( iDy >= 0 ) 
 
           CALL pixel(iXC+iX*xflag,iYC+iY,color) 
           CALL pixel(iXC-iX*xflag,iYC-iY,color) 
           INCR iX 
           iSigma = iA*iX*iX+2*iB*iX*iY+iC*iY*iY-iD 
           IF ( iSigma < 0 ) THEN 
 
               iDx=iDx + iC 
               iDy=iDy - iB 
               INCR iY 
           END IF 
           iDx=iDx + iB 
           iDy=iDy - iA 
       WEND 
 
       ' arc FROM (x3,y3) TO (xa,ya) 
       WHILE ( iX <= iXA ) 
 
           CALL pixel(iXC+iX*xflag,iYC+iY,color) 
           CALL pixel(iXC-iX*xflag,iYC-iY,color) 
           INCR iX 
           iYm1 = iY-1 
           iSigma = iA*iX*iX+2*iB*iX*iYm1+iC*iYm1*iYm1-iD 
           IF ( iSigma >= 0 ) THEN 
 
               iDx=iDx - iC 
               iDy=iDy + iB 
               iY = iYm1 
           END IF 
           iDx=iDx + iB 
           iDy=iDy - iA 
       WEND 
   END IF 
 
END SUB    
 
Back to Home page