2012-05-10 32 views
7

Estoy haciendo un control personalizado en Delphi (heredado de TCustomControl) que consiste en una serie de elementos de la lista de polígonos (formas irregulares). Necesito implementar eventos de mouse por artículo, pero primero debo ser capaz de detectar si la posición del mouse está dentro de un polígono determinado (array of TPoint). Estoy capturando el mensaje Prueba de aciertos (WM_NCHITTEST) y aquí es donde tendré que hacer esta validación. Tengo varios polígonos, haré un bucle en cada elemento del polígono y realizaré esta comprobación para ver si la posición X/Y del mouse está dentro de este polígono.¿Identifica si un punto está dentro de un polígono?

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

Solo para señalar, me falta una línea de código 'P: = ScreenToClient (P);' justo después de asignar 'P.X' y' P.Y'. Esto convierte esos puntos de ser relativos a la pantalla a ser relativos al control. –

+0

Por supuesto, podría ser tan fácil como 'P: = ScreenToClient (Point (Message.XPos, Message.YPos));' (convertir 3 líneas de código en uno) –

Respuesta

15

Puede utilizar 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

Esta fue mi primera idea también. Supongo que la sobrecarga de crear una región GDI no es tan mala (?). –

+4

@Andreas No me imagino que la sobrecarga sea mala. La región GDI debe ser muy liviana. Si fue un problema, puede almacenar en caché las regiones junto a los polígonos. –

+0

¡Excelente! No tendré mucho problema con los gastos generales porque no espero que este control tenga más de 20 elementos de lista (que ya es un gran número para este control). –

1

Verificando si el punto está dentro de un polígono se puede hacer imaginando una línea horizontal a través de ese punto, luego de izquierda a derecha contando cuántas veces esta línea imaginada cruza un polígono. Si el número de cruces de polígonos antes de golpear un punto es impar, entonces el punto está dentro, incluso si el punto está fuera de un polígono.

0

Hay otra técnica que utilizamos ampliamente, que no implica ninguna matemática en absoluto y puede manejar controles integrados extremadamente complejos de cualquier forma. Simplemente tenga una imagen fuera de pantalla del control con todas las partes codificadas por colores (como se muestra en la imagen a continuación) en las que el usuario podría hacer clic.

Al mover el mouse, simplemente observe el color del píxel debajo del mouse en nuestra imagen fuera de pantalla y eso nos dice exactamente en qué botón/control están, blanco para no sobre él y cualquier serie de colores para las diversas partes.

Color mask

// pseudo-código

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: La imagen de la máscara de color adjunta representa cinco controles circulares idénticas divididas en cuadrantes con un botón central (y ya que todos usan los mismos colores tenemos constantes para cada color y determinamos cuál de los cinco es el mouse por una simple XPosition) junto con un control irregular adicional a su derecha y un conjunto o botones rectangulares debajo.

Cuestiones relacionadas