38

I have a convex polygon (typically just a rotated square), and I know all of 4 points. How do I determine if a given point (yellow/green) is inside the polygon?

enter image description here

EDIT: For this particular project, I don't have access to all of the libraries of the JDK, such as AWT.

NPike
  • 13,136
  • 12
  • 63
  • 80
  • 1
    Did you mean "convex" in the title? – Kavka Jan 04 '12 at 02:46
  • You can use the Polygon and Point in the `java.awt` library: `new Polygon(x_coordinates, y_coordinates, coordinates.length).contains(new Point(x, y))` where `x_coordinates` and `y_coordinates` are of type `Array[Integer]` – Seraf Aug 31 '18 at 13:01

9 Answers9

98

This page: http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html shows how to do this for any polygon.

I have a Java implementation of this, but it is too big to post here in its entirety. However, you should be able to work it out:

class Boundary {
    private final Point[] points; // Points making up the boundary
    ...


    /**
     * Return true if the given point is contained inside the boundary.
     * See: http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
     * @param test The point to check
     * @return true if the point is inside the boundary, false otherwise
     *
     */
    public boolean contains(Point test) {
      int i;
      int j;
      boolean result = false;
      for (i = 0, j = points.length - 1; i < points.length; j = i++) {
        if ((points[i].y > test.y) != (points[j].y > test.y) &&
            (test.x < (points[j].x - points[i].x) * (test.y - points[i].y) / (points[j].y-points[i].y) + points[i].x)) {
          result = !result;
         }
      }
      return result;
    }
}

And here is a sketch of the Point class

/**
 * Two dimensional cartesian point.
 */
public class Point {
  public final double x;
  public final double y;
  ...
}
Carl Walsh
  • 6,100
  • 2
  • 46
  • 50
Dean Povey
  • 9,256
  • 1
  • 41
  • 52
  • Can you explain the `(points[i].y > test.y) != (points[j].y > test.y)` part? – Seraf Aug 30 '18 at 15:04
  • 1
    It's making sure points[i].y and points[j].y are not on the same side of test.y – Dean Povey Sep 01 '18 at 04:54
  • Looks like this works for both convex and concave polygons? The OP is asking for convex polygons only, which should yield a faster algorithm. – Rodrigo May 18 '20 at 03:57
  • Yes, you are right. For a convex polygon you can compute the orientation of the point with each of the sides in anticlockwise order. It’s inside if the orientation is on the left for all of the sides (the orientation is positive/clockwise). Orientation can be computed according to: (y2 - y1)*(x3 - x2) - (y3 - y2)*(x2 - x1). If 0 the points are colinear, if positive then points are oriented clockwise, if negative counterclockwise. – Dean Povey May 20 '20 at 22:35
  • do the points have to be in order in array? – jpell Feb 12 '21 at 14:40
59

For those who would like to understand how the method written by Dean Povey above works, here is the explanation:

The method looks at a "ray" that starts at the tested spot and extends to infinity to the right side of the X axis. For each polygon segment, it checks if the ray crosses it. If the total number of segment crossing is odd then the tested point is considered inside the polygon, otherwise - it is outside.

To understand the way the crossing is calculated, consider the following figure:

            v2
            o
           /
          / c (intersection)
o--------x----------------------> to infinity
t       /
       /   
      /
     o
     v1

For the intersection to occur, tested.y must be between the y values of the segment's vertices (v1 and v2). This is the first condition of the if statement in the method. If this is what happens, then the horizontal line must intersect the segment. It only remains to establish whether the intersection happens to the right of the tested point or to the left of it. This requires finding the x coordinate of the intersection point, which is:

              t.y - v1.y
c.x = v1.x + ----------- * (v2.x - v1.x)
             v2.y - v1.y

All that remains to be done is examining the subtleties:

  • If v1.y == v2.y then the ray goes along the segment and therefore the segment has no influence on the outcome. Indeed, the first part of the if statement returns false in that case.
  • The code first multiplies and only then divides. This is done to support very small differences between v1.x and v2.x, which might lead to a zero after the division, due to rounding.
  • Finally, the problem of crossing exactly on a vertex should be addressed. Consider the following two cases:
         o                    o
         |                     \     o
         | A1                C1 \   /
         |                       \ / C2  
o--------x-----------x------------x--------> to infinity
        /           / \
    A2 /        B1 /   \ B2
      /           /     \ 
     o           /       o
                o

Now, to verify if it works, check for yourself what is returned for each of the 4 segments by the if condition in the method body. You should find that the segments above the ray (A1, C1, C2) receive a positive result, while those below it (A2, B1, B2) receive a negative one. This means that the A vertex contributes an odd number (1) to the crossing count, while B and C contribute an even number (0 and 2, respectively), which is exactly what is desired. A is indeed a real crossing of the polygon, while B and C are just two cases of "fly-by".

sielnix
  • 73
  • 1
  • 4
Nadav
  • 1,023
  • 9
  • 10
18

Assuming that your point is at Y coordinate y, simply calculate the x positions where each of the polygon's (non-horizontal) lines cross y. Count the number of x positions that are less than the x position of your point. If the number of x positions is odd, your point is inside the polygon. Note: this works for all polygons, not just convex. Think of it this way: draw a line from infinitely far away straight in to your point. When that line crosses a polygon line, it is now inside the polygon. Cross the line again, outside. Cross again, inside (and so forth). Hope this helps!

Jim
  • 641
  • 4
  • 9
10

The java.awt.Polygon class has a number of contains(...) methods if you use Polygon objects to represent your polygon.

trashgod
  • 203,806
  • 29
  • 246
  • 1,045
Kavka
  • 4,191
  • 16
  • 33
5

Just to add a (simple) Java implementation of the original code in C from code suggested by @Dean Povey (I don't know why @Dean Povey is referring to a large implementation):

static boolean pnpoly(double[] vertx, double[] verty, double testx, double testy)
{
    int nvert = vertx.length;
    int i, j;
    boolean c = false;
    for (i = 0, j = nvert-1; i < nvert; j = i++) {
        if ( ((verty[i]>testy) != (verty[j]>testy)) &&
                (testx < (vertx[j]-vertx[i]) * (testy-verty[i]) / (verty[j]-verty[i]) + vertx[i]) )
            c = !c;
    }
    return c;
}   

I did not change the case to comply with Java rule to show the minimal changes required. I have also tested it in simple cases and it works fine.

Aguinore
  • 91
  • 11
Eypros
  • 5,370
  • 6
  • 42
  • 75
1

Say, x[] is the array of x points and y[] is the array of y points.
You are meant to return 1 if the point exists in the polygon and 2 if not. where (planeX,planeY) is the point you need to check.

//check like this
return new Polygon(x,y,x.length).contains(planeX, planeY)?1:2;
PMerlet
  • 2,568
  • 4
  • 23
  • 39
Apoorva Ambhoj
  • 164
  • 1
  • 5
1

Check to see if it's on the same side of the 4 half-planes defined by the lines that contain the line segments that make up the sides of the quad.

Here is a good explanation.

user1118321
  • 25,567
  • 4
  • 55
  • 86
0

Polygon's abscissas x_array: Array[Integer]

Polygon's ordinates: y_array: Array[Integer]

Point: x: Integer, y: Integer

import java.awt.Polygon
import java.awt.Point
...

final boolean isInPolygon = 
    new Polygon(x_array,y_array,x_array.length).contains(new Point(x, y));

In this example we create an object java.awt.Polygon and use the method contains to check if your coordinates are in the shape you designed.

I use the object java.awt.Point to represent the coordinates to make the code elegant but that is optional, you can directly use .contains(x, y)

Seraf
  • 850
  • 1
  • 17
  • 34
0

Many answers are based on intersections. Here is a simple algorithm not based on intersections but only using vector products. This method is less complex that methods based on intersections, but only works for convex polygons. Since the question is about convex polygons, this less complex method should be preferred.

Imagine that your convex polygon (P0, P1, ..., Pn), with (Pi-1, Pi) being its segments, is a needle clock and that the point C you want to check is inside the clock. The points (Pi) will either turn clockwise, or counterclockwise. Imagine a needle attached to C, that gives hours when turning clockwise. The vectors CP0→, CP1→, CP2→, CP...→, CPn→ all turn either clockwise, or counterclockwise. This means that the vector products CP0→⨯CP1→, CP1→⨯CP2→, CP2→⨯CP...→, CPn-1→⨯CPn→ and CPn→⨯CP0→ all have the same orientation. Since they are also colinear, their orientation is defined by the sign of their scalar product taken two by two. This property only occurs when C is inside the polygon. Therefore, iif the signs of the scalar products of the vector products are constant, C is inside the polygon.

Alexandre Fenyo
  • 4,526
  • 1
  • 17
  • 24