forked from LeenkxTeam/LNXSDK
		
	
		
			
	
	
		
			141 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			141 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
|  | from .geometry import Rectangle | ||
|  | 
 | ||
|  | 
 | ||
|  | class PackingAlgorithm(object): | ||
|  |     """PackingAlgorithm base class""" | ||
|  | 
 | ||
|  |     def __init__(self, width, height, rot=True, bid=None, *args, **kwargs): | ||
|  |         """
 | ||
|  |         Initialize packing algorithm | ||
|  | 
 | ||
|  |         Arguments: | ||
|  |             width (int, float): Packing surface width | ||
|  |             height (int, float): Packing surface height | ||
|  |             rot (bool): Rectangle rotation enabled or disabled | ||
|  |             bid (string|int|...): Packing surface identification | ||
|  |         """
 | ||
|  |         self.width = width | ||
|  |         self.height = height | ||
|  |         self.rot = rot | ||
|  |         self.rectangles = [] | ||
|  |         self.bid = bid | ||
|  |         self._surface = Rectangle(0, 0, width, height) | ||
|  |         self.reset() | ||
|  | 
 | ||
|  |     def __len__(self): | ||
|  |         return len(self.rectangles) | ||
|  | 
 | ||
|  |     def __iter__(self): | ||
|  |         return iter(self.rectangles) | ||
|  | 
 | ||
|  |     def _fits_surface(self, width, height): | ||
|  |         """
 | ||
|  |         Test surface is big enough to place a rectangle | ||
|  | 
 | ||
|  |         Arguments: | ||
|  |             width (int, float): Rectangle width | ||
|  |             height (int, float): Rectangle height | ||
|  | 
 | ||
|  |         Returns: | ||
|  |             boolean: True if it could be placed, False otherwise | ||
|  |         """
 | ||
|  |         assert(width > 0 and height > 0) | ||
|  |         if self.rot and (width > self.width or height > self.height): | ||
|  |             width, height = height, width | ||
|  | 
 | ||
|  |         if width > self.width or height > self.height: | ||
|  |             return False | ||
|  |         else: | ||
|  |             return True | ||
|  |      | ||
|  |     def __getitem__(self, key): | ||
|  |         """
 | ||
|  |         Return rectangle in selected position. | ||
|  |         """
 | ||
|  |         return self.rectangles[key] | ||
|  | 
 | ||
|  |     def used_area(self): | ||
|  |         """
 | ||
|  |         Total area of rectangles placed | ||
|  | 
 | ||
|  |         Returns: | ||
|  |             int, float: Area | ||
|  |         """
 | ||
|  |         return sum(r.area() for r in self) | ||
|  | 
 | ||
|  |     def fitness(self, width, height, rot = False): | ||
|  |         """
 | ||
|  |         Metric used to rate how much space is wasted if a rectangle is placed. | ||
|  |         Returns a value greater or equal to zero, the smaller the value the more  | ||
|  |         'fit' is the rectangle. If the rectangle can't be placed, returns None. | ||
|  | 
 | ||
|  |         Arguments: | ||
|  |             width (int, float): Rectangle width | ||
|  |             height (int, float): Rectangle height | ||
|  |             rot (bool): Enable rectangle rotation | ||
|  | 
 | ||
|  |         Returns: | ||
|  |             int, float: Rectangle fitness  | ||
|  |             None: Rectangle can't be placed | ||
|  |         """
 | ||
|  |         raise NotImplementedError | ||
|  |          | ||
|  |     def add_rect(self, width, height, rid=None): | ||
|  |         """
 | ||
|  |         Add rectangle of widthxheight dimensions. | ||
|  | 
 | ||
|  |         Arguments: | ||
|  |             width (int, float): Rectangle width | ||
|  |             height (int, float): Rectangle height | ||
|  |             rid: Optional rectangle user id | ||
|  | 
 | ||
|  |         Returns: | ||
|  |             Rectangle: Rectangle with placemente coordinates | ||
|  |             None: If the rectangle couldn be placed. | ||
|  |         """
 | ||
|  |         raise NotImplementedError | ||
|  | 
 | ||
|  |     def rect_list(self): | ||
|  |         """
 | ||
|  |         Returns a list with all rectangles placed into the surface. | ||
|  |          | ||
|  |         Returns: | ||
|  |             List: Format [(x, y, width, height, rid), ...] | ||
|  |         """
 | ||
|  |         rectangle_list = [] | ||
|  |         for r in self: | ||
|  |             rectangle_list.append((r.x, r.y, r.width, r.height, r.rid)) | ||
|  | 
 | ||
|  |         return rectangle_list | ||
|  | 
 | ||
|  |     def validate_packing(self): | ||
|  |         """
 | ||
|  |         Check for collisions between rectangles, also check all are placed | ||
|  |         inside surface. | ||
|  |         """
 | ||
|  |         surface = Rectangle(0, 0, self.width, self.height) | ||
|  | 
 | ||
|  |         for r in self: | ||
|  |             if not surface.contains(r): | ||
|  |                 raise Exception("Rectangle placed outside surface") | ||
|  | 
 | ||
|  |          | ||
|  |         rectangles = [r for r in self] | ||
|  |         if len(rectangles) <= 1: | ||
|  |             return | ||
|  | 
 | ||
|  |         for r1 in range(0, len(rectangles)-2): | ||
|  |             for r2 in range(r1+1, len(rectangles)-1): | ||
|  |                 if rectangles[r1].intersects(rectangles[r2]): | ||
|  |                     raise Exception("Rectangle collision detected") | ||
|  | 
 | ||
|  |     def is_empty(self): | ||
|  |         # Returns true if there is no rectangles placed. | ||
|  |         return not bool(len(self)) | ||
|  | 
 | ||
|  |     def reset(self): | ||
|  |         self.rectangles = []    # List of placed Rectangles. | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 |