123 lines
3.6 KiB
Python
123 lines
3.6 KiB
Python
from abc import abstractmethod, ABCMeta
|
|
from itertools import permutations
|
|
import numpy as np
|
|
|
|
class Point(object):
|
|
__slots__ = ["x", "y"]
|
|
def __init__(self, x, y):
|
|
self.x = x
|
|
self.y = y
|
|
|
|
def __abs__(self):
|
|
return (self.x**2 + self.y**2)**0.5
|
|
def __add__(self, other):
|
|
if(isinstance(self, Point)):
|
|
return Point(self.x + other.x, self.y + other.y)
|
|
raise TypeError("cannot add {} and {}".format(type(other), type(self)))
|
|
def __sub__(self, other):
|
|
if(isinstance(self, Point)):
|
|
return Point(self.x - other.x, self.y - other.y)
|
|
raise TypeError("cannot subtract {} and {}".format(type(other), type(self)))
|
|
|
|
def rotate(self, angle):
|
|
return Point(self.x * np.cos(angle) - self.y * np.sin(angle)
|
|
, self.x * np.sin(angle) + self.y * np.cos(angle))
|
|
def __repr__(self):
|
|
return "{}({}, {})".format(type(self).__name__, self.x, self.y)
|
|
|
|
|
|
class TwoDimensionalObject(metaclass=ABCMeta):
|
|
@abstractmethod
|
|
def contains_point(self, point: Point):
|
|
pass
|
|
|
|
def __contains__(self, other):
|
|
if(isinstance(other, Point)):
|
|
return self.contains_point(other)
|
|
raise TypeError("unable to check if {} is in {}".format(type(other), type(self)))
|
|
|
|
|
|
class Circle(TwoDimensionalObject):
|
|
def __init__(self, origin: Point, radius):
|
|
self.origin = origin
|
|
self.radius = radius
|
|
|
|
def contains_point(self, point: Point):
|
|
return abs(self.origin - point) < self.radius
|
|
|
|
def __repr__(self):
|
|
return "{}({}, {})".format(type(self).__name__, self.origin, self.radius)
|
|
|
|
class Rectangle(TwoDimensionalObject):
|
|
"""
|
|
A Rectangle is constructed as follows:
|
|
The Points p1, p2 are connected using orthogonal lines::
|
|
|
|
p1 +-------+
|
|
| |
|
|
| |
|
|
| |
|
|
+-------+ p2
|
|
|
|
and then the Rectangle is rotated ``angle`` degrees around ``p1``.
|
|
|
|
"""
|
|
def __init__(self, p1: Point, p2: Point, angle=0):
|
|
self.p1 = p1
|
|
self.p2 = p2
|
|
|
|
self.local_p1 = Point(0, 0)
|
|
self.local_p2 = (p2 - p1).rotate(angle)
|
|
|
|
self.angle = angle
|
|
|
|
def contains_point(self, point: Point):
|
|
point_in_eigen_frame = (point - self.p1).rotate(self.angle)
|
|
|
|
if(self.local_p1.x < self.local_p2.x):
|
|
if(point_in_eigen_frame.x < self.local_p1.x or point_in_eigen_frame.x > self.local_p1.x):
|
|
return False
|
|
else:
|
|
if(point_in_eigen_frame.x > self.local_p1.x or point_in_eigen_frame.x < self.local_p1.x):
|
|
return False
|
|
|
|
if(self.local_p1.y < self.local_p2.y):
|
|
if(point_in_eigen_frame.y < self.local_p1.y or point_in_eigen_frame.y > self.local_p1.y):
|
|
return False
|
|
else:
|
|
if(point_in_eigen_frame.y > self.local_p1.y or point_in_eigen_frame.y < self.local_p1.y):
|
|
return False
|
|
|
|
return True
|
|
def __repr__(self):
|
|
return "{}({}, {}, angle={})".format(type(self).__name__, self.p1, self.p2, self.angle)
|
|
|
|
|
|
class Triangle(TwoDimensionalObject):
|
|
def __init__(self, p1: Point, p2: Point, p3: Point):
|
|
self.p1 = p1
|
|
self.p2 = p2
|
|
self.p3 = p3
|
|
def contains_point(self, point: Point):
|
|
points = [self.p1, self.p2, self.p3]
|
|
triangle_surface = abs(sum([points[i].x * (points[(i + 1) % 3].y - points[(i + 2) % 3].y) for i in range(3)]))
|
|
|
|
points = [point, self.p1, self.p2, self.p3]
|
|
surfaces = [abs(sum([p[i].x * (p[(i + 1) % 4].y - p[(i + 2) % 4].y) for i in range(4)])) for p in permutations(points)]
|
|
|
|
return max(surfaces) < triangle_surface
|
|
def __repr__(self):
|
|
return "{}({}, {}, {})".format(type(self).__name__, self.p1, self.p2, self.p3)
|
|
|
|
|
|
|
|
class CollectionOfFigures(object):
|
|
def __init__(self, figures):
|
|
self.figures = figures
|
|
|
|
def containing(self, point: Point):
|
|
return [f for f in self.figures if point in f]
|
|
|
|
def __repr__(self):
|
|
return "{}({})".format(type(self).__name__, repr(self.figures))
|