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