2015-10-21 130 views
1

我試圖創建一個簡單的界面(如繪畫)來繪製幾個基本的形狀,如線,圓和弧。我已經找到了線條和圓圈,但是我很難獲得所需的弧線繪製效果。我現在在.NET中使用graphics.Draw...方法,但可能願意嘗試不同的本機方法。繪製一個三點弧VB.NET

我期待創建用戶選擇起點,終點和「半徑」的功能。但是,我想讓這個「半圓」觸及這三點。 enter image description here繼照片(取自應用程序)後,Pt5由用戶的三次點擊計算並設置弧的「半徑」。圖像描繪了圓的中心位於Pt4,但理想情況下,中心將在邊緣處使用Pt1Pt2進行計算。

一旦中心的計算,我想用它來創建弧的邊界(DrawArc(Pens.Black, CInt(Center.X - Radius), CInt(Center.Y - Radius), CInt(Radius * 2), CInt(Radius * 2), ?, ?))和由點Pt1Pt2沿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)) 
+0

我不明白用戶如何「挑選」通過點擊PT3半徑的中心。我假設你想把pt1-pt2作爲一個圓的和絃。 – Fruitbat

+0

我猜半徑是錯誤的詞,但是。在點1,2和5之間應該有3個和絃。點5被構造爲Pt3到線Pt1-Pt2的Pt4的垂直線上的投影。因此,當用戶將鼠標移動到Pt3時,Pt5沿着垂直線Pt4-Pt5移動。對困惑感到抱歉。 – tbm0115

回答

0

所以我們都在同一頁上,該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,Angle。也就是說,第一次點擊定義了中心,第二次點擊決定了開始點,第三次點擊決定了弧線跨越的角度。這很容易適應其他方法,但我使用的是最簡單的數學解釋。

在第一次點擊時,我們將鼠標位置記錄到變量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。)

我們現在也有足夠的信息n來計算弧的邊界矩形。由於弧是圓形的,我們的包廂裏只是

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,startend變量都是PointF,您需要根據需要進行轉換。我刪除了轉換和捕獲邏輯,以使上面的代碼更容易遵循。
  • 所有其他變量是Single。您需要將Atan函數的結果轉換爲Single
  • 如果Δ x在計算反正切時爲0,則可以通過比較y值來判斷角度是正值還是負值。如果(center.Y < start.Y)那麼你知道它是+ π/2。 - 你也應該檢查0半徑。
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)) 
0

我用了大量的資源來完成我的目標。最後,由於this的問題,我最終放棄使用DrawArc作爲ExcludeClip。基本上,一旦我創建了正確的橢圓,我需要剪切點1和2之間的橢圓區域。下面,你會看到我的解決方案。第一張圖片顯示了我用來驗證我的數學的「幫手」線是否正確,其次是最終結果。

圖例:

*黃色線路1:垂直於PT1-P t2中線在中點(Pt和-PT5)

*黃色線路2:垂直線到第一黃色(平行於PT1-P t2中線)(PT3 -Pt5)

* PT5:黃色線

之間交點

*紅線:PT1-PT5

*藍線:P t2中-PT5

*粉紅線:垂直線,以紅/藍在中點(PT10,PT11)

*中心:交點粉色線之間(CNTR)

*米色線:紅的擴展/藍線在ExcludeClip使用{(PT20-PT1-PT5)和(PT21-P t2中-PT5)}

With Helper Lines This is what the user will see

這裏是我的代碼:

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點在圓周上