2012-05-10 16 views
7

Sto creando un controllo personalizzato in Delphi (ereditato da TCustomControl) che consiste in un numero di voci di un elenco di poligoni (forme irregolari). Devo implementare eventi del mouse per elemento, ma prima devo essere in grado di rilevare se la posizione del mouse si trova all'interno di un dato poligono (array of TPoint). Sto recuperando il messaggio Hit Test (WM_NCHITTEST) ed è qui che dovrò fare questa convalida. Ho un numero di poligoni, eseguirò un ciclo attraverso ogni oggetto poligonale ed eseguirò questo controllo per vedere se la posizione X/Y del mouse si trova all'interno di questo poligono.Identificare se un punto si trova all'interno di un poligono?

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

Giusto per precisare, mi manca una riga di codice 'P: = ScreenToClient (P);' subito dopo l'assegnazione di 'P.X' e' P.Y'. Ciò converte quei punti dall'essere relativi allo schermo rispetto al controllo. –

+0

Ovviamente potrebbe essere facile come 'P: = ScreenToClient (Point (Message.XPos, Message.YPos));' (trasforma 3 righe di codice in una sola) –

risposta

15

È possibile utilizzare PtInRegion:

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

Questa è stata anche la mia prima idea. Presumo che il sovraccarico di creazione di una regione GDI non sia troppo brutto (?). –

+4

@Andreas Non immagino che l'overhead sia male. La regione GDI dovrebbe essere molto leggera. Se si tratta di un problema, è possibile memorizzare nella cache le regioni accanto ai poligoni. –

+0

Eccellente! Non avrò molti problemi con l'overhead, perché non mi aspetto che questo controllo abbia più di 20 voci di elenco (che è già un grande numero per questo controllo). –

1

Controllare se il punto si trova all'interno di un poligono può essere fatto immaginando una linea orizzontale attraverso quel punto, quindi da sinistra a destra contando quante volte questa linea immaginata attraversa un poligono. Se il numero di poligoni incrocia prima di colpire un punto è dispari allora il punto è dentro, se anche allora il punto è fuori da un poligono.

0

C'è un'altra tecnica che utilizziamo estensivamente, che non comporta alcuna matematica e in grado di gestire controlli embedded estremamente complessi di qualsiasi forma. Basta avere un'immagine fuori schermo del controllo con tutte le parti codificate a colori (come mostrato nell'immagine sotto) che l'utente potrebbe fare clic.

Mentre muovono il mouse, basta guardare il colore del pixel sotto il mouse nella nostra immagine fuori schermo e questo ci dice esattamente quale pulsante/controllo sono troppo bianchi per non averlo sopra, e qualsiasi serie di colori per le varie parti.

Color mask

// Pseudo-codice

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; 

NOTA: L'immagine Maschera colore allegata rappresenta cinque controlli circolari identici spezzettati in quadranti con un tasto centrale (e poiché tutti usano gli stessi colori abbiamo costanti per ogni colore e determiniamo quale dei cinque il mouse è finito con una semplice XPposizione) insieme ad un ulteriore controllo irregolare alla loro destra e un pulsante o un pulsante rettangolare sotto.

Problemi correlati