Drawing rotated ellipses, Please help.

BlitzMax Forums/BlitzMax Beginners Area/Drawing rotated ellipses, Please help.

Ryan Burnside(Posted 2007) [#1]
Once again I've come across a problem that I need assistance with. The goal was to draw a rotated ellipse if a given width and height. My initial code could produce the ellipse with no rotation. The code simply used a for loop and n many points. I found that this method worked but the ellipses formed were not always symmetrical. This probably arises from the fact that a single center point was used regardless if the shape was odd or even in width and height. This is for a lower resolution game so odd shaped ellipses aren't passable and anti aliasing would ruin the feeling of the art. I really need symmetry in these n-gon circles.

The second problem is how to go about rotating the shapes at a given angle. I tinkered around a bit with my code but could never actually implement rotation.


The two problems at a glance:
1. Symmetry is important here with no anti aliasing
2. Rotation of the ellipse is unclear to me.

The code:
Function draw_oval(centerx#,centery#,width#,height#,rot#,n#)
	' draw the shape with a for loop
	Local angle#=360.0/n
	Local r1#=width/2.0
	Local r2#=height/2.0
	For Local i=0 To n-1
		Local x1=centerx+Cos(i*angle)*r1
		Local x2=centerx+Cos((i+1)*angle)*r1
		Local y1=centery+Sin(i*angle)*r2
		Local y2=centery+Sin((i+1)*angle)*r2
		DrawLine(x1,y1,x2,y2)
	Next
End Function


A picture showing both unsymmetrical ellipses and the intended result with rotation parameter. If you look at the quadrants of the ellipse you can see that 1 and 2 are more round that qudrants 3 and 4.




Azathoth(Posted 2007) [#2]
Something like:
Graphics 640,480
i=0
While Not KeyHit(KEY_ESCAPE)
	Cls
	SetColor 255,255,255
	SetHandle 150/2,50/2
	SetRotation i
	DrawOval 100,100,150,50
	i:+1
	If i=360 Then i=0
	Flip
Wend



Ryan Burnside(Posted 2007) [#3]
No, the need to be n-gons and unfilled.


assari(Posted 2007) [#4]
How about something like this




Ryan Burnside(Posted 2007) [#5]
Thanks for all the help so far.

After a brief head explosion and some mopping of the floor I came up with this.
Function draw_oval(centerx#,centery#,width#,height#,rot#,n#)
	' draw the shape with a for loop
	
	
	Local angle#=360.0/n
	Local r1#=width/2.0
	Local r2#=height/2.0

	For Local i#=0 To n-1
	
		Local x1#=centerx+Cos(i*angle)*r1
		Local y1#=centery+Sin(i*angle)*r2
		Local x2#=centerx+Cos((i+1)*angle)*r1
		Local y2#=centery+Sin((i+1)*angle)*r2
		' get new data
		Local d1#=point_distance(centerx,centery,x1,y1)
		Local a1#=point_direction(centerx,centery,x1,y1)
		Local fx#=centerx+Cos(a1+rot)*d1
		Local fy#=centery+Sin(a1+rot)*d1
		
		Local d2#=point_distance(centerx,centery,x2,y2)
		Local a2#=point_direction(centerx,centery,x2,y2)
		Local fx2#=centerx+Cos(a2+rot)*d2
		Local fy2#=centery+Sin(a2+rot)*d2
	
		DrawLine(fx,fy,fx2,fy2)
		
	Next
End Function


The problem if drawing even width ovals remains. I don't know how to draw an oval with an even width. Using a central pixel always will make the width odd. If I tell it to draw a 32x32 oval I get a 31x31 oval.


DJWoodgate(Posted 2007) [#6]
Maybe a bit faster. Have not really tested it much though, so not sure about the even width problem you mention.

Function draw_oval(centerx#,centery#,width#,height#,rot#,n#)
	' draw the shape with a for loop

	Local angle# = 360.0/n
	Local r1# = width/2.0
	Local r2# = height/2.0
	
	Local crot# = Cos(rot)
	Local srot# = Sin(rot)

	Local lx# = centerx + r1 * crot
	Local ly# = centery + r1 * srot

	For Local i#=1 To n
	
		Local rx# = Cos(i*angle)*r1
		Local ry# = Sin(i*angle)*r2
		
		Local tx# = centerx+(rx * crot - ry * srot)
		Local ty# = centery+(rx * srot + ry * crot)
	
		DrawLine(lx,ly,tx,ty,False)
		
		lx=tx
		ly=ty
		
	Next
	
End Function



Ryan Burnside(Posted 2007) [#7]
Ok thanks for the tip. I was double calculating I suppose...

I do NOT want to use rotated pixmaps for this because this bit of code is for a drawing program for artists. I need precision that tends to make symmetrical circles rather than mathmatically represented ones on pixels. Why do some circles produced lack symmetry? It's as if the plotting method truncates fractional pixel values rather than rounding them.


DJWoodgate(Posted 2007) [#8]
I am not sure exactly how it works in DirectX. However my driver offers an option to change the pixel centre so as far as I can see you can't be certain exactly which pixel will be filled for fractional values without running some sort of profiling routine...

Maybe there is some way to get DX to cough up that information?

Failing that perhaps the best option is to do your own rounding and truncation.


ImaginaryHuman(Posted 2007) [#9]
Look up a fully integer-based bresenham-like rotatable ellipse routine, it will be much quicker than doing all that trigonometry.


Dreamora(Posted 2007) [#10]
and much slower again due to the 2D through 3D implementation and the need to draw to pixmap and draw that one to the backbuffer.


ImaginaryHuman(Posted 2007) [#11]
You want to draw the ellipse rotating directly to the display in realtime, I take it?

You could assemble a vertex array and pass it to OpenGL


Jimmy(Posted 2011) [#12]
No one seem to actually post a Bresenham like code for rotated ellipses solution, only reffering to them, so I post it here.

It produces excellent and smooth ellipses, which is rather scarce thing to see these days.

Graphics  800,600,16,2:SetBuffer BackBuffer()
Repeat:Cls:xx=MouseX():yy=MouseY()
Color 0,255,0:ellipse(512,384,1+yy/2.0,200,xx/200.0)
Color 255,255,255:Plot 512,384:Plot 512+100,384:Plot 512-100,384:Plot 512,384+200:Plot 512,384-200
Flip
Until MouseDown(2)

Function ellipse (iXC#,iYC#,A#,B#,angle#)
;
; General ellipse
;
; Known bugs:
; jumpy movement sometimes, as it uses huge numbers, too large To fit storage
; It has few lines with trigonometry, these could very possibly be replaced with integer table or 
; tables of degrees Or similiar. It wraps the angle into an acceptable range, and sets xflag accordingly.
;
R2D# = 180.0/Pi:D2R# = Pi/180.0
If angle# < Pi/2.0
xflag#=1
ElseIf angle#>Pi/2 And angle#<Pi
temp#=angle#-Pi/2.0:angle#=Pi/2.0-temp#:xflag#=-1
ElseIf angle#>=Pi And angle#<Pi*1.5
angle#=angle#-Pi:xflag#=1
ElseIf angle#>=Pi*1.5 And angle#<Pi*2.0
angle#=angle#-Pi:temp#=angle#-Pi/2.0:angle#=Pi/2.0-temp#:xflag#=-1
EndIf

; ...To output these 4 integer points. (and the said xflag), These are where major and minor axis of ellipse ends (determines shape as in aligned axis algorithms).
ixa#=Int(Cos(angle#*R2D#)*A#)
iya#=Int(Sin(angle#*R2D#)*A#)
ixb#=Int(Cos((angle#+(Pi/2.0))*R2D#)*B#)
iyb#=Int(Sin((angle#+(Pi/2.0))*R2D#)*B#)

; rest of code uses only variables:
; ixa,iya,ixb,iyb
; ixc, iyc, xflag
; these are as every one else from now on, integers

Plot ixc#-ixa#,iyc#-iya#
Plot ixc#-ixb#,iyc#-iyb#
Plot ixc#-ixb#,iyc#-iya#
Plot ixc#-ixa#,iyc#-iyb#

; From now on, integers only, it uses multiplication much, and needs very large integers (in large resolutions even more than 64bits)
; therefor single precision are used to alloud the space, but theres no fixed point Or floating point involved.

   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#

   If iYA# <= iXA#
       ; 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
           Plot iXC#+iX#*xflag#,iYC#+iY#
           Plot iXC#-iX#*xflag#,iYC#-iY#
           iY#=iY#+1
           iSigma# = iA#*iX#*iX#+2*iB#*iX#*iY#+iC#*iY#*iY#-iD# 
           If iSigma# < 0 Then iDx#=iDx#-iB#:iDy#=iDy#+iA#:iX#=iX#-1
           iDx#=iDx# + iC# 
           iDy#=iDy# - iB# 
       Wend 

       ; Arc FROM (x0,y0) TO point (x1,y1) where dy/dx = 1 
       While iDx# <= iDy# 
           Plot iXC#+iX#*xflag#,iYC#+iY# 
           Plot iXC#-iX#*xflag#,iYC#-iY# 
           iY#=iY#+1
           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# 
           iDx#=iDx# + iC# 
           iDy#=iDy# - iB# 
       Wend 

       ; Arc FROM (x1,y1) TO point (x2,y2) where dy/dx = 0 
       While iDy# >= 0
           Plot iXC#+iX#*xflag#,iYC#+iY#
           Plot iXC#-iX#*xflag#,iYC#-iY#
           iX=iX+1 
           iSigma# = iA#*iX#*iX#+2*iB#*iX#*iY#+iC#*iY#*iY#-iD# 
           If iSigma# < 0 Then iDx#=iDx# + iC# : iDy#=iDy# - iB# : iY#=iY#+1 
           iDx#=iDx# + iB# 
           iDy#=iDy# - iA# 
       Wend 

       ; Arc FROM (x2,y2) TO point (x3,y3) where dy/dx = -1 
       While iDy# >= -iDx# 
           Plot iXC#+iX#*xflag#,iYC#+iY#
           Plot iXC#-iX#*xflag#,iYC#-iY#
           iX#=iX#+1 
           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# 
           iDx#=iDx# + iB# 
           iDy#=iDy# - iA# 
       Wend 

       ; Arc FROM (x3,y3) TO (xa,ya) 
       While iY# >= iYA# 
           Plot iXC#+iX#*xflag#,iYC#+iY#
           Plot iXC#-iX#*xflag#,iYC#-iY#
           iY#=iY#-1
           iSigma# = iA#*iX#*iX#+2*iB#*iX#*iY#+iC#*iY#*iY#-iD# 
           If iSigma# < 0 Then iDx#=iDx# + iB#:iDy#=iDy# - iA#:iX#=iX#+1 
           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# 
           Plot iXC#+iX#*xflag#,iYC#+iY#
           Plot iXC#-iX#*xflag#,iYC#-iY# 
           iX#=iX#-1
           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# 
           iDx#=IDx# - iB# 
           iDy#=Idy# +iA# 
       Wend 

       ; Arc FROM (x0,y0) TO point (x1,y1) where dx/dy = 0 
       While iDx# <= 0
           Plot iXC#+iX#*xflag#,iYC#+iY#
           Plot iXC#-iX#*xflag#,iYC#-iY#
           iY#=iY#+1
           iSigma# = iA#*iX#*iX#+2*iB#*iX#*iY#+iC#*iY#*iY#-iD# 
           If iSigma# < 0 Then iDx#=iDx# - iB#:iDy#=iDy# + iA#:iX#=iX#-1
           iDx#=iDx# + iC# 
           iDy#=iDy# - iB# 
       Wend 

       ; Arc FROM (x1,y1) TO point (x2,y2) where dy/dx = 1 
       While iDx# <= iDy# 
           Plot iXC#+iX#*xflag#,iYC#+iY#
           Plot iXC#-iX#*xflag#,iYC#-iY#
           iY#=iY#+1
           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# 
           iDx#=iDx# + iC# 
           iDy#=iDy# - iB# 
       Wend 

       ; Arc FROM (x2,y2) TO point (x3,y3) where dy/dx = 0 
       While iDy# >= 0
           Plot iXC#+iX#*xflag#,iYC#+iY#
           Plot iXC#-iX#*xflag#,iYC#-iY# 
           iX#=iX#+1 
           iSigma# = iA#*iX#*iX#+2*iB#*iX#*iY#+iC#*iY#*iY#-iD# 
           If iSigma# < 0 Then iDx#=iDx# + iC# : iDy#=iDy# - iB# : iY#=iY#+1 
           iDx#=iDx# + iB# 
           iDy#=iDy# - iA# 
       Wend 

       ; Arc FROM (x3,y3) TO (xa,ya) 
       While iX# <= iXA#
           Plot iXC#+iX#*xflag#,iYC#+iY# 
           Plot iXC#-iX#*xflag#,iYC#-iY# 
           iX#=iX#+1 
           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# 
           iDx#=iDx# + iB# 
           iDy#=iDy# - iA# 
       Wend 

   EndIf
End Function



ImaginaryHuman(Posted 2011) [#13]
Wow thats a lot of code for an ellipse ;-)

Good work tho.


Midimaster(Posted 2011) [#14]
if you like it shorter, take this:

SuperStrict

Graphics 800,600
SetColor 255,255,255
SetColor 255,255,255
For Local i%=0 To 360 Step 1
	Cls
	DrawRotatedOval 300,300,200,100,i
	Flip 0
Next
WaitKey


Function DrawRotatedOval(centerX#, centerY# , Width# , Height# , Rotation#)
	Local i# , distance# , angle# , actSin# , actCos# , preSin#, preCos# 
	For i#=0 To 360 Step .1
		distance= Sqr( (Sin(i)*Width)^2  + (Cos(i)*Height)^2 )
		angle=ATan2( Sin(i)*Width , Cos(i)*Height)
		actSin = Sin(angle + Rotation)*distance+centerX
		actCos = Cos(angle + Rotation)*distance+centerY
		If i>0
			DrawLine actSin , actCos , preSin , preCos
		EndIf
		preSin = actSin
		preCos = actCos 
	Next
End Function



...and speed it up with an "step 1" or "step 10"

or more dynamic with variable steprate and 3 additional features
"filled" , "Startangle" and "EndAngle":

Function DrawRotatedOval(centerX#, centerY# , Width# , Height# , Rotation# , StartAngle#=0, EndAngle#=360 , Filled%=0)
	Local i# , distance# , angle# , actSin# , actCos# , preSin#, preCos# , StepRate#
	stepRate=(250-200*filled)/Sqr(width^2 + height^2)
	i=StartAngle

	While i<EndAngle+stepRate
		
		distance= Sqr( (Sin(i)*Width)^2  + (Cos(i)*Height)^2 )
		angle=ATan2( Sin(i)*Width , Cos(i)*Height)
		actSin = Sin(angle + Rotation)*distance + centerX
		actCos = Cos(angle + Rotation)*distance + centerY
		If i>0
			DrawLine actSin , actCos , preSin  , preCos
			If Filled<>0 Then
				DrawLine centerX , centerY , actSin  , actCos
			EndIf
		EndIf
		i=i+stepRate
		preSin = actSin
		preCos = actCos 
	Wend

End Function



Last edited 2011