345 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			345 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
|  | from math import sqrt | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | class Point(object): | ||
|  | 
 | ||
|  |     __slots__ = ('x', 'y') | ||
|  | 
 | ||
|  |     def __init__(self, x, y): | ||
|  |         self.x = x | ||
|  |         self.y = y | ||
|  | 
 | ||
|  |     def __eq__(self, other): | ||
|  |         return (self.x == other.x and self.y == other.y) | ||
|  | 
 | ||
|  |     def __repr__(self): | ||
|  |         return "P({}, {})".format(self.x, self.y) | ||
|  | 
 | ||
|  |     def distance(self, point): | ||
|  |         """
 | ||
|  |         Calculate distance to another point | ||
|  |         """
 | ||
|  |         return sqrt((self.x-point.x)**2+(self.y-point.y)**2) | ||
|  | 
 | ||
|  |     def distance_squared(self, point): | ||
|  |         return (self.x-point.x)**2+(self.y-point.y)**2 | ||
|  | 
 | ||
|  | 
 | ||
|  | class Segment(object): | ||
|  |      | ||
|  |     __slots__ = ('start', 'end') | ||
|  | 
 | ||
|  |     def __init__(self, start, end): | ||
|  |         """
 | ||
|  |         Arguments: | ||
|  |             start (Point): Segment start point | ||
|  |             end (Point): Segment end point | ||
|  |         """
 | ||
|  |         assert(isinstance(start, Point) and isinstance(end, Point)) | ||
|  |         self.start = start | ||
|  |         self.end = end | ||
|  | 
 | ||
|  |     def __eq__(self, other): | ||
|  |         if not isinstance(other, self.__class__): | ||
|  |             None | ||
|  |         return self.start==other.start and self.end==other.end | ||
|  | 
 | ||
|  |     def __repr__(self): | ||
|  |         return "S({}, {})".format(self.start, self.end) | ||
|  |      | ||
|  |     @property | ||
|  |     def length_squared(self): | ||
|  |         """Faster than length and useful for some comparisons""" | ||
|  |         return self.start.distance_squared(self.end) | ||
|  | 
 | ||
|  |     @property | ||
|  |     def length(self): | ||
|  |         return self.start.distance(self.end) | ||
|  | 
 | ||
|  |     @property | ||
|  |     def top(self): | ||
|  |         return max(self.start.y, self.end.y) | ||
|  |      | ||
|  |     @property | ||
|  |     def bottom(self): | ||
|  |         return min(self.start.y, self.end.y) | ||
|  | 
 | ||
|  |     @property | ||
|  |     def right(self): | ||
|  |         return max(self.start.x, self.end.x) | ||
|  | 
 | ||
|  |     @property | ||
|  |     def left(self): | ||
|  |         return min(self.start.x, self.end.x) | ||
|  | 
 | ||
|  | 
 | ||
|  | class HSegment(Segment): | ||
|  |     """Horizontal Segment"""  | ||
|  | 
 | ||
|  |     def __init__(self, start, length): | ||
|  |         """
 | ||
|  |         Create an Horizontal segment given its left most end point and its | ||
|  |         length. | ||
|  | 
 | ||
|  |         Arguments: | ||
|  |             - start (Point): Starting Point | ||
|  |             - length (number): segment length | ||
|  |         """
 | ||
|  |         assert(isinstance(start, Point) and not isinstance(length, Point)) | ||
|  |         super(HSegment, self).__init__(start, Point(start.x+length, start.y)) | ||
|  | 
 | ||
|  |     @property | ||
|  |     def length(self): | ||
|  |         return self.end.x-self.start.x | ||
|  | 
 | ||
|  | 
 | ||
|  | class VSegment(Segment): | ||
|  |     """Vertical Segment""" | ||
|  | 
 | ||
|  |     def __init__(self, start, length): | ||
|  |         """
 | ||
|  |         Create a Vertical segment given its bottom most end point and its | ||
|  |         length. | ||
|  |          | ||
|  |         Arguments: | ||
|  |             - start (Point): Starting Point | ||
|  |             - length (number): segment length | ||
|  |         """
 | ||
|  |         assert(isinstance(start, Point) and not isinstance(length, Point)) | ||
|  |         super(VSegment, self).__init__(start, Point(start.x, start.y+length)) | ||
|  | 
 | ||
|  |     @property | ||
|  |     def length(self): | ||
|  |         return self.end.y-self.start.y | ||
|  |      | ||
|  | 
 | ||
|  | 
 | ||
|  | class Rectangle(object): | ||
|  |     """Basic rectangle primitive class.
 | ||
|  |     x, y-> Lower right corner coordinates | ||
|  |     width -  | ||
|  |     height -  | ||
|  |     """
 | ||
|  |     __slots__ = ('width', 'height', 'x', 'y', 'rid') | ||
|  | 
 | ||
|  |     def __init__(self, x, y, width, height, rid = None): | ||
|  |         """
 | ||
|  |         Args: | ||
|  |             x (int, float): | ||
|  |             y (int, float): | ||
|  |             width (int, float): | ||
|  |             height (int, float): | ||
|  |             rid (int): | ||
|  |         """
 | ||
|  |         assert(height >=0 and width >=0) | ||
|  | 
 | ||
|  |         self.width = width | ||
|  |         self.height = height | ||
|  |         self.x = x | ||
|  |         self.y = y | ||
|  |         self.rid = rid | ||
|  | 
 | ||
|  |     @property | ||
|  |     def bottom(self): | ||
|  |         """
 | ||
|  |         Rectangle bottom edge y coordinate | ||
|  |         """
 | ||
|  |         return self.y | ||
|  | 
 | ||
|  |     @property | ||
|  |     def top(self): | ||
|  |         """
 | ||
|  |         Rectangle top edge y coordiante | ||
|  |         """
 | ||
|  |         return self.y+self.height | ||
|  | 
 | ||
|  |     @property | ||
|  |     def left(self): | ||
|  |         """
 | ||
|  |         Rectangle left ednge x coordinate | ||
|  |         """
 | ||
|  |         return self.x | ||
|  | 
 | ||
|  |     @property | ||
|  |     def right(self): | ||
|  |         """
 | ||
|  |         Rectangle right edge x coordinate | ||
|  |         """
 | ||
|  |         return self.x+self.width | ||
|  | 
 | ||
|  |     @property | ||
|  |     def corner_top_l(self): | ||
|  |         return Point(self.left, self.top) | ||
|  | 
 | ||
|  |     @property | ||
|  |     def corner_top_r(self): | ||
|  |         return Point(self.right, self.top) | ||
|  | 
 | ||
|  |     @property | ||
|  |     def corner_bot_r(self): | ||
|  |         return Point(self.right, self.bottom) | ||
|  | 
 | ||
|  |     @property | ||
|  |     def corner_bot_l(self): | ||
|  |         return Point(self.left, self.bottom) | ||
|  | 
 | ||
|  |     def __lt__(self, other): | ||
|  |         """
 | ||
|  |         Compare rectangles by area (used for sorting) | ||
|  |         """
 | ||
|  |         return self.area() < other.area() | ||
|  |      | ||
|  |     def __eq__(self, other): | ||
|  |         """
 | ||
|  |         Equal rectangles have same area. | ||
|  |         """
 | ||
|  |         if not isinstance(other, self.__class__): | ||
|  |             return False | ||
|  | 
 | ||
|  |         return (self.width == other.width and \ | ||
|  |                 self.height == other.height and \ | ||
|  |                 self.x == other.x and \ | ||
|  |                 self.y == other.y) | ||
|  | 
 | ||
|  |     def __hash__(self): | ||
|  |         return hash((self.x, self.y, self.width, self.height)) | ||
|  | 
 | ||
|  |     def __iter__(self): | ||
|  |         """
 | ||
|  |         Iterate through rectangle corners | ||
|  |         """
 | ||
|  |         yield self.corner_top_l | ||
|  |         yield self.corner_top_r | ||
|  |         yield self.corner_bot_r | ||
|  |         yield self.corner_bot_l | ||
|  | 
 | ||
|  |     def __repr__(self): | ||
|  |         return "R({}, {}, {}, {})".format(self.x, self.y, self.width, self.height) | ||
|  | 
 | ||
|  |     def area(self): | ||
|  |         """
 | ||
|  |         Rectangle area | ||
|  |         """
 | ||
|  |         return self.width * self.height | ||
|  | 
 | ||
|  |     def move(self, x, y): | ||
|  |         """
 | ||
|  |         Move Rectangle to x,y coordinates | ||
|  | 
 | ||
|  |         Arguments: | ||
|  |             x (int, float): X coordinate | ||
|  |             y (int, float): Y coordinate | ||
|  |         """
 | ||
|  |         self.x = x | ||
|  |         self.y = y | ||
|  | 
 | ||
|  |     def contains(self, rect): | ||
|  |         """
 | ||
|  |         Tests if another rectangle is contained by this one | ||
|  | 
 | ||
|  |         Arguments: | ||
|  |             rect (Rectangle): The other rectangle | ||
|  | 
 | ||
|  |         Returns: | ||
|  |             bool: True if it is container, False otherwise | ||
|  |         """
 | ||
|  |         return (rect.y >= self.y and \ | ||
|  |                 rect.x >= self.x and \ | ||
|  |                 rect.y+rect.height <= self.y+self.height and \ | ||
|  |                 rect.x+rect.width  <= self.x+self.width) | ||
|  | 
 | ||
|  |     def intersects(self, rect, edges=False): | ||
|  |         """
 | ||
|  |         Detect intersections between this and another Rectangle. | ||
|  | 
 | ||
|  |         Parameters: | ||
|  |             rect (Rectangle): The other rectangle. | ||
|  |             edges (bool): True to consider rectangles touching by their | ||
|  |                 edges or corners to be intersecting. | ||
|  |                 (Should have been named include_touching) | ||
|  | 
 | ||
|  |         Returns: | ||
|  |             bool: True if the rectangles intersect, False otherwise | ||
|  |         """
 | ||
|  |         if edges: | ||
|  |             if (self.bottom > rect.top or self.top < rect.bottom or\ | ||
|  |                 self.left > rect.right or self.right < rect.left): | ||
|  |                 return False | ||
|  |         else: | ||
|  |             if (self.bottom >= rect.top or self.top <= rect.bottom or | ||
|  |                 self.left >= rect.right or self.right <= rect.left): | ||
|  |                 return False | ||
|  | 
 | ||
|  |         return True | ||
|  | 
 | ||
|  |     def intersection(self, rect, edges=False): | ||
|  |         """
 | ||
|  |         Returns the rectangle resulting of the intersection between this and another  | ||
|  |         rectangle. If the rectangles are only touching by their edges, and the  | ||
|  |         argument 'edges' is True the rectangle returned will have an area of 0. | ||
|  |         Returns None if there is no intersection. | ||
|  |          | ||
|  |         Arguments: | ||
|  |              rect (Rectangle): The other rectangle. | ||
|  |              edges (bool): If True Rectangles touching by their edges are  | ||
|  |                 considered to be intersection. In this case a rectangle of  | ||
|  |                 0 height or/and width will be returned. | ||
|  | 
 | ||
|  |         Returns: | ||
|  |             Rectangle: Intersection. | ||
|  |             None: There was no intersection. | ||
|  |         """
 | ||
|  |         if not self.intersects(rect, edges=edges): | ||
|  |             return None | ||
|  |          | ||
|  |         bottom = max(self.bottom, rect.bottom) | ||
|  |         left = max(self.left, rect.left) | ||
|  |         top = min(self.top, rect.top) | ||
|  |         right = min(self.right, rect.right) | ||
|  | 
 | ||
|  |         return Rectangle(left, bottom, right-left, top-bottom) | ||
|  | 
 | ||
|  |     def join(self, other): | ||
|  |         """
 | ||
|  |         Try to join a rectangle to this one, if the result is also a rectangle  | ||
|  |         and the operation is successful and this rectangle is modified to the union. | ||
|  | 
 | ||
|  |         Arguments: | ||
|  |             other (Rectangle): Rectangle to join | ||
|  | 
 | ||
|  |         Returns: | ||
|  |             bool: True when successfully joined, False otherwise | ||
|  |         """
 | ||
|  |         if self.contains(other): | ||
|  |             return True | ||
|  | 
 | ||
|  |         if other.contains(self): | ||
|  |             self.x = other.x | ||
|  |             self.y = other.y | ||
|  |             self.width = other.width | ||
|  |             self.height = other.height | ||
|  |             return True | ||
|  | 
 | ||
|  |         if not self.intersects(other, edges=True): | ||
|  |             return False | ||
|  | 
 | ||
|  |         # Other rectangle is Up/Down from this | ||
|  |         if  self.left == other.left and self.width == other.width: | ||
|  |             y_min = min(self.bottom, other.bottom) | ||
|  |             y_max = max(self.top, other.top)   | ||
|  |             self.y = y_min | ||
|  |             self.height = y_max-y_min | ||
|  |             return True | ||
|  | 
 | ||
|  |         # Other rectangle is Right/Left from this | ||
|  |         if  self.bottom == other.bottom and self.height == other.height: | ||
|  |             x_min = min(self.left, other.left) | ||
|  |             x_max = max(self.right, other.right) | ||
|  |             self.x = x_min | ||
|  |             self.width = x_max-x_min | ||
|  |             return True | ||
|  | 
 | ||
|  |         return False | ||
|  | 
 |