2012-03-02 75 views
4

我正在研究一個項目,其中有幾個矩形,我想爲每個矩形都設置一個懸停效果。現在我知道我可以捕獲消息WM_MOUSEMOVE,並遍歷每個矩形。但是如果我有很多矩形(如果50很多)。
我可能是錯的,但不會迭代那麼多,並且每次鼠標移動都會減慢應用程序的運行速度,就會對每一個進行測試。命中測試矩形

然後我就開始琢磨一個操作系統(如Windows),如何做到這一點,有沒有像100+我的屏幕上的東西,現在,所有的都有某種動畫,當我將鼠標懸停在他們。我不認爲每次鼠標移動一個像素時,Windows都會遍歷它們。

基本上:
1.如果我有大約50個長方形,並考慮到性能,我怎樣才能確定鼠標的哪個矩形已經結束。
2. Windows如何做到這一點? (我對任何事情都比較好奇,但是如果不是很複雜,也許我可以在自己的程序中實現類似的東西?)

哦,它們都是矩形,它們不會被旋轉或任何東西。

+0

使用PtInRect該函數。如果存在效率問題,請進行配置並進行更改。 – 2012-03-02 15:53:00

回答

8

,直到它變得清晰,一段代碼創建了一個真正的瓶頸,我不會被人打擾太多關於性能。讓我們假設你有這樣的瓶頸,並測量以下代碼的性能(這是在C#中,但我敢肯定,C++不會慢):

public class Rectangle 
{ 
    public int X { get; set; } 
    public int Y { get; set; } 
    public int W { get; set; } 
    public int H { get; set; } 

    public bool HitTest(int x, int y) 
    { 
     return x >= X && x < X + W && y >= Y && y < Y + H ? true : false; 
    } 
} 

我們有興趣性能的HitTest()方法,所以讓我們來衡量它!

void PerformanceTest() 
{ 
    const int Iterations = 1000000; 
    Random rnd = new Random(); 
    var rectangles = Enumerable.Range(1, 50).Select(
      r => new Rectangle { 
       X = rnd.Next(1000), 
       Y = rnd.Next(1000), 
       W = rnd.Next(1000), 
       H = rnd.Next(1000)}).ToList(); 

    Stopwatch sw = new Stopwatch(); 
    sw.Start(); 
    for (int i = 0; i < Iterations; i++) 
    { 
     rectangles.ForEach(r => r.HitTest(500, 500)); 
    } 
    sw.Stop(); 

    Console.WriteLine("Elapsed time: {0}ms. ({1}us per one iteration)", 
     sw.ElapsedMilliseconds, 
     (float)sw.ElapsedMilliseconds * 1000/Iterations); 
} 

在我的電腦上面的代碼打印:

執行時間:701ms。 (每一次迭代0.701us)

正如你所看到的,它需要不到一微秒的命中測試50米的矩形。你真的覺得這個時間太長了嗎?與創建幻想懸停效果的時間相比,你的程序還有其他什麼?當然,只有你可以回答這個問題。

但我的故事的寓意是:不要試圖預先優化,不花時間試圖解決它可能不存在的一個問題。

2

不要考慮性能。 如果你這樣做,然後衡量它!

鼠標事件是非常低級別的事件,它的快速,真的。 Windows將鼠標消息放入隊列中,並且您的應用程序讀取或忽略它們。在您的鼠標事件處理程序中,檢查哪個矩形是鼠標是一種快速操作。

如果你的「矩形」是Windows控件(他們應該),那麼你可以設置一個鼠標監聽器爲每個控件,以便正確的處理程序將被Windows自動被調用。

0

我同意對於少數的矩形(例如,50)依次每一個測試的明顯的方法很可能是最快的。

我猜想Windows的功能差不多。顯然,它不必測試子窗口,除非鼠標指針在父窗口中,甚至最糟糕的設計對話框也很少有一百個控件可以同時顯示。有很多命中測試區域的控件(例如ListViews,網格)優化他們自己的命中測試。

如果您有數萬個矩形,那麼性能可能會成爲問題,您可以使用the methods described here之一。

0

其他問題,這裏沒有回答你的第2部分,所以我給一個鏡頭:

2.如何窗戶做到這一點? (我對任何事情都比較好奇,但如果不是很複雜,也許我可以在自己的程序中實現類似的東西)

要實現的是即使你打開幾十個窗口,許多工具欄,每個工具欄都有很多項目,等等,每次移動鼠標時,窗口都不需要檢查的所有內容

Windows基本上分爲兩層:HWNDs,它是Windows本身如何管理桌面空間的細分;並且通常在每個HWND中都有一個控制器,用於管理該HWND中的自己的空間:管理自己的列表項的列表框,管理自己的選項卡的選項卡控件,管理自己的HTML頁面佈局的HTML控件等等。 (或者,在你的情況下,代碼管理50左右的矩形。)

當鼠標移動時,Windows首先確定將WM_MOUSEMOVE發送到的正確HWND。它通過遍歷HWND來完成。 HWND存儲爲一棵樹,表示包含,以及表示Z順序的兄弟之間的順序,因此Windows可以在此樹中執行簡單的深度優先下降,以找出任意給定點處最底層的HWND。如果你啓動Spy ++應用程序,你可以自己看看這個HWND樹是什麼樣的。請注意,Windows並未進行全面的遍歷遍歷:例如,在遍歷頂級應用程序窗口時,一旦Windows找到包含該點的第一個頂層HWND,就會找出該點在哪個應用程序中,將深入研究,徹底忽略所有其他下/後的應用程序 - 以及其中的所有控件。這是一個關鍵,這意味着即使屏幕上有許多許多可見的信息,windows也只需要遍歷相對較少的HWND。

一旦Windows確定了正確的HWND,它就會將相應的消息發送到該消息(WM_NCHITTEST,WM_MOUSEMOVE等),然後由該控件自行處理。對於包含固定尺寸項目的列表框,在特定點確定項目可能與分割操作一樣簡單;或者對於HTML控件,控件可以有自己的等價物,可以用它來遍歷並快速確定該點處的元素。在你的情況下,循環矩形列表可能會非常好。

這是一個稍微簡化的版本:它比上面稍微複雜 - 例如。窗口不僅僅是一個點的直接檢查,還有其他檢查可以用於奇形和透明的窗口(以及不可見和禁用的窗口);但基本樹下降的想法適用。

要記住的另一個重要問題是,所有這些都非常快:移動鼠標發生在「人類時間」,並且現代CPU可以在鼠標移動一對夫婦的時間內進行大量操作屏幕上的像素。最後,請注意,當您將鼠標從A點移動到屏幕上的B點時,鼠標並不總是遍歷它們之間的每個像素 - 尤其是當您快速移動鼠標時。

0

我相信你在答案中有一個完全不必要的'分支'(在你的測試中有一個額外的百萬次操作)。在你的「的HitTest」你結束與「?真:假」

return ... ? true : false; 

的是多餘的,因爲表達已經是「真」或「假」。當試圖製作超高效的代碼時,我總是想到'操作'執行...

PS:類似於++ var和var ++之類的東西可以對性能產生有價值的影響,具體取決於它在代碼中的使用方式(如優化趕上他們中的一些,併爲您解決問題...

PPS:而我在這......也從來沒有把一個表達式或方法調用你的循環,除非循環會改變表達結果,如:的for(int i = 0;我<的getWidth(); + +1)...如果這個循環一萬次,你的方法將被稱爲萬次:)