|
| 1 | +""" |
| 2 | +Temporary implementation of points_in_polygon() |
| 3 | +to troubleshoot discrepancies in latest Mac OS |
| 4 | +versions where some test events are incorrectly |
| 5 | +labelled. This will be removed after a solution |
| 6 | +is found but will remain in the repo in the |
| 7 | +'mac_num_op_testing' branch in case the issue |
| 8 | +occurs again in the future. |
| 9 | +""" |
| 10 | + |
| 11 | +def point_is_left( |
| 12 | + point_a_x, |
| 13 | + point_a_y, |
| 14 | + point_b_x, |
| 15 | + point_b_y, |
| 16 | + test_point_x, |
| 17 | + test_point_y |
| 18 | +): |
| 19 | + is_left = (point_b_x - point_a_x) * (test_point_y - point_a_y) - \ |
| 20 | + (test_point_x - point_a_x) * (point_b_y - point_a_y) |
| 21 | + return is_left |
| 22 | + |
| 23 | + |
| 24 | +def calc_wind_count(point_x, point_y, vert_count, poly_vertices): |
| 25 | + wind_count = 0 |
| 26 | + |
| 27 | + # loop through all edges of the polygon |
| 28 | + for i, (vert_a_x, vert_a_y) in enumerate(poly_vertices): |
| 29 | + if i >= vert_count - 1: |
| 30 | + vert_b_x = poly_vertices[0][0] |
| 31 | + vert_b_y = poly_vertices[0][1] |
| 32 | + else: |
| 33 | + vert_b_x = poly_vertices[i + 1][0] |
| 34 | + vert_b_y = poly_vertices[i + 1][1] |
| 35 | + |
| 36 | + if vert_a_y <= point_y: |
| 37 | + if point_y < vert_b_y: |
| 38 | + # point crosses & edge travels upward |
| 39 | + is_left = point_is_left(vert_a_x, vert_a_y, vert_b_x, vert_b_y, point_x, point_y) |
| 40 | + # print("if is_left: %.14f" % is_left) |
| 41 | + |
| 42 | + if is_left > 0: |
| 43 | + # point is left of edge |
| 44 | + wind_count += 1 # valid 'up' intersection |
| 45 | + else: |
| 46 | + if vert_b_y <= point_y: |
| 47 | + # point crosses & edge travels downward |
| 48 | + is_left = point_is_left(vert_a_x, vert_a_y, vert_b_x, vert_b_y, point_x, point_y) |
| 49 | + # print("vert_a_x: %.14f" % vert_a_x) |
| 50 | + # print("vert_a_y: %.14f" % vert_a_y) |
| 51 | + # print("vert_b_x: %.14f" % vert_b_x) |
| 52 | + # print("vert_b_y: %.14f" % vert_b_y) |
| 53 | + # print("point_x: %.14f" % point_x) |
| 54 | + # print("point_y: %.14f" % point_y) |
| 55 | + # print("else is_left: %.14f" % is_left) |
| 56 | + |
| 57 | + if is_left < 0: |
| 58 | + # point is right of edge |
| 59 | + wind_count -= 1 # valid 'down' intersect |
| 60 | + |
| 61 | + return wind_count |
| 62 | + |
| 63 | + |
| 64 | +def points_in_polygon(poly_vertices, points): |
| 65 | + """ |
| 66 | + Determines whether points in an array are inside a polygon. Points on the |
| 67 | + edge of the polygon are considered inclusive. This function uses the |
| 68 | + winding number method and is robust to complex polygons with crossing |
| 69 | + boundaries, including the presence of 'holes' created by boundary crosses. |
| 70 | +
|
| 71 | + This implementation is based on the C implementation by Dan Sunday. |
| 72 | + Original copyright notice: |
| 73 | + Copyright 2000 softSurfer, 2012 Dan Sunday |
| 74 | +
|
| 75 | + The website containing the above implementation is no longer available, |
| 76 | + but was archived by the Wayback Machine. The last archived version is |
| 77 | + available here: |
| 78 | +
|
| 79 | + https://web.archive.org/web/20210504233957/ |
| 80 | +
|
| 81 | + :param poly_vertices: Polygon vertices (array of 2-D points) |
| 82 | + :param points: Points to test for polygon inclusion |
| 83 | + :return: Array of winding counts for each point. True is inside polygon. |
| 84 | + """ |
| 85 | + # First, find the polygon's bounding box & store the min/max values |
| 86 | + min_x = poly_vertices[0][0] |
| 87 | + max_x = poly_vertices[0][0] |
| 88 | + min_y = poly_vertices[0][1] |
| 89 | + max_y = poly_vertices[0][1] |
| 90 | + |
| 91 | + for i, (vert_x, vert_y) in enumerate(poly_vertices): |
| 92 | + if vert_x < min_x: |
| 93 | + min_x = vert_x |
| 94 | + elif vert_x > max_x: |
| 95 | + max_x = vert_x |
| 96 | + |
| 97 | + if vert_y < min_y: |
| 98 | + min_y = vert_y |
| 99 | + elif vert_y > max_y: |
| 100 | + max_y = vert_y |
| 101 | + |
| 102 | + wind_counts = [] |
| 103 | + |
| 104 | + for i, (point_x, point_y) in enumerate(points): |
| 105 | + |
| 106 | + if point_x < min_x or point_x > max_x or point_y < min_y or point_y > max_y: |
| 107 | + wind_count = 0 |
| 108 | + else: |
| 109 | + wind_count = calc_wind_count( |
| 110 | + point_x, |
| 111 | + point_y, |
| 112 | + len(poly_vertices), |
| 113 | + poly_vertices |
| 114 | + ) |
| 115 | + |
| 116 | + wind_counts.append(wind_count) |
| 117 | + |
| 118 | + return wind_counts |
0 commit comments