1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071 |
- import typing
- import numpy
- def _collinear(vector_a: numpy.ndarray, vector_b: numpy.ndarray) -> bool:
- # null vector: https://math.stackexchange.com/a/1772580
- return numpy.allclose(numpy.cross(vector_a, vector_b), 0)
- class _Line:
- # pylint: disable=too-few-public-methods
- def __init__(self, point, vector):
- self.point = numpy.array(point, dtype=float)
- self.vector = numpy.array(vector, dtype=float)
- def __eq__(self, other: '_Line') -> bool:
- if not _collinear(self.vector, other.vector):
- return False
- return _collinear(self.vector, self.point - other.point)
- def __repr__(self) -> str:
- return 'line(t) = {} + {} t'.format(self.point, self.vector)
- def intersect_line(self, other: '_Line') \
- -> typing.Union[numpy.ndarray, bool]:
- # https://en.wikipedia.org/wiki/Skew_lines#Distance
- lines_normal_vector = numpy.cross(self.vector, other.vector)
- if numpy.allclose(lines_normal_vector, 0):
- return _collinear(self.vector, self.point - other.point)
- plane_normal_vector = numpy.cross(other.vector, lines_normal_vector)
- return self.point + self.vector \
- * (numpy.inner(other.point - self.point, plane_normal_vector)
- / numpy.inner(self.vector, plane_normal_vector))
- def _intersect_planes(normal_vector_a: numpy.ndarray,
- constant_a: float,
- normal_vector_b: numpy.ndarray,
- constant_b: float) -> typing.Union[_Line, bool]:
- line_vector = numpy.cross(normal_vector_a, normal_vector_b)
- if numpy.allclose(line_vector, 0):
- return constant_a == constant_b
- point = numpy.linalg.solve(
- numpy.vstack((normal_vector_a, normal_vector_b, line_vector)),
- numpy.vstack((constant_a, constant_b, 0)),
- )
- return _Line(point=point.reshape(3), vector=line_vector)
- def _between(lower_limit: numpy.ndarray,
- point: numpy.ndarray,
- upper_limit: numpy.ndarray) -> bool:
- return (lower_limit <= point).all() and (point <= upper_limit).all()
- def _intersect_line_segments(points_a: numpy.ndarray,
- points_b: numpy.ndarray) \
- -> typing.Union[numpy.ndarray, bool]:
- lines = [_Line(points[0], points[1] - points[0])
- for points in [points_a, points_b]]
- point = lines[0].intersect_line(lines[1])
- if isinstance(point, bool):
- return point
- for points in [points_a, points_b]:
- if not _between(points[0], point, points[1]) \
- and not _between(points[1], point, points[0]):
- return False
- return point
|