Drawing rotated ellipses, Please help.
BlitzMax Forums/BlitzMax Beginners Area/Drawing rotated ellipses, Please help.
| ||
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. ![]() |
| ||
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 |
| ||
No, the need to be n-gons and unfilled. |
| ||
How about something like this |
| ||
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. |
| ||
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 |
| ||
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. |
| ||
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. |
| ||
Look up a fully integer-based bresenham-like rotatable ellipse routine, it will be much quicker than doing all that trigonometry. |
| ||
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. |
| ||
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 |
| ||
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 |
| ||
Wow thats a lot of code for an ellipse ;-) Good work tho. |
| ||
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 |