On Wed, 20 Jun 2007 23:28:11 -0700, Christopher Ireland
<ci******@gmail.comwrote:
[...]
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;
}
}