给定一个边界矩形,起始角度和扫掠角度,如何确定弧线每端的点?
private void myPaint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
Rectangle rc = new Rectangle(242, 299, 200, 300);
Pen penRed = new Pen(Color.Red, 1);
g.DrawArc(penRed, rc, 18, -108);
// TODO - Determine Point of each end of arc
// Point pt1 = ???
// Point pt2 = ???
}
答案 0 :(得分:3)
使用这个优秀Mathematics answer的椭圆方程我们可以根据起始角度和扫描计算椭圆的起点和终点。
首先,我们需要边界框的中心,因此我们知道如何移动坐标。那只是
double minTheta = (Math.PI / 180) * (360 - start);
double maxTheta = (Math.PI / 180) * (360 - (start + sweep));
然后我们需要将角度从度数转换为弧度,并将顺时针角度更改为逆时针角度,如下所示:
public double NormalizeAngle(double angle)
{
while (angle >= 360) angle -= 360;
while (angle < 0) angle += 360;
return angle;
}
public void AdjustCoordinatesForAngle(double angle, ref double x, ref double y)
{
if (angle > 0 && angle <= 90)
{
x *= 1;
y *= 1;
}
else if (angle >= 90 && angle < 180)
{
x *= -1;
y *= 1;
}
else if (angle >= 180 && angle < 270)
{
x *= -1;
y *= -1;
}
else if (angle >= 270 && angle < 360)
{
x *= 1;
y *= -1;
}
}
我们还定义了2个辅助函数,第一个用于标准化角度(将任意角度映射到0-360范围内),第二个用于将计算出的(x,y)坐标调整到正确的象限。 (鉴于积极的y实际上是在表格上)
double minTheta = (Math.PI / 180) * (360 - start);
double maxTheta = (Math.PI / 180) * (360 - (start + sweep));
double a = width / 2.0;
double b = height / 2.0;
double denom = Math.Pow(a, 2) * Math.Pow(Math.Tan(minTheta), 2);
denom = denom / Math.Pow(b, 2);
denom = Math.Sqrt(denom + 1);
double x = Math.Abs(a / denom);
double y = Math.Abs((a * Math.Tan(minTheta)) / denom);
start = NormalizeAngle(start);
this.AdjustCoordinatesForAngle(start, ref x, ref y);
我们现在有足够的信息来计算起点和终点。
x += cX;
y += cY;
这些坐标是相对于边界框的中心,所以我们使用上面计算的中心点来抵消它:
g.FillRectangle(Brushes.Purple, new Rectangle((int)x - 3, (int)y - 3, 6, 6));
我们现在可以说明一点:
private void myPaint(object sender, PaintEventArgs e)
{
double start = 18;
double sweep = -108;
Graphics g = e.Graphics;
g.Clear(Color.Black);
Rectangle rc = new Rectangle(200, 10, 200, 300);
int cX = (rc.Left + rc.Right) / 2;
int cY = (rc.Bottom + rc.Top) / 2;
g.FillRectangle(Brushes.Yellow, Rectangle.FromLTRB(cX - 3, cY - 3, cX + 3, cY + 3));
int width = rc.Width;
int height = rc.Height;
if (start >= 360) start -= 360;
double minTheta = (Math.PI / 180) * (360 - start);
double maxTheta = (Math.PI / 180) * (360 - (start + sweep));
double a = width / 2.0;
double b = height / 2.0;
double denom = Math.Pow(a, 2) * Math.Pow(Math.Tan(minTheta), 2);
denom = denom / Math.Pow(b, 2);
denom = Math.Sqrt(denom + 1);
double x = Math.Abs(a / denom);
double y = Math.Abs((a * Math.Tan(minTheta)) / denom);
start = NormalizeAngle(start);
this.AdjustCoordinatesForAngle(start, ref x, ref y);
x += cX;
y += cY;
g.FillRectangle(Brushes.Purple, new Rectangle((int)x - 3, (int)y - 3, 6, 6));
denom = Math.Pow(a, 2) * Math.Pow(Math.Tan(maxTheta), 2);
denom = denom / Math.Pow(b, 2);
denom = Math.Sqrt(denom + 1);
x = Math.Abs(a / denom);
y = Math.Abs((a * Math.Tan(maxTheta)) / denom);
double endAngle = (start + sweep);
endAngle = NormalizeAngle(endAngle);
this.AdjustCoordinatesForAngle(endAngle, ref x, ref y);
x += cX;
y += cY;
g.FillRectangle(Brushes.Blue, new Rectangle((int)x - 3, (int)y - 3, 6, 6));
Pen penRed = new Pen(Color.Red, 1);
g.DrawRectangle(Pens.Green, rc);
g.DrawArc(penRed, rc, (float)start, (float)sweep);
}
所有绘画功能都是这样的:
{{1}}
我将窗口背景涂成黑色,使得盒子和线条更加突出,我留下了一些额外的绘图元素,因此更容易看出上面的计算中发生了什么。
将代码放入表单中,并与表单的paint事件相关联会产生以下结果:
最后一点,由于四舍五入,起点和终点可能会偏离一两个像素。如果你想要更高的准确度,你必须自己绘制弧线。