DrawArc and ellipse geometry (repost)

Discussion in 'Microsoft C# .NET' started by Christopher Ireland, Jun 20, 2007.

  1. Hello!

    Using the definition for en ellipse (http://en.wikipedia.org/wiki/Ellipse) I
    can draw an arc of points. However, the end points of this arc do not
    coincide with the end points of an arc drawn with the DrawArc method, e.g.

    public partial class Form2 : Form
    {
    public Form2()
    {
    InitializeComponent();
    }

    private void Form2_Paint(object sender, PaintEventArgs e)
    {
    Draw(e.ClipRectangle, e.Graphics);
    }

    private void RectCenter(Rectangle r, out int x, out int y)
    {
    x = (r.Left + r.Right) / 2;
    y = (r.Top + r.Bottom) / 2;
    }

    private PointF PointFromEllipse(Rectangle bounds, float degrees)
    {
    float a = bounds.Width / 2.0f;
    float b = bounds.Height / 2.0f;
    float rad = ((float)Math.PI / 180.0f) * degrees;

    int xCenter, yCenter;
    RectCenter(bounds, out xCenter, out yCenter);

    float x = xCenter + (a * (float)Math.Cos(rad));
    float y = yCenter + (b * (float)Math.Sin(rad));

    return new PointF(x, y);
    }

    private void Draw(Rectangle rect, Graphics g)
    {

    rect.Inflate(-5, -5);
    g.DrawArc(new Pen(Color.Green), rect, 0, 315);
    PointF p;

    for (int i = 0; i < 315; i++)
    {
    p = PointFromEllipse(rect, i);
    g.DrawLine(new Pen(Color.Red), p, new PointF(p.X, p.Y + 2));
    }
    }
    }

    Can anybody please give me an explanation for this difference?

    --
    Thank you,

    Christopher Ireland
     
    Christopher Ireland, Jun 20, 2007
    #1
    1. Advertisements

  2. Christopher Ireland

    Peter Duniho Guest

    On Wed, 20 Jun 2007 12:58:10 -0700, Christopher Ireland
    <> wrote:

    > Using the definition for en ellipse
    > (http://en.wikipedia.org/wiki/Ellipse) I
    > can draw an arc of points. However, the end points of this arc do not
    > coincide with the end points of an arc drawn with the DrawArc method,


    I'm not really clear on what your question is. You aren't drawing lines
    that would form an arc. You are drawing vertical bars 2 pixels high at
    various points around the ellipse, and that's exactly the output you get..

    Here's a version of your code (minus the constant Form-derived class
    implementation stuff) that corrects that along with some other problems
    (you were failing to dispose of newly created pens correctly, for
    example). It draws practically the same exact pixels as the DrawArc()
    method, with only very minor variations that one would naturally expect
    from two completely different implementations of drawing an ellipse
    (especially when one implementation is actually just drawing straight line
    segments between one degree intervals on the ellipse).


    protected override void OnPaint(PaintEventArgs e)
    {
    base.OnPaint(e);
    Draw(ClientRectangle, e.Graphics);
    }

    protected override void OnResize(EventArgs e)
    {
    base.OnResize(e);
    Invalidate();
    }

    private void RectCenter(Rectangle r, out int x, out int y)
    {
    x = (r.Left + r.Right) / 2;
    y = (r.Top + r.Bottom) / 2;
    }

    private PointF PointFromEllipse(Rectangle bounds, float degrees)
    {
    float a = bounds.Width / 2.0f;
    float b = bounds.Height / 2.0f;
    float rad = ((float)Math.PI / 180.0f) * degrees;

    int xCenter, yCenter;
    RectCenter(bounds, out xCenter, out yCenter);

    float x = xCenter + (a * (float)Math.Cos(rad));
    float y = yCenter + (b * (float)Math.Sin(rad));

    return new PointF(x, y);
    }

    private void Draw(Rectangle rect, Graphics g)
    {
    rect.Inflate(-5, -5);

    g.DrawArc(Pens.Green, rect, 0, 315);
    PointF p, pPrev = PointFromEllipse(rect, 0);

    for (int i = 1; i < 315; i++)
    {
    p = PointFromEllipse(rect, i);
    g.DrawLine(Pens.Red, p, pPrev);
    pPrev = p;
    }
    }
     
    Peter Duniho, Jun 20, 2007
    #2
    1. Advertisements

  3. Peter,

    > It draws practically the same exact pixels
    > as the DrawArc() method, with only very minor variations that one
    > would naturally expect from two completely different implementations
    > of drawing an ellipse (especially when one implementation is actually
    > just drawing straight line segments between one degree intervals on
    > the ellipse).


    Thank you for your reply, Peter, and I'm sorry for not making myself
    clearer. I've retouched your code to make my point:

    protected override void OnPaint(PaintEventArgs e)
    {
    base.OnPaint(e);
    Draw(ClientRectangle, e.Graphics);
    }

    protected override void OnResize(EventArgs e)
    {
    base.OnResize(e);
    Invalidate();
    }

    private void RectCenter(Rectangle r, out int x, out int y)
    {
    x = (r.Left + r.Right) / 2;
    y = (r.Top + r.Bottom) / 2;
    }

    private PointF PointFromEllipse(Rectangle bounds, float degrees)
    {
    float a = bounds.Width / 2.0f;
    float b = bounds.Height / 2.0f;
    float rad = ((float)Math.PI / 180.0f) * degrees;

    int xCenter, yCenter;
    RectCenter(bounds, out xCenter, out yCenter);

    float x = xCenter + (a * (float)Math.Cos(rad));
    float y = yCenter + (b * (float)Math.Sin(rad));

    return new PointF(x, y);
    }

    private void Draw(Rectangle rect, Graphics g)
    {
    rect.Inflate(-5, -5);

    using(Pen pen = new Pen(Color.Green, 3)) { //let's be good :)
    g.DrawArc(pen, rect, 0, 315);
    }
    PointF p, pPrev = PointFromEllipse(rect, 0);

    for (int i = 1; i <= 315; i++)
    {
    p = PointFromEllipse(rect, i);
    g.DrawLine(Pens.Red, p, pPrev);
    pPrev = p;
    }
    }
    }

    Here you can see that the green line doesn't reach as far around the arc as
    the red line, despite the fact that both are, in theory, drawing an arc
    through 315º. It is this effect, that the DrawArc method seems to draw
    "short", of which I would be interested in hearing an explanation.

    --
    Thank you,

    Christopher Ireland
     
    Christopher Ireland, Jun 21, 2007
    #3
  4. Christopher Ireland

    Peter Duniho Guest

    On Wed, 20 Jun 2007 23:28:11 -0700, Christopher Ireland
    <> wrote:

    > [...]
    > Here you can see that the green line doesn't reach as far around the arc
    > as
    > the red line, despite the fact that both are, in theory, drawing an arc
    > through 315º. It is this effect, that the DrawArc method seems to draw
    > "short", of which I would be interested in hearing an explanation.


    Ahhh...I understand the question now.

    The answer is that your ellipse function and what Windows does aren't the
    same (obviously). More specifically, the algorithm you've used assumes
    your ellipse was once a circle that's been squashed, and you are
    designating the degrees in the coordinate space of the original perfectly
    round circle. Of course, when you squash the circle in one dimension or
    the other to get an ellipse, you wind up squashing your angles too.

    Windows, on the other hand, is doing a true polar coordinate clipping of a
    circle to obtain the arc. That is, the beginning and ending point of the
    arc, specified in polar coordinates, assumes that you're really drawing an
    ellipse, but designating the start and end points in an unmodified polar
    coordinate space. That is, take the original ellipse, find where it
    intersects with lines drawn from the center outward at the given angles,
    and draw the arc between those lines.

    IMHO, the Windows version is more mathematically correct, but I feel that
    the main point is to decide which is more appropriate to your needs and
    use it consistently. Obviously though, you can't mix and match without
    seeing the discrepancy you're asking about.

    For what it's worth, here's yet another version of your code (see below)
    that I hopes makes it much clearer what's going on. I added a lot more
    stuff, so that you can use the arrow keys to adjust the limits of your
    arc, as well as drawing a perfectly round circle so that you can compare
    the angles for that circle with the angles for the ellipse. In
    particular, note that the version of the ellipse drawn with DrawArc()
    aligns perfectly with the circle, while your ellipse calculate lags and
    leads the circle depending on where in the arc you are.

    Hope that helps.

    Pete


    Here's the code:

    protected override void OnPaint(PaintEventArgs e)
    {
    base.OnPaint(e);
    Draw(ClientRectangle, e.Graphics);
    }

    protected override void OnResize(EventArgs e)
    {
    base.OnResize(e);
    Invalidate();
    }

    private float _degreesTotal = 360.0f;

    protected override bool ProcessDialogKey(Keys keyData)
    {
    bool fHandled = false;

    switch (keyData)
    {
    case Keys.Left:
    _degreesTotal -= 1.0f;
    fHandled = true;
    break;
    case Keys.Right:
    _degreesTotal += 1.0f;
    fHandled = true;
    break;
    }

    if (fHandled)
    {
    if (_degreesTotal < 0)
    {
    _degreesTotal += 360.0f;
    }
    else if (_degreesTotal >= 360.0f)
    {
    _degreesTotal -= 360.0f;
    }
    Invalidate();
    }

    return base.ProcessDialogKey(keyData);
    }

    private Point RectCenter(Rectangle r)
    {
    return new Point((r.Left + r.Right) / 2, (r.Top + r.Bottom) /
    2);
    }

    private PointF PointFromEllipse(Rectangle bounds, float degrees)
    {
    float a = bounds.Width / 2.0f;
    float b = bounds.Height / 2.0f;
    float rad = ((float)Math.PI / 180.0f) * degrees;

    Point ptCenter = RectCenter(bounds);

    float x = ptCenter.X + (a * (float)Math.Cos(rad));
    float y = ptCenter.Y + (b * (float)Math.Sin(rad));

    return new PointF(x, y);
    }

    private void Draw(Rectangle rect, Graphics g)
    {
    rect.Inflate(-5, -5);

    Rectangle rectSquare;
    Point ptCenter = RectCenter(rect);
    int dxySquare = Math.Min(rect.Width, rect.Height);

    rectSquare = new Rectangle(new Point(ptCenter.X - dxySquare /
    2, ptCenter.Y - dxySquare / 2), new Size(dxySquare, dxySquare));

    g.DrawLine(Pens.Black, rect.Location, new Point(rect.Left
    + rect.Width, rect.Top + rect.Height));
    g.DrawLine(Pens.Black, new Point(rect.Left + rect.Width,
    rect.Top), new Point(rect.Left, rect.Top + rect.Height));
    g.DrawString("_degreesTotal: " + _degreesTotal.ToString(),
    Font, Brushes.Black, 10.0f, 10.0f);

    using (Pen penGreen = new Pen(Color.Green, 3.0f))
    {
    g.DrawArc(penGreen, rect, 0, _degreesTotal);
    g.DrawArc(penGreen, rectSquare, 0, _degreesTotal);
    }

    PointF ptPrevSquare = PointFromEllipse(rectSquare, 0),
    ptPrev = PointFromEllipse(rect, 0);

    for (int i = 1; i <= _degreesTotal; i++)
    {
    PointF ptSquare = PointFromEllipse(rectSquare, i),
    pt = PointFromEllipse(rect, i);
    g.DrawLine(Pens.Red, pt, ptPrev);
    g.DrawLine(Pens.Red, ptSquare, ptPrevSquare);
    ptPrev = pt;
    ptPrevSquare = ptSquare;
    }
    }
     
    Peter Duniho, Jun 21, 2007
    #4
  5. Peter,

    > In particular, note that the version of the ellipse
    > drawn with DrawArc() aligns perfectly with the circle, while your
    > ellipse calculate lags and leads the circle depending on where in the
    > arc you are.


    Thank you again for your time, Peter.

    Yes, I noticed that affect as well.

    Now then, what I'm after is to get a _correct_ point back from the ellipse
    drawn by DrawArc when I pass PointFromEllipse a given angle. Given that the
    wikipedia ellipse algorithm turns out to be a squashed circle, do you happen
    to know what the actual windows (mathematically correct) algorithm is so I
    can make an accurate calculation?

    --
    Thank you,

    Christopher Ireland

    "Growth for the sake of growth is the ideology of the cancer cell."
    Edward Abbey
     
    Christopher Ireland, Jun 21, 2007
    #5
  6. Christopher Ireland

    Peter Duniho Guest

    On Thu, 21 Jun 2007 12:37:34 -0700, Christopher Ireland
    <> wrote:

    > [...]
    > Now then, what I'm after is to get a _correct_ point back from the
    > ellipse
    > drawn by DrawArc when I pass PointFromEllipse a given angle. Given that
    > the
    > wikipedia ellipse algorithm turns out to be a squashed circle, do you
    > happen
    > to know what the actual windows (mathematically correct) algorithm is so
    > I
    > can make an accurate calculation?


    Not off the top of my head, no. It should be a simple geometry problem
    though. Possibly easier if solved in polar coordinate space than in
    Cartesian.

    Pete
     
    Peter Duniho, Jun 21, 2007
    #6
  7. Peter,

    > Not off the top of my head, no. It should be a simple geometry
    > problem though. Possibly easier if solved in polar coordinate space
    > than in Cartesian.


    Ok. I'm not sure it's that trivial.

    I'll always have the alternative of drawing the windows arc to a graphics
    path, grabbing the array of points and reading in the last one. Not as
    elegant though :)

    -
    Thank you,

    Christopher Ireland
     
    Christopher Ireland, Jun 21, 2007
    #7
  8. Christopher Ireland

    Peter Duniho Guest

    On Thu, 21 Jun 2007 13:34:30 -0700, Christopher Ireland
    <> wrote:

    >> Not off the top of my head, no. It should be a simple geometry
    >> problem though. Possibly easier if solved in polar coordinate space
    >> than in Cartesian.

    >
    > Ok. I'm not sure it's that trivial.


    Gracious...don't give up so easily.

    The Wikipedia article on Ellipse has exactly the formula you want. And I
    was right, in polar coordinates it's a pretty simple calculation (assuming
    you've got the Wikipedia article to give you the exact formula :)...I'm
    happy to not have to have derived it myself).

    Try this:

    private PointF PointFromEllipse(Rectangle rectBounds, float
    degrees)
    {
    // Convert the basic input into something more usable
    Point ptCenter = RectCenter(rectBounds);
    float radians = ((float)Math.PI * degrees) / 180.0f;
    float radiusH = rectBounds.Width / 2.0f,
    radiusV = rectBounds.Height / 2.0f;

    // Calculate the radius of the ellipse for the given angle
    bool fXMajor = radiusH > radiusV;
    float a = fXMajor ? radiusH : radiusV,
    b = fXMajor ? radiusV : radiusH;
    float eccentricity = (float)Math.Sqrt(1 - (b * b) / (a * a));
    float radiusAngle = b / (float)Math.Sqrt(1 - (eccentricity *
    eccentricity) * Math.Pow(Math.Cos(radians), 2));

    // Convert the radius back to Cartesian coordinates
    if (fXMajor)
    {
    return new PointF(ptCenter.X + radiusAngle *
    (float)Math.Cos(radians),
    ptCenter.Y + radiusAngle * (float)Math.Sin(radians));
    }
    else
    {
    return new PointF(ptCenter.X + radiusAngle *
    (float)Math.Sin(radians),
    ptCenter.Y + radiusAngle * (float)Math.Cos(radians));
    }
    }

    Pete
     
    Peter Duniho, Jun 21, 2007
    #8
  9. Christopher Ireland

    Peter Duniho Guest

    On Thu, 21 Jun 2007 14:53:35 -0700, Peter Duniho
    <> wrote:

    > Try this:
    >
    > [function snipped]


    Oops.

    Well, there's a pretty significant bug, in that the routine doesn't handle
    vertically-oriented ellipses (major axis parallel to the Y axis)
    correctly. I tried to include that, but screwed it up and didn't bother
    to test that case before posting.

    I leave the fix as an exercise for the reader. It's not actually that
    hard. :)
     
    Peter Duniho, Jun 21, 2007
    #9
  10. Peter,

    > Gracious...don't give up so easily.


    I'm sorry, I don't think I implied I was going to give up, did I?

    > The Wikipedia article on Ellipse has exactly the formula you want. And I
    > was right, in polar coordinates it's a pretty simple
    > calculation (assuming you've got the Wikipedia article to give you
    > the exact formula :)...I'm happy to not have to have derived it
    > myself).


    Well, I guess if it was that trivial one would have got it right first time,
    huh ;-)?

    > Try this:


    Thank you again Peter, I'll see if I can make this work for me.

    --
    Thank you,

    Christopher Ireland
     
    Christopher Ireland, Jun 22, 2007
    #10
  11. Christopher Ireland

    Peter Duniho Guest

    On Thu, 21 Jun 2007 23:30:12 -0700, Christopher Ireland
    <> wrote:

    > Well, I guess if it was that trivial one would have got it right first
    > time,
    > huh ;-)?


    I don't think I used the word "trivial". The formula is simple, that's
    all.

    Still, you'd think one would, wouldn't you? :)

    At least I recognized the need to deal with the case, even if I didn't
    handle it correctly. That said, I botched it up well enough that it
    occurs to me that if you start with the code I posted, you could spend a
    while figuring out what needs to be taken out altogether.

    I like leaving _something_ for the reader to figure out on their own, but
    in this case that seems a little unfair. So, here's the correct function:

    private PointF PointFromEllipse(Rectangle rectBounds, float
    degrees)
    {
    // Convert the basic input into something more usable
    Point ptCenter = RectCenter(rectBounds);
    float radians = ((float)Math.PI * degrees) / 180.0f;
    float radiusH = rectBounds.Width / 2.0f,
    radiusV = rectBounds.Height / 2.0f;

    // Calculate the radius of the ellipse for the given angle
    bool fXMajor = radiusH > radiusV;
    float a = fXMajor ? radiusH : radiusV,
    b = fXMajor ? radiusV : radiusH;
    float eccentricity = (float)Math.Sqrt(1 - (b * b) / (a * a));
    float radiusAngle = b / (float)Math.Sqrt(1 - (eccentricity *
    eccentricity) *
    Math.Pow(fXMajor ? Math.Cos(radians) : Math.Sin(radians),
    2));

    // Convert the radius back to Cartesian coordinates
    return new PointF(ptCenter.X + radiusAngle *
    (float)Math.Cos(radians),
    ptCenter.Y + radiusAngle * (float)Math.Sin(radians));
    }
     
    Peter Duniho, Jun 22, 2007
    #11
  12. Peter,

    > At least I recognized the need to deal with the case, even if I didn't
    > handle it correctly.


    And I didn't know how to deal with the case, you're quite correct. That's
    why I wrote to this group with the hope that somebody like you did :)

    > I like leaving _something_ for the reader to figure out on their own,
    > but in this case that seems a little unfair.


    LOL!

    >So, here's the correct function:


    That's perfect, thank you Peter.

    --
    Thank you,

    Christopher Ireland
     
    Christopher Ireland, Jun 22, 2007
    #12
    1. Advertisements

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Nanda

    Draw Ellipse and store in gif or png file?

    Nanda, Jul 29, 2003, in forum: Microsoft C# .NET
    Replies:
    1
    Views:
    302
    Chua Wen Ching
    Jul 31, 2003
  2. John Baro

    GDI+ DrawArc

    John Baro, May 17, 2004, in forum: Microsoft C# .NET
    Replies:
    0
    Views:
    681
    John Baro
    May 17, 2004
  3. Christopher Ireland

    Graphics.DrawArc explanation requested

    Christopher Ireland, Aug 24, 2007, in forum: Microsoft C# .NET
    Replies:
    2
    Views:
    456
    Christopher Ireland
    Aug 27, 2007
  4. weird0

    how to Draw an ellipse using c#

    weird0, Oct 16, 2007, in forum: Microsoft C# .NET
    Replies:
    2
    Views:
    950
    Bob Powell [MVP]
    Oct 16, 2007
  5. Slickuser

    Draw ellipse with specify x,y,width, height

    Slickuser, Nov 29, 2007, in forum: Microsoft C# .NET
    Replies:
    4
    Views:
    517
    MikeY
    Nov 29, 2007
Loading...

Share This Page