繁体   English   中英

绘制三点弧VB.NET

[英]Drawing a 3-Point Arc VB.NET

我正在尝试创建一个简单的界面(如绘画)来绘制一些基本形状,例如直线,圆和弧。 我已经弄清楚了直线和圆,但是我很难获得画圆弧所需的效果。 我现在正在.NET中使用graphics.Draw...方法,但可能愿意尝试其他本机方法。

我正在寻找创建用户选择起点,终点和“半径”的功能。 但是,我希望“半圆”触及所有这三个点。 在此处输入图片说明 在图片之后(从应用程序中获取),根据用户的三次单击来计算Pt5并设置圆弧的“半径”。 该图像描述了圆的中心在Pt4 ,但理想情况下将使用边缘处的Pt1Pt2

一旦计算出中心,我想用它来创建弧的边界( DrawArc(Pens.Black, CInt(Center.X - Radius), CInt(Center.Y - Radius), CInt(Radius * 2), CInt(Radius * 2), ?, ?) ),并通过沿Pt1-Pt2线投影点Pt1Pt2 “切”圆。

这是我对上面的线/圆的计算(很抱歉,冗长的问题...):

    '' 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)

最后的几点思考:

  • 此方法将始终从起始角度逆时针绘制弧。
  • centerstartend变量都是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.DrawEllipse(Pens.Blue, New Rectangle(pt5.X - r, pt5.Y - r, 2 * r, 2 * r))

我使用了许多资源来实现自己的目标。 最后,由于这个问题,我最终放弃了对DrawArc使用ExcludeClip 基本上,一旦创建了正确的椭圆,就需要在点1和2之间裁剪椭圆的区域。下面,您将看到我的解决方案。 第一张图片显示了我用来验证数学是否正确的“ helper”行,然后是最终结果。

传说:

*黄线1:在中点(Pt4-Pt5)垂直于Pt1-Pt2线

*黄色线2:垂直于第一个黄色的线(与Pt1-Pt2线平行)(Pt3-Pt5)

* Pt5:黄线之间的交点

*红线:Pt1-Pt5

*蓝线:Pt2-Pt5

*粉红线:在中点(Pt10,Pt11)到红色/蓝色的垂直线

*中心:粉红线之间的交点(Cntr)

*米色线: 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

帮助我根据圆周上的3个点找到了圆心

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM