I changed the original code to make it a little more readable (also this uses Eigen). The algorithm is identical.
// This uses the ray-casting algorithm to decide whether the point is inside
// the given polygon. See https://en.wikipedia.org/wiki/Point_in_polygon#Ray_casting_algorithm
bool pnpoly(const Eigen::MatrixX2d &poly, float x, float y)
{
// If we never cross any lines we're inside.
bool inside = false;
// Loop through all the edges.
for (int i = 0; i < poly.rows(); ++i)
{
// i is the index of the first vertex, j is the next one.
// The original code uses a too-clever trick for this.
int j = (i + 1) % poly.rows();
// The vertices of the edge we are checking.
double xp0 = poly(i, 0);
double yp0 = poly(i, 1);
double xp1 = poly(j, 0);
double yp1 = poly(j, 1);
// Check whether the edge intersects a line from (-inf,y) to (x,y).
// First check if the line crosses the horizontal line at y in either direction.
if ((yp0 <= y) && (yp1 > y) || (yp1 <= y) && (yp0 > y))
{
// If so, get the point where it crosses that line. This is a simple solution
// to a linear equation. Note that we can't get a division by zero here -
// if yp1 == yp0 then the above if will be false.
double cross = (xp1 - xp0) * (y - yp0) / (yp1 - yp0) + xp0;
// Finally check if it crosses to the left of our test point. You could equally
// do right and it should give the same result.
if (cross < x)
inside = !inside;
}
}
return inside;
}
To expand on the "too-clever trick". We want to iterate over all adjacent vertices, like this (imagine there are 4 vertices):
My code above does it the simple obvious way - j = (i + 1) % num_vertices
. However this uses integer division which is much much slower than all other operations. So if this is performance critical (e.g. in an AAA game) you want to avoid it.
The original code changes the order of iteration a bit:
This is still totally valid since we're still iterating over every vertex pair and it doesn't really matter whether you go clockwise or anticlockwise, or where you start. However now it lets us avoid the integer division. In easy-to-understand form:
int i = 0;
int j = num_vertices - 1; // 3
while (i < num_vertices) { // 4
{body}
j = i;
++i;
}
Or in very terse C style:
for (int i = 0, j = num_vertices - 1; i < num_vertices; j = i++) {
{body}
}