PC Review


Reply
Thread Tools Rate Thread

DrawArc and ellipse geometry (repost)

 
 
Christopher Ireland
Guest
Posts: n/a
 
      20th Jun 2007
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


 
Reply With Quote
 
 
 
 
Peter Duniho
Guest
Posts: n/a
 
      20th Jun 2007
On Wed, 20 Jun 2007 12:58:10 -0700, Christopher Ireland
<(E-Mail Removed)> 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;
}
}
 
Reply With Quote
 
 
 
 
Christopher Ireland
Guest
Posts: n/a
 
      21st Jun 2007
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


 
Reply With Quote
 
Peter Duniho
Guest
Posts: n/a
 
      21st Jun 2007
On Wed, 20 Jun 2007 23:28:11 -0700, Christopher Ireland
<(E-Mail Removed)> 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;
}
}
 
Reply With Quote
 
Christopher Ireland
Guest
Posts: n/a
 
      21st Jun 2007
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


 
Reply With Quote
 
Peter Duniho
Guest
Posts: n/a
 
      21st Jun 2007
On Thu, 21 Jun 2007 12:37:34 -0700, Christopher Ireland
<(E-Mail Removed)> 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
 
Reply With Quote
 
Christopher Ireland
Guest
Posts: n/a
 
      21st Jun 2007
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


 
Reply With Quote
 
Peter Duniho
Guest
Posts: n/a
 
      21st Jun 2007
On Thu, 21 Jun 2007 13:34:30 -0700, Christopher Ireland
<(E-Mail Removed)> 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
 
Reply With Quote
 
Peter Duniho
Guest
Posts: n/a
 
      21st Jun 2007
On Thu, 21 Jun 2007 14:53:35 -0700, Peter Duniho
<(E-Mail Removed)> 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.
 
Reply With Quote
 
Christopher Ireland
Guest
Posts: n/a
 
      22nd Jun 2007
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


 
Reply With Quote
 
 
 
Reply

Thread Tools
Rate This Thread
Rate This Thread:

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Graphics.DrawArc explanation requested Christopher Ireland Microsoft C# .NET 2 27th Aug 2007 10:41 AM
Drawing a Half-Circle and Half-Ellipse (Word 2003)? W. Watson Microsoft Word New Users 3 27th Jan 2006 04:18 PM
DrawArc in CF =?Utf-8?B?U3RldmVu?= Microsoft Dot NET Compact Framework 3 4th Jun 2004 05:11 PM
GDI+ DrawArc John Baro Microsoft C# .NET 0 17th May 2004 12:32 AM
Draw Ellipse and store in gif or png file? Nanda Microsoft C# .NET 1 31st Jul 2003 04:31 AM


Features
 

Advertising
 

Newsgroups
 


All times are GMT +1. The time now is 08:45 AM.