2012-05-10 16 views
7

Ich mache ein benutzerdefiniertes Steuerelement in Delphi (geerbt von TCustomControl), die aus einer Anzahl von Polygonlistenelementen (unregelmäßige Formen) besteht. Ich muss Mausereignisse pro Element implementieren, aber zuerst muss ich erkennen können, ob die Mausposition innerhalb eines gegebenen Polygons liegt (array of TPoint). Ich erhalte die Nachricht zum Treffer-Test (WM_NCHITTEST) und hier muss ich diese Validierung durchführen. Ich habe eine Anzahl von Polygonen, ich mache eine Schleife durch jedes Polygon-Objekt und führe diese Prüfung durch, um zu sehen, ob sich die X/Y-Position der Maus innerhalb dieses Polygons befindet.Identifizieren, ob ein Punkt innerhalb eines Polygons liegt?

procedure TMyControl.WMNCHitTest(var Message: TWMNCHitTest); 
var 
    P: TPoint; //X/Y of Mouse 
    Poly: TPoints; //array of TPoint 
    X: Integer; //iterator 
    I: TMyListItem; //my custom list item 
begin 
    P.X:= Message.XPos; 
    P.Y:= Message.YPos; 
    for X := 0 to Items.Count - 1 do begin 
    I:= Items[X]; //acquire my custom list item by index 
    Poly:= I.Points; //acquire polygon points 

    //Check if Point (P) is within Polygon (Poly)...? 

    end; 
end; 
+0

Nur um darauf hinzuweisen, fehlt mir eine Zeile Code 'P: = ScreenToClient (P);' direkt nach dem Zuweisen von 'P.X' und' P.Y'. Dadurch werden diese Punkte relativ zum Bildschirm relativ zum Steuerelement konvertiert. –

+0

Natürlich könnte es so einfach sein wie 'P: = ScreenToClient (Point (Message.XPos, Message.YPos));' (3 Zeilen Code in einen verwandeln) –

Antwort

15

können Sie PtInRegion verwenden: http://en.wikipedia.org/wiki/Point_in_polygon#Ray_casting_algorithm diese

Die meisten Computer-Grafiken Klassen als Beispiel:

function PointInPolygon(Point: TPoint; const Polygon: array of TPoint): Boolean; 
var 
    rgn: HRGN; 
begin 
    rgn := CreatePolygonRgn(Polygon[0], Length(Polygon), WINDING); 
    Result := PtInRegion(rgn, Point.X, Point.Y); 
    DeleteObject(rgn); 
end; 
+0

Das war meine erste Idee. Ich nehme an, der Aufwand beim Erstellen einer GDI-Region ist nicht so schlecht (?). –

+4

@Andreas Ich kann mir nicht vorstellen, dass der Overhead schlecht ist. Die GDI-Region sollte sehr leicht sein. Wenn es ein Problem war, konnten Sie die Regionen neben den Polygonen zwischenspeichern. –

+0

Ausgezeichnet! Ich habe kein Problem mit Overhead, weil ich nicht erwarte, dass dieses Steuerelement mehr als 20 Listenelemente hat (was für dieses Steuerelement bereits eine große Zahl ist). –

5

Sie können die Ray-Casting-Algorithmus hier verwenden.

1

Ob der Punkt innerhalb eines Polygons liegt, kann man sich vorstellen, indem man sich eine horizontale Linie durch diesen Punkt vorstellt und dann von links nach rechts zählt, wie oft diese gedachte Linie ein Polygon kreuzt. Wenn die Anzahl der Polygonkreuze vor dem Auftreffen auf einen Punkt ungerade ist, dann ist der Punkt innerhalb, wenn der Punkt auch außerhalb eines Polygons liegt.

0

Es gibt eine andere Technik, die wir ausgiebig verwenden, die überhaupt keine Mathematik beinhaltet und extrem komplexe eingebettete Steuerelemente beliebiger Form verarbeiten kann. Halten Sie einfach ein Off-Screen-Bild des Steuerelements bereit, in dem alle Teile farbcodiert sind (wie in der Abbildung unten gezeigt), auf die der Benutzer klicken könnte. Wenn sie ihre Maus bewegen, sehen Sie einfach die Farbe des Pixels unter der Maus in unserem Off-Screen-Bild an und das sagt uns genau, welche Taste/Steuerung sie über-weiß sind, nicht darüber und jede Serie von Farben für die verschiedenen Teile.

Color mask

// Pseudo-Code

function MouseOverControl(LocalMousePos:TPoint):ControlID; 
begin 
    //sanity check 
    Result:=IDNull; 
    if (LocalMouse.X < 0) or (LocalMouse.X > ControlWidth) or 
     (LocalMouse.Y < 0) or (LocalMouse.Y > ControlHeight) then 
      exit; 
    case OffScreenControlMask.Canvas.Pixels[LocalMousePos.X,LocalMousePos.Y] of 
    clwhite:exit; 
    clRed:result:=ControlIDOne; 
    clGreen:result:=ControlIDTwo; 
    clBlue:result:=ControlIDThree; 
    ... etc 
    end; 
end; 

HINWEIS: Das beigefügte Color Mask Bild stellt fünf identische Kreis Kontrollen in Quadranten mit einer mittleren Taste aufgebrochen (und da sie alle die gleichen Farben wir haben Konstanten für jede Farbe und wir bestimmen, welche der fünf der Maus über eine einfache XPosition ist) zusammen mit einer zusätzlichen unregelmäßigen Kontrolle auf der rechten Seite und einem Satz oder rechteckigen Schaltflächen darunter.