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