369 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			369 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| 
								 | 
							
								from .pack_algo import PackingAlgorithm
							 | 
						||
| 
								 | 
							
								from .geometry import Rectangle
							 | 
						||
| 
								 | 
							
								import itertools
							 | 
						||
| 
								 | 
							
								import operator
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class Guillotine(PackingAlgorithm):
							 | 
						||
| 
								 | 
							
								    """Implementation of several variants of Guillotine packing algorithm
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    For a more detailed explanation of the algorithm used, see:
							 | 
						||
| 
								 | 
							
								    Jukka Jylanki - A Thousand Ways to Pack the Bin (February 27, 2010)
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    def __init__(self, width, height, rot=True, merge=True, *args, **kwargs):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        Arguments:
							 | 
						||
| 
								 | 
							
								            width (int, float):
							 | 
						||
| 
								 | 
							
								            height (int, float):
							 | 
						||
| 
								 | 
							
								            merge (bool): Optional keyword argument
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        self._merge = merge
							 | 
						||
| 
								 | 
							
								        super(Guillotine, self).__init__(width, height, rot, *args, **kwargs)
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _add_section(self, section):
							 | 
						||
| 
								 | 
							
								        """Adds a new section to the free section list, but before that and if 
							 | 
						||
| 
								 | 
							
								        section merge is enabled, tries to join the rectangle with all existing 
							 | 
						||
| 
								 | 
							
								        sections, if successful the resulting section is again merged with the 
							 | 
						||
| 
								 | 
							
								        remaining sections until the operation fails. The result is then 
							 | 
						||
| 
								 | 
							
								        appended to the list.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Arguments:
							 | 
						||
| 
								 | 
							
								            section (Rectangle): New free section.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        section.rid = 0     
							 | 
						||
| 
								 | 
							
								        plen = 0
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        while self._merge and self._sections and plen != len(self._sections):
							 | 
						||
| 
								 | 
							
								            plen = len(self._sections)
							 | 
						||
| 
								 | 
							
								            self._sections = [s for s in self._sections if not section.join(s)]
							 | 
						||
| 
								 | 
							
								        self._sections.append(section)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _split_horizontal(self, section, width, height):
							 | 
						||
| 
								 | 
							
								        """For an horizontal split the rectangle is placed in the lower
							 | 
						||
| 
								 | 
							
								        left corner of the section (section's xy coordinates), the top
							 | 
						||
| 
								 | 
							
								        most side of the rectangle and its horizontal continuation,
							 | 
						||
| 
								 | 
							
								        marks the line of division for the split.
							 | 
						||
| 
								 | 
							
								        +-----------------+
							 | 
						||
| 
								 | 
							
								        |                 |
							 | 
						||
| 
								 | 
							
								        |                 |
							 | 
						||
| 
								 | 
							
								        |                 |
							 | 
						||
| 
								 | 
							
								        |                 |
							 | 
						||
| 
								 | 
							
								        +-------+---------+
							 | 
						||
| 
								 | 
							
								        |#######|         |
							 | 
						||
| 
								 | 
							
								        |#######|         |
							 | 
						||
| 
								 | 
							
								        |#######|         |
							 | 
						||
| 
								 | 
							
								        +-------+---------+
							 | 
						||
| 
								 | 
							
								        If the rectangle width is equal to the the section width, only one
							 | 
						||
| 
								 | 
							
								        section is created over the rectangle. If the rectangle height is
							 | 
						||
| 
								 | 
							
								        equal to the section height, only one section to the right of the
							 | 
						||
| 
								 | 
							
								        rectangle is created. If both width and height are equal, no sections
							 | 
						||
| 
								 | 
							
								        are created.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        # First remove the section we are splitting so it doesn't 
							 | 
						||
| 
								 | 
							
								        # interfere when later we try to merge the resulting split
							 | 
						||
| 
								 | 
							
								        # rectangles, with the rest of free sections.
							 | 
						||
| 
								 | 
							
								        #self._sections.remove(section)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # Creates two new empty sections, and returns the new rectangle.
							 | 
						||
| 
								 | 
							
								        if height < section.height:
							 | 
						||
| 
								 | 
							
								            self._add_section(Rectangle(section.x, section.y+height,
							 | 
						||
| 
								 | 
							
								                section.width, section.height-height))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if width < section.width:
							 | 
						||
| 
								 | 
							
								            self._add_section(Rectangle(section.x+width, section.y,
							 | 
						||
| 
								 | 
							
								                section.width-width, height))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _split_vertical(self, section, width, height):
							 | 
						||
| 
								 | 
							
								        """For a vertical split the rectangle is placed in the lower
							 | 
						||
| 
								 | 
							
								        left corner of the section (section's xy coordinates), the
							 | 
						||
| 
								 | 
							
								        right most side of the rectangle and its vertical continuation,
							 | 
						||
| 
								 | 
							
								        marks the line of division for the split.
							 | 
						||
| 
								 | 
							
								        +-------+---------+
							 | 
						||
| 
								 | 
							
								        |       |         |
							 | 
						||
| 
								 | 
							
								        |       |         |
							 | 
						||
| 
								 | 
							
								        |       |         |
							 | 
						||
| 
								 | 
							
								        |       |         |
							 | 
						||
| 
								 | 
							
								        +-------+         |
							 | 
						||
| 
								 | 
							
								        |#######|         |
							 | 
						||
| 
								 | 
							
								        |#######|         |
							 | 
						||
| 
								 | 
							
								        |#######|         |
							 | 
						||
| 
								 | 
							
								        +-------+---------+
							 | 
						||
| 
								 | 
							
								        If the rectangle width is equal to the the section width, only one
							 | 
						||
| 
								 | 
							
								        section is created over the rectangle. If the rectangle height is
							 | 
						||
| 
								 | 
							
								        equal to the section height, only one section to the right of the
							 | 
						||
| 
								 | 
							
								        rectangle is created. If both width and height are equal, no sections
							 | 
						||
| 
								 | 
							
								        are created.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        # When a section is split, depending on the rectangle size 
							 | 
						||
| 
								 | 
							
								        # two, one, or no new sections will be created. 
							 | 
						||
| 
								 | 
							
								        if height < section.height:
							 | 
						||
| 
								 | 
							
								            self._add_section(Rectangle(section.x, section.y+height,
							 | 
						||
| 
								 | 
							
								                width, section.height-height))
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        if width < section.width:
							 | 
						||
| 
								 | 
							
								            self._add_section(Rectangle(section.x+width, section.y,
							 | 
						||
| 
								 | 
							
								                section.width-width, section.height))
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _split(self, section, width, height):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        Selects the best split for a section, given a rectangle of dimmensions
							 | 
						||
| 
								 | 
							
								        width and height, then calls _split_vertical or _split_horizontal, 
							 | 
						||
| 
								 | 
							
								        to do the dirty work.
							 | 
						||
| 
								 | 
							
								       
							 | 
						||
| 
								 | 
							
								        Arguments:
							 | 
						||
| 
								 | 
							
								            section (Rectangle): Section to split
							 | 
						||
| 
								 | 
							
								            width (int, float): Rectangle width
							 | 
						||
| 
								 | 
							
								            height (int, float): Rectangle height
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        raise NotImplementedError
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _section_fitness(self, section, width, height):
							 | 
						||
| 
								 | 
							
								        """The subclass for each one of the Guillotine selection methods,
							 | 
						||
| 
								 | 
							
								        BAF, BLSF.... will override this method, this is here only
							 | 
						||
| 
								 | 
							
								        to asure a valid value return if the worst happens.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        raise NotImplementedError
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _select_fittest_section(self, w, h):
							 | 
						||
| 
								 | 
							
								        """Calls _section_fitness for each of the sections in free section 
							 | 
						||
| 
								 | 
							
								        list. Returns the section with the minimal fitness value, all the rest 
							 | 
						||
| 
								 | 
							
								        is boilerplate to make the fitness comparison, to rotatate the rectangles,
							 | 
						||
| 
								 | 
							
								        and to take into account when _section_fitness returns None because 
							 | 
						||
| 
								 | 
							
								        the rectangle couldn't be placed.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Arguments:
							 | 
						||
| 
								 | 
							
								            w (int, float): Rectangle width
							 | 
						||
| 
								 | 
							
								            h (int, float): Rectangle height
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Returns:
							 | 
						||
| 
								 | 
							
								            (section, was_rotated): Returns the tuple 
							 | 
						||
| 
								 | 
							
								                section (Rectangle): Section with best fitness
							 | 
						||
| 
								 | 
							
								                was_rotated (bool): The rectangle was rotated 
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        fitn = ((self._section_fitness(s, w, h), s, False) for s in self._sections 
							 | 
						||
| 
								 | 
							
								                if self._section_fitness(s, w, h) is not None)
							 | 
						||
| 
								 | 
							
								        fitr = ((self._section_fitness(s, h, w), s, True) for s in self._sections 
							 | 
						||
| 
								 | 
							
								                if self._section_fitness(s, h, w) is not None)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if not self.rot:
							 | 
						||
| 
								 | 
							
								            fitr = []
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        fit = itertools.chain(fitn, fitr)
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								            _, sec, rot = min(fit, key=operator.itemgetter(0))
							 | 
						||
| 
								 | 
							
								        except ValueError:
							 | 
						||
| 
								 | 
							
								            return None, None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return sec, rot
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    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.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        assert(width > 0 and height >0)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # Obtain the best section to place the rectangle.
							 | 
						||
| 
								 | 
							
								        section, rotated = self._select_fittest_section(width, height)
							 | 
						||
| 
								 | 
							
								        if not section:
							 | 
						||
| 
								 | 
							
								            return None
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        if rotated:
							 | 
						||
| 
								 | 
							
								            width, height = height, width
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        # Remove section, split and store results
							 | 
						||
| 
								 | 
							
								        self._sections.remove(section)
							 | 
						||
| 
								 | 
							
								        self._split(section, width, height)
							 | 
						||
| 
								 | 
							
								       
							 | 
						||
| 
								 | 
							
								        # Store rectangle in the selected position
							 | 
						||
| 
								 | 
							
								        rect = Rectangle(section.x, section.y, width, height, rid)
							 | 
						||
| 
								 | 
							
								        self.rectangles.append(rect)
							 | 
						||
| 
								 | 
							
								        return rect
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def fitness(self, width, height):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        In guillotine algorithm case, returns the min of the fitness of all 
							 | 
						||
| 
								 | 
							
								        free sections, for the given dimension, both normal and rotated
							 | 
						||
| 
								 | 
							
								        (if rotation enabled.)
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        assert(width > 0 and height > 0)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # Get best fitness section.
							 | 
						||
| 
								 | 
							
								        section, rotated = self._select_fittest_section(width, height)
							 | 
						||
| 
								 | 
							
								        if not section:
							 | 
						||
| 
								 | 
							
								            return None
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        # Return fitness of returned section, with correct dimmensions if the
							 | 
						||
| 
								 | 
							
								        # the rectangle was rotated.
							 | 
						||
| 
								 | 
							
								        if rotated:
							 | 
						||
| 
								 | 
							
								            return self._section_fitness(section, height, width)
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            return self._section_fitness(section, width, height)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def reset(self):
							 | 
						||
| 
								 | 
							
								        super(Guillotine, self).reset()
							 | 
						||
| 
								 | 
							
								        self._sections = []
							 | 
						||
| 
								 | 
							
								        self._add_section(Rectangle(0, 0, self.width, self.height))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class GuillotineBaf(Guillotine):
							 | 
						||
| 
								 | 
							
								    """Implements Best Area Fit (BAF) section selection criteria for 
							 | 
						||
| 
								 | 
							
								    Guillotine algorithm.
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    def _section_fitness(self, section, width, height):
							 | 
						||
| 
								 | 
							
								        if width > section.width or height > section.height:
							 | 
						||
| 
								 | 
							
								            return None
							 | 
						||
| 
								 | 
							
								        return section.area()-width*height
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class GuillotineBlsf(Guillotine):
							 | 
						||
| 
								 | 
							
								    """Implements Best Long Side Fit (BLSF) section selection criteria for 
							 | 
						||
| 
								 | 
							
								    Guillotine algorithm.
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    def _section_fitness(self, section, width, height):
							 | 
						||
| 
								 | 
							
								        if width > section.width or height > section.height:
							 | 
						||
| 
								 | 
							
								            return None
							 | 
						||
| 
								 | 
							
								        return max(section.width-width, section.height-height)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class GuillotineBssf(Guillotine):
							 | 
						||
| 
								 | 
							
								    """Implements Best Short Side Fit (BSSF) section selection criteria for 
							 | 
						||
| 
								 | 
							
								    Guillotine algorithm.
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    def _section_fitness(self, section, width, height):
							 | 
						||
| 
								 | 
							
								        if width > section.width or height > section.height:
							 | 
						||
| 
								 | 
							
								            return None
							 | 
						||
| 
								 | 
							
								        return min(section.width-width, section.height-height)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class GuillotineSas(Guillotine):
							 | 
						||
| 
								 | 
							
								    """Implements Short Axis Split (SAS) selection rule for Guillotine 
							 | 
						||
| 
								 | 
							
								    algorithm.
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    def _split(self, section, width, height):
							 | 
						||
| 
								 | 
							
								        if section.width < section.height:
							 | 
						||
| 
								 | 
							
								            return self._split_horizontal(section, width, height)
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            return self._split_vertical(section, width, height)
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class GuillotineLas(Guillotine):
							 | 
						||
| 
								 | 
							
								    """Implements Long Axis Split (LAS) selection rule for Guillotine 
							 | 
						||
| 
								 | 
							
								    algorithm.
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    def _split(self, section, width, height):
							 | 
						||
| 
								 | 
							
								        if section.width >= section.height:
							 | 
						||
| 
								 | 
							
								            return self._split_horizontal(section, width, height)
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            return self._split_vertical(section, width, height)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class GuillotineSlas(Guillotine):
							 | 
						||
| 
								 | 
							
								    """Implements Short Leftover Axis Split (SLAS) selection rule for 
							 | 
						||
| 
								 | 
							
								    Guillotine algorithm.
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    def _split(self, section, width, height):
							 | 
						||
| 
								 | 
							
								        if section.width-width < section.height-height:
							 | 
						||
| 
								 | 
							
								            return self._split_horizontal(section, width, height)
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            return self._split_vertical(section, width, height)
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class GuillotineLlas(Guillotine):
							 | 
						||
| 
								 | 
							
								    """Implements Long Leftover Axis Split (LLAS) selection rule for 
							 | 
						||
| 
								 | 
							
								    Guillotine algorithm.
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    def _split(self, section, width, height):
							 | 
						||
| 
								 | 
							
								        if section.width-width >= section.height-height:
							 | 
						||
| 
								 | 
							
								            return self._split_horizontal(section, width, height)
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            return self._split_vertical(section, width, height)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class GuillotineMaxas(Guillotine):
							 | 
						||
| 
								 | 
							
								    """Implements Max Area Axis Split (MAXAS) selection rule for Guillotine
							 | 
						||
| 
								 | 
							
								    algorithm. Maximize the larger area == minimize the smaller area.
							 | 
						||
| 
								 | 
							
								    Tries to make the rectangles more even-sized.
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    def _split(self, section, width, height):
							 | 
						||
| 
								 | 
							
								        if width*(section.height-height) <= height*(section.width-width):
							 | 
						||
| 
								 | 
							
								            return self._split_horizontal(section, width, height)
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            return self._split_vertical(section, width, height)
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class GuillotineMinas(Guillotine):
							 | 
						||
| 
								 | 
							
								    """Implements Min Area Axis Split (MINAS) selection rule for Guillotine 
							 | 
						||
| 
								 | 
							
								    algorithm. 
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    def _split(self, section, width, height):
							 | 
						||
| 
								 | 
							
								        if width*(section.height-height) >= height*(section.width-width):
							 | 
						||
| 
								 | 
							
								            return self._split_horizontal(section, width, height)
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            return self._split_vertical(section, width, height)
							 | 
						||
| 
								 | 
							
								       
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								# Guillotine algorithms GUILLOTINE-RECT-SPLIT, Selecting one
							 | 
						||
| 
								 | 
							
								# Axis split, and one selection criteria.
							 | 
						||
| 
								 | 
							
								class GuillotineBssfSas(GuillotineBssf, GuillotineSas):
							 | 
						||
| 
								 | 
							
								    pass
							 | 
						||
| 
								 | 
							
								class GuillotineBssfLas(GuillotineBssf, GuillotineLas):
							 | 
						||
| 
								 | 
							
								    pass
							 | 
						||
| 
								 | 
							
								class GuillotineBssfSlas(GuillotineBssf, GuillotineSlas):
							 | 
						||
| 
								 | 
							
								    pass
							 | 
						||
| 
								 | 
							
								class GuillotineBssfLlas(GuillotineBssf, GuillotineLlas):
							 | 
						||
| 
								 | 
							
								    pass
							 | 
						||
| 
								 | 
							
								class GuillotineBssfMaxas(GuillotineBssf, GuillotineMaxas):
							 | 
						||
| 
								 | 
							
								    pass
							 | 
						||
| 
								 | 
							
								class GuillotineBssfMinas(GuillotineBssf, GuillotineMinas):
							 | 
						||
| 
								 | 
							
								    pass
							 | 
						||
| 
								 | 
							
								class GuillotineBlsfSas(GuillotineBlsf, GuillotineSas):
							 | 
						||
| 
								 | 
							
								    pass
							 | 
						||
| 
								 | 
							
								class GuillotineBlsfLas(GuillotineBlsf, GuillotineLas):
							 | 
						||
| 
								 | 
							
								    pass
							 | 
						||
| 
								 | 
							
								class GuillotineBlsfSlas(GuillotineBlsf, GuillotineSlas):
							 | 
						||
| 
								 | 
							
								    pass
							 | 
						||
| 
								 | 
							
								class GuillotineBlsfLlas(GuillotineBlsf, GuillotineLlas):
							 | 
						||
| 
								 | 
							
								    pass
							 | 
						||
| 
								 | 
							
								class GuillotineBlsfMaxas(GuillotineBlsf, GuillotineMaxas):
							 | 
						||
| 
								 | 
							
								    pass
							 | 
						||
| 
								 | 
							
								class GuillotineBlsfMinas(GuillotineBlsf, GuillotineMinas):
							 | 
						||
| 
								 | 
							
								    pass
							 | 
						||
| 
								 | 
							
								class GuillotineBafSas(GuillotineBaf, GuillotineSas):
							 | 
						||
| 
								 | 
							
								    pass
							 | 
						||
| 
								 | 
							
								class GuillotineBafLas(GuillotineBaf, GuillotineLas):
							 | 
						||
| 
								 | 
							
								    pass
							 | 
						||
| 
								 | 
							
								class GuillotineBafSlas(GuillotineBaf, GuillotineSlas):
							 | 
						||
| 
								 | 
							
								    pass
							 | 
						||
| 
								 | 
							
								class GuillotineBafLlas(GuillotineBaf, GuillotineLlas):
							 | 
						||
| 
								 | 
							
								    pass
							 | 
						||
| 
								 | 
							
								class GuillotineBafMaxas(GuillotineBaf, GuillotineMaxas):
							 | 
						||
| 
								 | 
							
								    pass
							 | 
						||
| 
								 | 
							
								class GuillotineBafMinas(GuillotineBaf, GuillotineMinas):
							 | 
						||
| 
								 | 
							
								    pass
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 |