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