Identify if a point is within a polygon?

I'm making a custom control in Delphi (inherited from TCustomControl) which consists of a number of polygon list items (irregular shapes). I need to implement mouse events per item, but first I need to be able to detect if the mouse position is within a given polygon (array of TPoint). I am catching the Hit Test message (WM_NCHITTEST) and this is where I will need to do this validation. I have a number of polygons, I will do a loop through each polygon item and perform this check to see if the mouse's X/Y position is within this polygon.

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;

Answers


You can use 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;

You can use the ray casting algorithm found here: http://en.wikipedia.org/wiki/Point_in_polygon#Ray_casting_algorithm

Most computer graphics classes use this as an example.


Checking if point is inside of a polygon can be done by imagining a horizontal line through that point, then from left to right counting how many times this imagined line crosses a polygon. If number of polygon crosses before hitting a point is odd then point is inside, if even then point is outside of a polygon.


There is another technique that we use extensively, which doesn't involve any math at all and can handle extremely complex embedded controls of any shape at all. Simply have an off-screen image of the control with all the parts color-coded (as shown in the image below) that the user could click.

As they move their mouse, simply look at the color of the pixel underneath the mouse in our off-screen image and that tells us exactly what button/control they are over -- white for not over it, and any series of colors for the various parts.

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;

NOTE: The attached Color Mask image represents five identical circular controls broken up into quadrants with a center button (and since they all use the same colors we have constants for each color and we determine which one of the five the mouse is over by a simple XPosition) along with an additional irregular control to their right and a set or rectangular buttons beneath.


Need Your Help

FFMpeg image sequence to video using VP9 encoder

encoding ffmpeg mp4 webm lossless

I'm trying to transcode mp4 to webm format in a lossless way. I tried two ways to do that.

C char array getting corrupted after being passed into function

c arrays pointers tree char

I'm trying to implement a program similar to 20 Questions, in which a text file of the questions and guesses for answers are loaded, copied into a char array (where the new space lines are replaced...