geometry.py 1.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950
  1. import typing
  2. import numpy
  3. def _collinear(vector_a: numpy.ndarray, vector_b: numpy.ndarray) -> bool:
  4. # null vector: https://math.stackexchange.com/a/1772580
  5. return numpy.allclose(numpy.cross(vector_a, vector_b), 0)
  6. class _Line:
  7. # pylint: disable=too-few-public-methods
  8. def __init__(self, point, vector):
  9. self.point = numpy.array(point, dtype=float)
  10. self.vector = numpy.array(vector, dtype=float)
  11. def __eq__(self, other: '_Line') -> bool:
  12. if not _collinear(self.vector, other.vector):
  13. return False
  14. return _collinear(self.vector, self.point - other.point)
  15. def __repr__(self) -> str:
  16. return 'line(t) = {} + {} t'.format(self.point, self.vector)
  17. def _intersect_line(self, other: '_Line') \
  18. -> typing.Union[numpy.ndarray, bool]:
  19. # https://en.wikipedia.org/wiki/Skew_lines#Distance
  20. lines_normal_vector = numpy.cross(self.vector, other.vector)
  21. if numpy.allclose(lines_normal_vector, 0):
  22. return _collinear(self.vector, self.point - other.point)
  23. plane_normal_vector = numpy.cross(other.vector, lines_normal_vector)
  24. return self.point + self.vector \
  25. * (numpy.inner(other.point - self.point, plane_normal_vector)
  26. / numpy.inner(self.vector, plane_normal_vector))
  27. def _intersect_planes(normal_vector_a: numpy.ndarray,
  28. constant_a: float,
  29. normal_vector_b: numpy.ndarray,
  30. constant_b: float) -> typing.Union[_Line, bool]:
  31. line_vector = numpy.cross(normal_vector_a, normal_vector_b)
  32. if numpy.allclose(line_vector, 0):
  33. return constant_a == constant_b
  34. point = numpy.linalg.solve(
  35. numpy.vstack((normal_vector_a, normal_vector_b, line_vector)),
  36. numpy.vstack((constant_a, constant_b, 0)),
  37. )
  38. return _Line(point=point.reshape(3), vector=line_vector)