[英]Drawing a 3-Point Arc VB.NET
我正在嘗試創建一個簡單的界面(如繪畫)來繪制一些基本形狀,例如直線,圓和弧。 我已經弄清楚了直線和圓,但是我很難獲得畫圓弧所需的效果。 我現在正在.NET中使用graphics.Draw...
方法,但可能願意嘗試其他本機方法。
我正在尋找創建用戶選擇起點,終點和“半徑”的功能。 但是,我希望“半圓”觸及所有這三個點。 在圖片之后(從應用程序中獲取),根據用戶的三次單擊來計算
Pt5
並設置圓弧的“半徑”。 該圖像描述了圓的中心在Pt4
,但理想情況下將使用邊緣處的Pt1
和Pt2
。
一旦計算出中心,我想用它來創建弧的邊界( DrawArc(Pens.Black, CInt(Center.X - Radius), CInt(Center.Y - Radius), CInt(Radius * 2), CInt(Radius * 2), ?, ?)
),並通過沿Pt1-Pt2
線投影點Pt1
和Pt2
“切”圓。
這是我對上面的線/圓的計算(很抱歉,冗長的問題...):
'' 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
。 (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.