Page 1 of 3

Drawing Arcs

Posted: Tue Aug 28, 2007 8:43 pm
by Xombie
I tried this question earlier in the GDI+ post under Tips & Tricks to no avail. So now I'm making it a little more generic.

How can I draw an elliptical arc at any angle of my choosing? Anti-aliased would be nice but I can live without if I have to. Also, when I say "at any angle" I mean to rotate the theoretical ellipse before tracing it's arc. I do not mean to just start the arc angle at some degree followed by a different end angle.

Hope that makes sense and that someone out there can help.

Posted: Tue Aug 28, 2007 9:26 pm
by Kaeru Gaman
you can first calculate the coordinates of the unrotated ellipse.

these could also be understood as a radius of a circle.

along this circle you can now rotate the point.

for each point on the (¼) ellipse you will get a different radius.

. Image

Posted: Tue Aug 28, 2007 10:53 pm
by Ollivier
(And to accelerate the execution of your algo)

(To explain what you wanna create, I put a convention : ^a = vector a)

In a classical plane (orthonormal or 'carthesian plane') named (O;^i;^j)

Code: Select all

>>O is the origin
>>^i is a horizontal vector (1;0) (x axial)
>>^j is a vertical vector (0;1) (y axial)
But these values are the result of trigo calcul like that:

Code: Select all

>> ^i [Cos(0); Sin(0) ]
>> ^j [Cos((Pi/2)+0); Sin((Pi/2)+0) ]     }} (Results are above)
Zéro is the angle of your draw (ellipse) in the plane (Z rotation)
So you replace 0 by a variable 'Zr'

Code: Select all

>> ^i [Cos(Zr); Sin(Zr) ]
>> ^j [Cos(Pi/2+Zr); Sin(Pi/2+Zr) ]

Now, when you have a point p(x, y), you know that:

Code: Select all

^p = x^i + y^j    (syntax: x^i = x*^i)
^p = x^[Cos(Zr); Sin(Zr) ] + y^[Cos((Pi/2)+Zr); Sin((Pi/2)+Zr) ]
^p = x^[A; B] + y^[C; D]
= ^p[xA + yC; xB + yD]   (Sum of 2 vectors)
= ^p[ xCos(Zr) + yCos((Pi/2)+Zr)  ;   xSin(Zr) + ySin((Pi/2)+Zr) ]
Examples:
>> Zr = 0 (no rotation)

Code: Select all

^p1[ xCos(0) + yCos((Pi/2)+0)  ;   xSin(0) + ySin((Pi/2)+0) ]
^p1[ x*1 + y*0)  ;   x*0 + y*1]
^p1[ x ; y ]
>> Zr = Pi/2 (90° left rotation)

Code: Select all

^p2[ xCos(Pi/2) + yCos((Pi/2)+Pi/2)  ;   xSin(Pi/2) + ySin((Pi/2)+Pi/2) ]
^p2[ xCos(Pi/2) + yCos(Pi)  ;   xSin(Pi/2) + ySin(Pi) ]
^p2[ x*0 + y*-1  ;   x*1 + y*0 ]
^p2[ -y ; x ]
To begin a 3D engine, I wrote this code:

Code: Select all

 InitSprite() 
  InitKeyboard() 
  OpenScreen(1024, 768, 32, "x") 
  ;*********** 
  R.F = 100.0 
  Xtheta.F = 0.0 
  Ytheta.F = 0.0 
  Ztheta.F = 0.0      
  Ttheta.F = 0.0      
  ;*********** 
  Repeat 
    StartDrawing(ScreenOutput() ) 
      Box(0, 0, 1024, 768, #Black) 
      Vxx.F = Cos(Ztheta) 
      Vxy.F = -Sin(Ztheta) 
      Vyx.F = Cos(Ztheta + #PI / 2.0) 
      Vyy.F = -Sin(Ztheta + #PI / 2.0) 
      Line(512, 384, Vxx * 20.0, Vxy * 20.0, #Blue) 
      Line(512, 384, Vyx * 20.0, Vyy * 20.0, #Blue) 
      For i = 0 To 359 
        ; MaJ référentiel Vecteur x et Vecteur y 
        X.F = #PI * i / 180.0 
        
        Tx.F = Cos(X) * Cos(Ytheta) * 100.0 
        Ty.F = Sin(X) * 100.0 
      
        Ex.F = Vxx * Tx + Vxy * Ty 
        Ey.F = Vxy * Tx + Vyy * Ty 
        ;Trace vecteur E 
        Plot(512 + Ex, 384 + Ey, #Blue) 
        If Abs(X - Xtheta) < 0.01 
          Line(512, 384, Ex, Ey, #White) 
          Box(511 + Ex, 383 + Ey, 3, 3, #White) 
        EndIf 
        
      Next    
    Xtmp = Xtheta * 180 / #PI 
    Xtmp % 180 
    DrawText(0, 0, Str(Xtmp), #White, #Black) 
    Ytmp = Ytheta * 180 / #PI 
    Ytmp % 180 
    DrawText(0, 16, Str(Ytmp), #White, #Black) 
    ZTmp = (Ztheta * 180 / #PI) 
    Ztmp % 180 
    DrawText(0, 32, Str(Ztmp), #White, #Black) 
    DrawText(0, 48, "ARROW KEYS PageUp PageDown to change x, y, et z values") 
    StopDrawing() 
    FlipBuffers() 
    ExamineKeyboard() 
    If KeyboardPushed(#PB_Key_PageUp) 
      Ztheta + 0.04      
    EndIf 
    If KeyboardPushed(#PB_Key_PageDown) 
      Ztheta - 0.04 
    EndIf 
    If KeyboardPushed(#PB_Key_Up) 
      Xtheta + 0.04 
      If Xtheta > 2.0 * #PI 
        Xtheta - 2.0 * #PI 
      EndIf 
    EndIf 
    If KeyboardPushed(#PB_Key_Down) 
      Xtheta - 0.04 
      If Xtheta < 0.0 
        Xtheta + 2.0 * #PI 
      EndIf 
    EndIf 
    If KeyboardPushed(#PB_Key_Left) 
      Ytheta + 0.04 
    EndIf 
    If KeyboardPushed(#PB_Key_Right) 
      Ytheta - 0.04 
    EndIf 
  Until KeyboardPushed(#PB_Key_Escape)

Posted: Wed Aug 29, 2007 6:33 pm
by Xombie
Thanks, Kaeru Gaman and Ollivier.

Your code is really nifty, Ollivier (I especially like the 3D view) but way beyond my means. I only need a simple 2d elliptical arc that can be rotated about it's ... Y? axis.

And I'm afraid I don't quite follow your example, Kaeru Gaman. I'm afraid I'm just not a mathematician =/

Would I be able to beg an algorithm or a link to an algorithm in any language that would allow me to draw an elliptical arc rotated an arbitrary amount?

Sorry for being greedy but I tried digging around with Google and came up short. Lines and rectangles are more my speed :(

Posted: Wed Aug 29, 2007 7:13 pm
by Ollivier
I'm just not a mathematician =/
Excuse me!

Kaeru show you (really good draw :D ) how to turn your draw.

Do you know drawing a circle with 'Plot' in a loop 'For...Next'?

Posted: Thu Aug 30, 2007 3:32 pm
by Xombie
Well, I know drawing and understand the basic programming side of it. My problem comes in when having to work out the formulas for the thing I'm drawing.

I'm pretty sure I could work out a circle, though.

Posted: Thu Aug 30, 2007 5:30 pm
by Kaeru Gaman
my point was, when you can draw a horizontal ellipse, you can work the rotation out.

- you calculate an x/y for a point on the horizontal ellipse.

- from this x/y, you take the radius and angle, to interprete it as a point on a circle.

- you turn the angle of this radius along your desired rotation angle.

- you plot the point.

- repeat all these steps for each desired point of the ellipse.
you will get different radiuses each calculation.
the result will be a rotated ellipse.

this is just the theory behind it.
sorry, I'm too lazy to work it out as code right now.
and I think, understanding this basic concept will help you more than a complete routine. ;)

Posted: Thu Aug 30, 2007 5:57 pm
by Helle
Is this too simple?

Code: Select all

 Width  = 500
  Height = 300
  If OpenWindow(0, 0, 0, Width, Height+1, "Rotations-Ellipse", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
    If StartDrawing(WindowOutput(0))
      i = 1
      x = Width/2
      y = Height/2
      
       For w = 0 To 360
         If w <= 90 Or w>=270
           col = 1234
          Else
           col = 123456
         EndIf 

          Ellipse(x, y, x*Cos(w*#PI/180), y, col+i)
          FillArea(0, 0, col+i, RGB(212, 208, 200))
         
          i = ~i
          Delay(10)
      
       Next
      StopDrawing()
    EndIf
    
    Repeat : Event = WaitWindowEvent() : Until  Event = #PB_Event_CloseWindow
  EndIf
Gruss
Helle

Posted: Sat Sep 01, 2007 10:26 am
by Ollivier

Code: Select all

xCenter FLOAT (pixel)
yCenter FLOAT (pixel)
xRadius FLOAT (pixel)
yRadius FLOAT (pixel)
zRotation FLOAT (rad)
StartAngle FLOAT (rad)
FinishAngle FLOAT (rad)

Code: Select all

Procedure EllipseWithRotation(xCenter.F, yCenter.F, xRadius.F, yRadius.F, zRotation.F, StartAngle.F, FinishAngle.F, Color.L)
    Protected Angle.F
    Protected AngleStep.F    
    Protected X.F
    Protected Y.F
    Protected DisplayX.F
    Protected DisplayY.F
    Protected GreatestRadius.F
    If xRadius > yRadius
        GreatestRadius.F = xRadius
    Else
        GreatestRadius = yRadius
    EndIf
    AngleStep.F = #PI / GreatestRadius
    NewList DrawX.POINT()
    Angle = StartAngle
    Repeat        
        X.F = Cos(Angle) * xRadius                           ; Draw an ellipse...
        Y.F = -Sin(Angle) * yRadius
        DisplayX.F = X * Cos(zRotation) - Y * Sin(zRotation) ; Rotation...
        DisplayY.F = X * Sin(zRotation) + Y * Cos(zRotation)        
        AddElement(DrawX() )                                 ; Record...
        DrawX()\X = xCenter + DisplayX
        DrawX()\Y = yCenter + DisplayY        
        Angle + AngleStep        
    Until Angle > FinishAngle
    ResetList(DrawX() )                                      ; Draw...
    For i = 1 To CountList(DrawX() ) - 1        
        SelectElement(DrawX(), i - 1)
        x1 = DrawX()\X
        y1 = DrawX()\Y
        SelectElement(DrawX(), i)
        x2 = DrawX()\X
        y2 = DrawX()\Y
        LineXY(x1, y1, x2, y2, Color)        
    Next
    ClearList(DrawX() )
EndProcedure
Nice day!

Posted: Sat Sep 01, 2007 11:29 am
by dell_jockey
Look Ma! No API.... ;)

Well done, thanks a lot Ollivier! Your usage of lists forces me to re-think parts of an application that I'm currently developing. Thanks again.

Posted: Sat Sep 01, 2007 11:44 am
by Ollivier
Indeed, lists offer many possibilities :D

Posted: Sat Sep 01, 2007 12:00 pm
by Ollivier
2 Xombie (or somebody who knows the answer)

What is 'anti-aliased' ???

Posted: Sat Sep 01, 2007 12:36 pm
by dell_jockey
Ollivier,

check out: http://en.wikipedia.org/wiki/Xiaolin_Wu ... _algorithm

Xiaolin Wu's algorithm is one of many anti-aliassing techniques.

bye
Hans

Posted: Sat Sep 01, 2007 12:57 pm
by Ollivier
So, reading the wikipedia's article, I think I answer Xombie enough correctly... With a list! He'll have just to manage the 2 lines 'NewList' and 'ClearList' to recuperate coordonates x and y arcs...

Thanks

Posted: Sat Sep 01, 2007 8:11 pm
by Xombie
Thanks, everyone! I'm excited to try this out tonight and see what kind of damage I can do.

So, yes, thanks again for being patient with me even though I'm quite slow at stuff like this :p

Thanks again, Ollivier, Helle and Kaeru Gaman!