给定边界矩形,起始角度和扫掠角度,如何确定弧端点的点

时间:2016-06-10 23:18:26

标签: c# .net wpf trigonometry

给定一个边界矩形,起始角度和扫掠角度,如何确定弧线每端的点?

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 = ???
}

enter image description here

1 个答案:

答案 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事件相关联会产生以下结果:

Final Result

最后一点,由于四舍五入,起点和终点可能会偏离一两个像素。如果你想要更高的准确度,你必须自己绘制弧线。