forked from LeenkxTeam/LNXSDK
		
	
		
			
				
	
	
		
			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
 | |
| 
 |