Problem
I recently found a need to determine if my points are inside of a polygon. So I learned about this approach in C++ and adapted it to python. However, the C++ code I was studying isn't quite right I think? I believe I have fixed it, but I am not quite sure so I was hoping folks brighter than me might help me caste some light on this?
The theorem is super simple and the idea is like this, given an nth closed polygon you draw an arbitrary line, if your point is inside, you line will intersect with the edges an odd number of times. Otherwise, you will be even and it is outside the polygon. Pretty freaking cool.
I had the following test cases:
polygon_x = [5, 5, 11, 10]
polygon_y = [5, 10, 5, 10]
test1_x = 6
test1_y = 6
result1 = point_in_polygon(test1_x, test1_y, polygon_x, polygon_y)
print(result1)
test2_x = 13
test2_y = 5
result2 = point_in_polygon(test2_x, test2_y, polygon_x, polygon_y)
print(result2)
The above would give me both false if I defined it as follows:
if polygon_x[i] < polygon_x[(i+1) % length]:
temp_x = polygon_x[i]
temp_y = polygon_x[(i+1) % length]
else:
temp_x = polygon_x[(i+1) % length]
temp_y = polygon_x[i]
This is wrong! I should be getting true
for result1
and then false
for result2
. So clearly, something is funky.
The code I was reading in C++ makes sense except for the above. In addition, it failed my test case which made me think that temp_y
should be defined with polygon_y
and not polygon_x
. Sure enough, when I did this, my test case for (6,6)
passes. It still fails when my points are on the line, but as long as I am inside the polygon, it will pass. Expected behavior.
Polygon code adopted to python
def point_in_polygon(self, target_x, target_y, polygon_x, polygon_y):
print(polygon_x)
print(polygon_y)
#Variable to track how many times ray crosses a line segment
crossings = 0
temp_x = 0
temp_y = 0
length = len(polygon_x)
for i in range(0,length):
if polygon_x[i] < polygon_x[(i+1) % length]:
temp_x = polygon_x[i]
temp_y = polygon_y[(i+1) % length]
else:
temp_x = polygon_x[(i+1) % length]
temp_y = polygon_y[i]
print(str(temp_x) + ", " + str(temp_y))
#check
if target_x > temp_x and target_x <= temp_y and (target_y < polygon_y[i] or target_y <= polygon_y[(i+1)%length]):
eps = 0.000001
dx = polygon_x[(i+1) % length] - polygon_x[i]
dy = polygon_y[(i+1) % length] - polygon_y[i]
k = 0
if abs(dx) < eps:
k = 999999999999999999999999999
else:
k = dy/dx
m = polygon_y[i] - k * polygon_x[i]
y2 = k*target_x + m
if target_y <= y2:
crossings += 1
print(crossings)
if crossings % 2 == 1:
return True
else:
return False
Summary
Can someone please explain to me what the temp_x
and temp_y
approaches are doing? Also, if my fix for redefining the temp_x
for polygon_x
and temp_y
for polygon_y
is the correct approach? I doubt it. Here is why.
What is going on for temp_x
and temp_y
doesn't quite make sense to me. For i = 0
, clearly polygon_x[0] < polygon_x[1]
is false
, so we get temp_x[1] = 5
and temp_y[0] = 5
. That is (5,5)
. This just happens to be one of my pairs. However, suppose I feed the algorithm my points out of order (by axis, pairwise integrity is always a must), something like:
x = [5, 10, 10, 5]
y = [10,10, 5, 5]
In this case, when i = 0
, we get temp_x[1] = 10
and temp_y[0] = 10
. Okay, by coincidence (10,10)
. I also tested points against the "corrected" algorithm (9,9)
and it is still inside. In short, I am trying to find a counterexample, for why my fix won't work, but I can't. If this is working, I need to understand what the method is doing and hope someone could help explain it to me?
Regardless, if I am right or wrong, I would appreciate it if someone could help shed some better light on this problem. I'm even open to solving the problem in a more efficient way for n-polygons, but I want to make sure I am understanding code correctly. As a coder, I am uncomfortable with a method that doesn't quite make sense.
Thank you so much for listening to my thoughts above. Any suggestions greatly welcomed.