绘制 3 点圆弧 VB.NET
Drawing a 3-Point Arc VB.NET
我正在尝试创建一个简单的界面(如绘画)来绘制一些基本形状,例如直线、圆和弧。我已经弄清楚了直线和圆圈,但我很难获得所需的画弧效果。我现在在 .NET 中使用 graphics.Draw...
方法,但可能愿意尝试不同的本机方法。
我正在寻找创建用户选择起点、终点和 "radius" 的功能。但是,我希望 "semi-circle" 触及所有三个点。 下图(取自应用),Pt5
是根据用户的三次点击计算出来的,设置弧度的"radius"。该图像描绘了圆心在 Pt4
上,但理想情况下,圆心将在边缘 Pt1
和 Pt2
上计算。
计算出中心后,我想用它来创建圆弧的边界 (DrawArc(Pens.Black, CInt(Center.X - Radius), CInt(Center.Y - Radius), CInt(Radius * 2), CInt(Radius * 2), ?, ?)
) 和 "cut" 通过点 Pt1
和 Pt2
沿着 Pt1-Pt2
线。
这是我对上面 lines/circle 的计算(抱歉问题太长...):
'' Pt1 and Pt2 are taken earlier
Dim pt3 As Point = e.Location
Dim pt4, pt5, pt10, pt11, Cntr As New Point
Dim m1, m2, m3, m4 As Double
Dim b1, b2, b3, b4, b5 As Double
Dim r As Double
'' Get center (midpoint)
pt4.X = ((pt1.X - pt2.X) / 2) + pt2.X
pt4.Y = ((pt1.Y - pt2.Y) / 2) + pt2.Y
'' Get picked-point slope
m1 = (pt2.Y - pt1.Y) / (pt2.X - pt1.X)
'' Inverse slope
m2 = -(1 / m1)
'' Get picked-intercept
b1 = pt1.Y - (m1 * pt1.X)
'' Get perpendicular intercept
b2 = pt4.Y - (m2 * pt4.X)
'' Get parallel intercept
b3 = pt3.Y - (m1 * pt3.X)
''ln1: y = m1X + b1 ; (pt1, pt2)
''ln2: y = m2X + b2 ; (pt4, pt5)
''ln3: y = m1X + b3 ; (pt3, pt5)
'' pt5.X = (yInt1 - yInt2)/(slope1-slope2)
'' pt5.Y = (slope2 * pt5.X) + yInt2
pt5.X = ((b2 - b3) / (m1 - m2))
pt5.Y = (m2 * pt5.X) + b2
'' Get perpendicular slope between Pt1 and Pt5
m3 = -(1 / (pt5.Y - pt1.Y) / (pt5.X - pt1.X))
'' Get perpendicular slope between Pt2 and Pt5
m4 = -(1 / ((pt5.Y - pt2.Y) / (pt5.X - pt2.X)))
'' Get perpendicular intercept between Pt1 and Pt5
b4 = pt1.Y - (m3 * pt1.X)
'' Get perpendicular intercept between Pt2 and Pt5
b5 = pt2.Y - (m4 * pt2.X)
Cntr.X = (b5 - b4) / (m3 - m4) '((m3 * m4 * ((pt1.Y - pt2.Y))) + (m4 * (pt1.X + pt5.X)) - (m3 * (pt5.X + pt2.X))) / (2 * (m4 - m3))
Cntr.Y = (m2 * Cntr.X) + b2
'' Calculate radius
r = Math.Sqrt(((pt5.Y - Cntr.Y) ^ 2) + ((pt5.X - Cntr.X) ^ 2))
所以我们都在同一页上,DrawArc
方法有几个重载,但我将使用这个
Public Sub DrawArc(pen As System.Drawing.Pen,
x As Single,
y As Single,
width As Single,
height As Single,
startAngle As Single,
sweepAngle As Single)
为了使用它,我们需要计算包含圆弧的边界矩形,如果它被绘制为一个完整的椭圆,开始扫描的角度,以及圆弧的中心角。
对于以下内容,用户以更常见的方式定义三个圆弧点:中心、起点、角度。也就是说,第一次单击定义圆心,第二次单击确定起点,第三次单击确定圆弧跨越的角度。将其适用于其他方法很容易,但我使用的方法具有最简单的数学解释。
第一次点击时,我们将鼠标位置记录到变量center
中。现在这是我们圆弧的中心。
第二次点击时,我们将鼠标位置记录到变量start
中。这个点给了我们两个关键信息,半径和到起点的角度。我们使用毕达哥拉斯定理(又名距离公式)计算半径如下:
r = Math.Sqrt(Math.Pow(start.X - center.X, 2) + Math.Pow(start.Y - center.Y))
要计算角度,我们可以使用 Δy/Δx 的反正切值
theta = Math.Atan((start.Y - center.Y) / (start.X - center.X))
(我会把它作为练习留给你检查 (start.X - center.X) = 0
并弄清楚它是否是 +/- π/2。)
我们现在也有足够的信息来计算圆弧的边界矩形。由于圆弧是圆的,我们的盒子就是
x = center.X - r
y = center.Y - r
width = 2 * r
height = 2 * r
当用户第三次点击(或者实际上是每次移动鼠标)时,我们将值存储到 angle
变量中。现在,我们需要做的就是计算该点的角度,因为我们正在绘制一个圆弧段。第二个角度的计算方法同上一个:
alpha = Math.Atan((angle.Y - center.Y) / (angle.X - center.X))
(记住,和以前一样,检查 (angle.X - center.X) = 0
)
我们现在可以通过减去
来计算后掠角
sweep = theta - alpha
我们现在有足够的信息来使用我们计算的参数调用 DrawArc
方法:
g.DrawArc(Pen.Black, x, y, width, height, theta, sweep)
一些最后的想法:
- 此方法总是从头开始逆时针绘制圆弧
角度。
center
、start
、end
变量都是PointF
,需要转换。我删除了转换和捕获逻辑以使上面的代码更容易理解。
- 所有其他变量都是
Single
。您需要将 Atan
函数的结果转换为 Single
.
- 如果在计算反正切时Δx为0,则可以通过比较y值来判断角度是正还是负。如果
(center.Y < start.Y)
那么你知道它是 +π/2。
-您还应该检查半径是否为 0。
我可能完全误解了你想做什么,但假设你对 pt5 的计算是你想要的,并且你希望 pt1 和 pt2 在圆周上,那么这样的事情可能会做到。
我想你希望 pt5 成为中心。
r = Math.Sqrt((pt5.Y - pt1.Y) ^ 2 + (pt5.X - pt1.X) ^ 2)
假设您已经可以访问适当的图形对象,g
g.DrawEllipse(Pens.Blue, New Rectangle(pt5.X - r, pt5.Y - r, 2 * r, 2 * r))
我使用了很多资源来完成我的 objective。最后,由于 this 问题,我最终放弃了对 ExcludeClip
使用 DrawArc
。基本上,一旦我创建了正确的椭圆,我需要剪裁点 1 和点 2 之间的椭圆区域。下面,您将看到我的解决方案。第一张图片显示了我用来验证我的数学正确性的 "helper" 行,然后是最终结果。
图例:
*黄线1:在中点(Pt4-Pt5)垂直于Pt1-Pt2线
*黄线2:垂直于第一条黄线(平行于Pt1-Pt2线)(Pt3-Pt5)
*Pt5:黄线交点
*红线:Pt1-Pt5
*蓝色Line:Pt2-Pt5
*粉色线:中点 Red/Blue 的垂直线 (Pt10,Pt11)
*居中:粉色线(Cntr)的交点
*米色线:Red/Blue 线的扩展,用于 ExcludeClip
{(Pt20-Pt1-Pt5) 和 (Pt21-Pt2-Pt5)}
这是我的代码:
Dim pt3 As Point = e.Location
Dim pt4, pt5, pt10, pt11, pt20, pt21, Cntr As New Point
Dim m1, m2, mr, mt As Double
Dim b1, b2, b3, b4, b5 As Double
Dim r As Double
'' Get center (midpoint)
pt4.X = ((pt1.X - pt2.X) / 2) + pt2.X
pt4.Y = ((pt1.Y - pt2.Y) / 2) + pt2.Y
'' Get picked-point slope
m1 = (pt2.Y - pt1.Y) / (pt2.X - pt1.X)
'' Inverse slope
m2 = -(1 / m1)
'' Get picked-intercept
b1 = pt1.Y - (m1 * pt1.X)
'' Get perpendicular intercept
b2 = pt4.Y - (m2 * pt4.X)
'' Get parallel intercept
b3 = pt3.Y - (m1 * pt3.X)
''ln1: y = m1X + b1 ; (pt1, pt2)
''ln2: y = m2X + b2 ; (pt4, pt5)
''ln3: y = m1X + b3 ; (pt3, pt5)
'' pt5.X = (yInt1 - yInt2)/(slope1-slope2)
'' pt5.Y = (slope2 * pt5.X) + yInt2
pt5.X = ((b2 - b3) / (m1 - m2))
pt5.Y = (m2 * pt5.X) + b2
'' Setup Pt1-Pt5 perpendicular line (Pt10-Cntr)
pt10.X = ((pt5.X - pt1.X) / 2) + pt1.X
pt10.Y = ((pt5.Y - pt1.Y) / 2) + pt1.Y
'' Setup Pt2-Pt5 perpendicular line (Pt11-Cntr)
pt11.X = ((pt5.X - pt2.X) / 2) + pt2.X
pt11.Y = ((pt5.Y - pt2.Y) / 2) + pt2.Y
'' Get perpendicular slope between Pt1 and Pt5
mr = (pt5.Y - pt1.Y) / (pt5.X - pt1.X)
'' Get perpendicular slope between Pt2 and Pt5
mt = (pt2.Y - pt5.Y) / (pt2.X - pt5.X)
'' Get perpendicular intercept between Pt1 and Pt5
b4 = pt1.Y - (mr * pt1.X)
'' Get perpendicular intercept between Pt2 and Pt5
b5 = pt2.Y - (mt * pt2.X)
If Not mr > 10000 And Not mt > 10000 And Not mr < -10000 And Not mt < -10000 And Not mr = 0 And Not mt = 0 And Not mr = mt Then
Cntr.X = (((mr * mt) * ((pt2.Y - pt1.Y))) + (mr * (pt5.X + pt2.X)) - (mt * (pt1.X + pt5.X))) / (2 * (mr - mt))
Cntr.Y = -(1 / mr) * (Cntr.X - ((pt1.X + pt5.X) / 2)) + ((pt1.Y + pt5.Y) / 2)
'' Calculate radius
r = Math.Sqrt(((pt5.Y - Cntr.Y) ^ 2) + ((pt5.X - Cntr.X) ^ 2))
'' Determine which side to extend Chords
If pt1.X > pt5.X Then
pt20.X = pt1.X + (r * 2)
Else
pt20.X = pt1.X - (r * 2)
End If
pt20.Y = (mr * (pt20.X)) + b4
If pt2.X > pt5.X Then
pt21.X = pt2.X + (r * 2)
Else
pt21.X = pt2.X - (r * 2)
End If
pt21.Y = (mt * (pt21.X)) + b5
'g.DrawLine(Pens.Black, pt1, pt2)
'g.DrawLine(Pens.Orange, pt4, pt5)
'g.DrawLine(Pens.Orange, pt3, pt5)
'g.DrawLine(Pens.Pink, pt10, Cntr)
'g.DrawLine(Pens.Pink, pt11, Cntr)
'g.DrawLine(Pens.Red, pt1, pt5)
'g.DrawLine(Pens.Blue, pt2, pt5)
'g.DrawLine(Pens.Beige, pt1, pt20)
'g.DrawLine(Pens.Beige, pt2, pt21)
Dim path As New Drawing2D.GraphicsPath()
path.AddPolygon({pt20, pt1, Cntr, pt2, pt21})
g.ExcludeClip(New Region(path))
g.DrawEllipse(Pens.Black, CInt(Cntr.X - r), CInt(Cntr.Y - r), CInt(r * 2), CInt(r * 2))
Else
Debug.WriteLine("mr: " & mr.ToString & vbTab & "mt: " & mt.ToString)
End If
This帮我根据圆周上的3个点求圆心
我正在尝试创建一个简单的界面(如绘画)来绘制一些基本形状,例如直线、圆和弧。我已经弄清楚了直线和圆圈,但我很难获得所需的画弧效果。我现在在 .NET 中使用 graphics.Draw...
方法,但可能愿意尝试不同的本机方法。
我正在寻找创建用户选择起点、终点和 "radius" 的功能。但是,我希望 "semi-circle" 触及所有三个点。 Pt5
是根据用户的三次点击计算出来的,设置弧度的"radius"。该图像描绘了圆心在 Pt4
上,但理想情况下,圆心将在边缘 Pt1
和 Pt2
上计算。
计算出中心后,我想用它来创建圆弧的边界 (DrawArc(Pens.Black, CInt(Center.X - Radius), CInt(Center.Y - Radius), CInt(Radius * 2), CInt(Radius * 2), ?, ?)
) 和 "cut" 通过点 Pt1
和 Pt2
沿着 Pt1-Pt2
线。
这是我对上面 lines/circle 的计算(抱歉问题太长...):
'' Pt1 and Pt2 are taken earlier
Dim pt3 As Point = e.Location
Dim pt4, pt5, pt10, pt11, Cntr As New Point
Dim m1, m2, m3, m4 As Double
Dim b1, b2, b3, b4, b5 As Double
Dim r As Double
'' Get center (midpoint)
pt4.X = ((pt1.X - pt2.X) / 2) + pt2.X
pt4.Y = ((pt1.Y - pt2.Y) / 2) + pt2.Y
'' Get picked-point slope
m1 = (pt2.Y - pt1.Y) / (pt2.X - pt1.X)
'' Inverse slope
m2 = -(1 / m1)
'' Get picked-intercept
b1 = pt1.Y - (m1 * pt1.X)
'' Get perpendicular intercept
b2 = pt4.Y - (m2 * pt4.X)
'' Get parallel intercept
b3 = pt3.Y - (m1 * pt3.X)
''ln1: y = m1X + b1 ; (pt1, pt2)
''ln2: y = m2X + b2 ; (pt4, pt5)
''ln3: y = m1X + b3 ; (pt3, pt5)
'' pt5.X = (yInt1 - yInt2)/(slope1-slope2)
'' pt5.Y = (slope2 * pt5.X) + yInt2
pt5.X = ((b2 - b3) / (m1 - m2))
pt5.Y = (m2 * pt5.X) + b2
'' Get perpendicular slope between Pt1 and Pt5
m3 = -(1 / (pt5.Y - pt1.Y) / (pt5.X - pt1.X))
'' Get perpendicular slope between Pt2 and Pt5
m4 = -(1 / ((pt5.Y - pt2.Y) / (pt5.X - pt2.X)))
'' Get perpendicular intercept between Pt1 and Pt5
b4 = pt1.Y - (m3 * pt1.X)
'' Get perpendicular intercept between Pt2 and Pt5
b5 = pt2.Y - (m4 * pt2.X)
Cntr.X = (b5 - b4) / (m3 - m4) '((m3 * m4 * ((pt1.Y - pt2.Y))) + (m4 * (pt1.X + pt5.X)) - (m3 * (pt5.X + pt2.X))) / (2 * (m4 - m3))
Cntr.Y = (m2 * Cntr.X) + b2
'' Calculate radius
r = Math.Sqrt(((pt5.Y - Cntr.Y) ^ 2) + ((pt5.X - Cntr.X) ^ 2))
所以我们都在同一页上,DrawArc
方法有几个重载,但我将使用这个
Public Sub DrawArc(pen As System.Drawing.Pen,
x As Single,
y As Single,
width As Single,
height As Single,
startAngle As Single,
sweepAngle As Single)
为了使用它,我们需要计算包含圆弧的边界矩形,如果它被绘制为一个完整的椭圆,开始扫描的角度,以及圆弧的中心角。
对于以下内容,用户以更常见的方式定义三个圆弧点:中心、起点、角度。也就是说,第一次单击定义圆心,第二次单击确定起点,第三次单击确定圆弧跨越的角度。将其适用于其他方法很容易,但我使用的方法具有最简单的数学解释。
第一次点击时,我们将鼠标位置记录到变量center
中。现在这是我们圆弧的中心。
第二次点击时,我们将鼠标位置记录到变量start
中。这个点给了我们两个关键信息,半径和到起点的角度。我们使用毕达哥拉斯定理(又名距离公式)计算半径如下:
r = Math.Sqrt(Math.Pow(start.X - center.X, 2) + Math.Pow(start.Y - center.Y))
要计算角度,我们可以使用 Δy/Δx 的反正切值
theta = Math.Atan((start.Y - center.Y) / (start.X - center.X))
(我会把它作为练习留给你检查 (start.X - center.X) = 0
并弄清楚它是否是 +/- π/2。)
我们现在也有足够的信息来计算圆弧的边界矩形。由于圆弧是圆的,我们的盒子就是
x = center.X - r
y = center.Y - r
width = 2 * r
height = 2 * r
当用户第三次点击(或者实际上是每次移动鼠标)时,我们将值存储到 angle
变量中。现在,我们需要做的就是计算该点的角度,因为我们正在绘制一个圆弧段。第二个角度的计算方法同上一个:
alpha = Math.Atan((angle.Y - center.Y) / (angle.X - center.X))
(记住,和以前一样,检查 (angle.X - center.X) = 0
)
我们现在可以通过减去
来计算后掠角sweep = theta - alpha
我们现在有足够的信息来使用我们计算的参数调用 DrawArc
方法:
g.DrawArc(Pen.Black, x, y, width, height, theta, sweep)
一些最后的想法:
- 此方法总是从头开始逆时针绘制圆弧 角度。
center
、start
、end
变量都是PointF
,需要转换。我删除了转换和捕获逻辑以使上面的代码更容易理解。- 所有其他变量都是
Single
。您需要将Atan
函数的结果转换为Single
. - 如果在计算反正切时Δx为0,则可以通过比较y值来判断角度是正还是负。如果
(center.Y < start.Y)
那么你知道它是 +π/2。 -您还应该检查半径是否为 0。
我可能完全误解了你想做什么,但假设你对 pt5 的计算是你想要的,并且你希望 pt1 和 pt2 在圆周上,那么这样的事情可能会做到。
我想你希望 pt5 成为中心。
r = Math.Sqrt((pt5.Y - pt1.Y) ^ 2 + (pt5.X - pt1.X) ^ 2)
假设您已经可以访问适当的图形对象,g
g.DrawEllipse(Pens.Blue, New Rectangle(pt5.X - r, pt5.Y - r, 2 * r, 2 * r))
我使用了很多资源来完成我的 objective。最后,由于 this 问题,我最终放弃了对 ExcludeClip
使用 DrawArc
。基本上,一旦我创建了正确的椭圆,我需要剪裁点 1 和点 2 之间的椭圆区域。下面,您将看到我的解决方案。第一张图片显示了我用来验证我的数学正确性的 "helper" 行,然后是最终结果。
图例:
*黄线1:在中点(Pt4-Pt5)垂直于Pt1-Pt2线
*黄线2:垂直于第一条黄线(平行于Pt1-Pt2线)(Pt3-Pt5)
*Pt5:黄线交点
*红线:Pt1-Pt5
*蓝色Line:Pt2-Pt5
*粉色线:中点 Red/Blue 的垂直线 (Pt10,Pt11)
*居中:粉色线(Cntr)的交点
*米色线:Red/Blue 线的扩展,用于 ExcludeClip
{(Pt20-Pt1-Pt5) 和 (Pt21-Pt2-Pt5)}
这是我的代码:
Dim pt3 As Point = e.Location
Dim pt4, pt5, pt10, pt11, pt20, pt21, Cntr As New Point
Dim m1, m2, mr, mt As Double
Dim b1, b2, b3, b4, b5 As Double
Dim r As Double
'' Get center (midpoint)
pt4.X = ((pt1.X - pt2.X) / 2) + pt2.X
pt4.Y = ((pt1.Y - pt2.Y) / 2) + pt2.Y
'' Get picked-point slope
m1 = (pt2.Y - pt1.Y) / (pt2.X - pt1.X)
'' Inverse slope
m2 = -(1 / m1)
'' Get picked-intercept
b1 = pt1.Y - (m1 * pt1.X)
'' Get perpendicular intercept
b2 = pt4.Y - (m2 * pt4.X)
'' Get parallel intercept
b3 = pt3.Y - (m1 * pt3.X)
''ln1: y = m1X + b1 ; (pt1, pt2)
''ln2: y = m2X + b2 ; (pt4, pt5)
''ln3: y = m1X + b3 ; (pt3, pt5)
'' pt5.X = (yInt1 - yInt2)/(slope1-slope2)
'' pt5.Y = (slope2 * pt5.X) + yInt2
pt5.X = ((b2 - b3) / (m1 - m2))
pt5.Y = (m2 * pt5.X) + b2
'' Setup Pt1-Pt5 perpendicular line (Pt10-Cntr)
pt10.X = ((pt5.X - pt1.X) / 2) + pt1.X
pt10.Y = ((pt5.Y - pt1.Y) / 2) + pt1.Y
'' Setup Pt2-Pt5 perpendicular line (Pt11-Cntr)
pt11.X = ((pt5.X - pt2.X) / 2) + pt2.X
pt11.Y = ((pt5.Y - pt2.Y) / 2) + pt2.Y
'' Get perpendicular slope between Pt1 and Pt5
mr = (pt5.Y - pt1.Y) / (pt5.X - pt1.X)
'' Get perpendicular slope between Pt2 and Pt5
mt = (pt2.Y - pt5.Y) / (pt2.X - pt5.X)
'' Get perpendicular intercept between Pt1 and Pt5
b4 = pt1.Y - (mr * pt1.X)
'' Get perpendicular intercept between Pt2 and Pt5
b5 = pt2.Y - (mt * pt2.X)
If Not mr > 10000 And Not mt > 10000 And Not mr < -10000 And Not mt < -10000 And Not mr = 0 And Not mt = 0 And Not mr = mt Then
Cntr.X = (((mr * mt) * ((pt2.Y - pt1.Y))) + (mr * (pt5.X + pt2.X)) - (mt * (pt1.X + pt5.X))) / (2 * (mr - mt))
Cntr.Y = -(1 / mr) * (Cntr.X - ((pt1.X + pt5.X) / 2)) + ((pt1.Y + pt5.Y) / 2)
'' Calculate radius
r = Math.Sqrt(((pt5.Y - Cntr.Y) ^ 2) + ((pt5.X - Cntr.X) ^ 2))
'' Determine which side to extend Chords
If pt1.X > pt5.X Then
pt20.X = pt1.X + (r * 2)
Else
pt20.X = pt1.X - (r * 2)
End If
pt20.Y = (mr * (pt20.X)) + b4
If pt2.X > pt5.X Then
pt21.X = pt2.X + (r * 2)
Else
pt21.X = pt2.X - (r * 2)
End If
pt21.Y = (mt * (pt21.X)) + b5
'g.DrawLine(Pens.Black, pt1, pt2)
'g.DrawLine(Pens.Orange, pt4, pt5)
'g.DrawLine(Pens.Orange, pt3, pt5)
'g.DrawLine(Pens.Pink, pt10, Cntr)
'g.DrawLine(Pens.Pink, pt11, Cntr)
'g.DrawLine(Pens.Red, pt1, pt5)
'g.DrawLine(Pens.Blue, pt2, pt5)
'g.DrawLine(Pens.Beige, pt1, pt20)
'g.DrawLine(Pens.Beige, pt2, pt21)
Dim path As New Drawing2D.GraphicsPath()
path.AddPolygon({pt20, pt1, Cntr, pt2, pt21})
g.ExcludeClip(New Region(path))
g.DrawEllipse(Pens.Black, CInt(Cntr.X - r), CInt(Cntr.Y - r), CInt(r * 2), CInt(r * 2))
Else
Debug.WriteLine("mr: " & mr.ToString & vbTab & "mt: " & mt.ToString)
End If
This帮我根据圆周上的3个点求圆心