2011-10-11 101 views
1

在分析我的GDI +項目時,我發現以下IsLineVisible函數是在我的自定義面板上繪製和移動對象時「最熱」的函數之一。優化GDI +功能的性能

有沒有可能優化它?

Private Function IsLineVisible(ByVal detectorRectangle As Rectangle, 
           ByVal pen As Pen, 
           ByVal ParamArray points() As Point) As Boolean 
    Using path As New GraphicsPath() 
     path.AddLines(points) 
     Return IsPathVisible(detectorRectangle, path, pen) 
    End Using 
    End Function 

    ' Helper functions ''''''''''''''''''''''''''''''''''''' 
    Private Function IsPathVisible(ByVal detectorRectangle As Rectangle, 
           ByVal path As GraphicsPath, 
           ByVal pen As Pen) As Boolean 
    If Not path.IsPoint Then 
     path.Widen(pen) 
    End If 
    Return IsPathVisible(detectorRectangle, path) 
    End Function 


    Private Function IsPathVisible(ByVal detectorRectangle As Rectangle, 
           ByVal path As GraphicsPath) As Boolean 
    Using r As New Region(path) 
     If r.IsVisible(detectorRectangle) Then 
     Return True 
     Else 
     Return False 
     End If 
    End Using 
    End Function 

enter image description here

回答

5

更新2:

public bool AreLinesVisible(Point[] p, int width, Rectangle rect) 
    { 
     for (var i = 1; i < p.Length; i++) 
      if (IsLineVisible(p[i - 1], p[i], width, rect)) 
       return true; 
     return false; 
    } 

修訂包括厚度/寬度。

這是完全未經測試的代碼,但它應該給你基本思路對於沒有昂貴的framwork一個超快速的解決方案要求:

public bool IsLineVisible(Point p1, Point p2, int width, Rectangle rect) 
{ 
    var a = Math.Atan2(p1.Y - p2.Y, p1.X - p2.X) + Math.PI/2; 
    var whalf = (width + 1)*0.5; 
    var dx = (int) Math.Round(whalf*Math.Sin(a)); 
    var dy = (int) Math.Round(whalf*Math.Cos(a)); 
    return IsLineVisible(new Point(p1.X - dx, p1.Y - dy), new Point(p2.X - dx, p2.Y - dy), rect) 
     || IsLineVisible(new Point(p1.X + dx, p1.Y + dy), new Point(p2.X + dx, p2.Y + dy), rect); 
} 

public bool IsLineVisible(Point p1, Point p2, Rectangle rect) 
{ 
    if (p1.X > p2.X) // make sure p1 is the leftmost point 
     return IsLineVisible(p2, p1, rect); 

    if (rect.Contains(p1) || rect.Contains(p2)) 
     return true; // one or both end-points within the rect -> line is visible 

    //if both points are simultaneously left or right or above or below -> line is NOT visible 
    if (p1.X < rect.X && p2.X < rect.X) 
     return false; 
    if (p1.X >= rect.Right && p2.X >= rect.Right) 
     return false; 
    if (p1.Y < rect.Y && p2.Y < rect.Y) 
     return false; 
    if (p1.Y >= rect.Bottom && p2.Y >= rect.Bottom) 
     return false; 

    // now recursivley break down the line in two part and see what happens 
    // (this is an approximation...) 
    var pMiddle = new Point((p1.X + p2.X)/2, (p1.Y + p2.Y)/2); 
    return IsLineVisible(p1, new Point(pMiddle.X - 1, pMiddle.Y), rect) 
      || IsLineVisible(new Point(pMiddle.X + 1, pMiddle.Y), p2, rect); 
} 
+0

謝謝。但是你「忘記」了筆的寬度**(厚度)。 – serhio

+0

是的,但我說的基本思想 - 你只需要計算兩條輪廓線,組成你的粗線並通過它們。如果其中一個是可見的 - 那麼你的粗線是可見的! –

+0

這條線也可以有一個樣式:虛線,虛線... – serhio

0

我能看到的唯一的事情是可能使用更寬/更厚Pen

這樣可以減少方法,減少對Widen的調用,而不會失去太多的效果(我希望在最後一個)。

+0

我使用其他筆,我通過作爲參數...這是所有的重點。我有一些寬線,其他粗線,所以我應該檢測不同類型的線... – serhio

0

而不是創建一個路徑,這是一個非常昂貴的GDI構造,如何循環你的點,將該點與前一點連接,並檢查該線是否與矩形相交?

它應該在計算上花費較少,並且能夠停止第一個段上的循環與矩形相交。

這篇文章應該有助於交集測試。 How to find the intersection point between a line and a rectangle?

+0

這個想法是筆,可以是不同的。不只是一行像素的故事。 – serhio

0

有沒有必要建立一個區域; GraphicsPath.IsVisible可以用來代替。我會擴大GraphicsPath並將其緩存以供每個需要命中測試的對象重用。