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))