add utils.vertices.poly_contains_points
This commit is contained in:
parent
fd0b2ba4cd
commit
97db83a1d5
@ -7,7 +7,9 @@ from .array import is_scalar
|
||||
from .autoslots import AutoSlots
|
||||
|
||||
from .bitwise import get_bit, set_bit
|
||||
from .vertices import remove_duplicate_vertices, remove_colinear_vertices
|
||||
from .vertices import (
|
||||
remove_duplicate_vertices, remove_colinear_vertices, poly_contains_points
|
||||
)
|
||||
from .transform import rotation_matrix_2d, normalize_mirror
|
||||
|
||||
#from . import pack2d
|
||||
|
@ -52,3 +52,66 @@ def remove_colinear_vertices(vertices: ArrayLike, closed_path: bool = True) -> N
|
||||
slopes_equal[[0, -1]] = False
|
||||
|
||||
return vertices[~slopes_equal]
|
||||
|
||||
|
||||
def poly_contains_points(
|
||||
vertices: ArrayLike,
|
||||
points: ArrayLike,
|
||||
include_boundary: bool = True,
|
||||
) -> NDArray[numpy.int_]:
|
||||
"""
|
||||
Tests whether the provided points are inside the implicitly closed polygon
|
||||
described by the provided list of vertices.
|
||||
|
||||
Args:
|
||||
vertices: Nx2 Arraylike of form [[x0, y0], [x1, y1], ...], describing an implicitly-
|
||||
closed polygon. Note that this should include any offsets.
|
||||
points: Nx2 ArrayLike of form [[x0, y0], [x1, y1], ...] containing the points to test.
|
||||
include_boundary: True if points on the boundary should be count as inside the shape.
|
||||
Default True.
|
||||
|
||||
Returns:
|
||||
ndarray of booleans, [point0_is_in_shape, point1_is_in_shape, ...]
|
||||
"""
|
||||
points = numpy.array(points, copy=False)
|
||||
vertices = numpy.array(vertices, copy=False)
|
||||
|
||||
if points.size == 0:
|
||||
return numpy.zeros(0)
|
||||
|
||||
min_bounds = numpy.min(vertices, axis=0)[None, :]
|
||||
max_bounds = numpy.max(vertices, axis=0)[None, :]
|
||||
|
||||
trivially_outside = ((points < min_bounds).any(axis=1)
|
||||
| (points > max_bounds).any(axis=1))
|
||||
|
||||
nontrivial = ~trivially_outside
|
||||
if trivially_outside.all():
|
||||
inside = numpy.zeros_like(trivially_outside, dtype=bool)
|
||||
return inside
|
||||
|
||||
ntpts = points[None, nontrivial, :] # nontrivial points, along axis 1 of ndarray
|
||||
verts = vertices[:, :, None]
|
||||
|
||||
y0_le = verts[:, 1] <= ntpts[..., 1] # (axis 0) y_vertex <= y_point (axis 1)
|
||||
y1_le = numpy.roll(y0_le, -1, axis=0) # rolled by 1 vertex
|
||||
|
||||
upward = y0_le & ~y1_le
|
||||
downward = ~y0_le & y1_le
|
||||
|
||||
dv = numpy.roll(verts, -1, axis=0) - verts
|
||||
is_left = (dv[:, 0] * (ntpts[..., 1] - verts[:, 1]) # >0 if left of dv, <0 if right, 0 if on the line
|
||||
- dv[:, 1] * (ntpts[..., 0] - verts[:, 0]))
|
||||
|
||||
winding_number = ((upward & (is_left > 0)).sum(axis=0)
|
||||
- (downward & (is_left < 0)).sum(axis=0))
|
||||
|
||||
nontrivial_inside = winding_number != 0 # filter nontrivial points based on winding number
|
||||
if include_boundary:
|
||||
nontrivial_inside[(is_left == 0).any(axis=0)] = True # check if point lies on any edge
|
||||
|
||||
inside = nontrivial.copy()
|
||||
inside[nontrivial] = nontrivial_inside
|
||||
return inside
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user