ytgui / temp

0 stars 0 forks source link

BPP #56

Closed ytgui closed 5 years ago

ytgui commented 5 years ago

https://github.com/wwei10/ga-bpp https://github.com/YacineGACI/BinPacking-GeneticAlgorithm https://github.com/pnvasko/GeneticBinPacker https://github.com/scottndiku/Binpacking-GA

ytgui commented 5 years ago
import random
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import patches

class Rect:
    def __init__(self, left_top, right_bottom=None):
        if not right_bottom:
            self.left, self.top = 0, 0
            self.right, self.bottom = left_top
        else:
            self.left, self.top = left_top
            self.right, self.bottom = right_bottom

    def height(self):
        return self.bottom - self.top

    def width(self):
        return self.right - self.left

    def area(self):
        return self.height() * self.width()

class BPPSolver:
    def __init__(self):
        self.option_rects = [Rect([2, 2]), Rect([3, 3]), Rect([4, 4]),
                             Rect([2, 3]), Rect([3, 2]), Rect([2, 4]), Rect([4, 2]), 
                             Rect([3, 4]), Rect([4, 3])]
        self.target_rect = None

    def solve(self, target_rect, depth=0):
        if depth == 0:
            self.target_rect = target_rect
        # exit condition
        assert target_rect.area() >= 0
        assert target_rect.width() >= 0
        assert target_rect.height() >= 0
        if target_rect.area() == 0:
            return True, []
        # recursive
        option_rects = self.option_rects.copy()
        random.shuffle(option_rects)
        for rect in option_rects:
            for new_target_1, new_target_2 in self.fill_area(target_rect, rect):
                if not self.rect_valid(new_target_1):
                    continue
                if not self.rect_valid(new_target_2):
                    continue
                assert new_target_1.area() + new_target_2.area() + rect.area() == target_rect.area()
                current_path = list()
                ret_1, path_1 = self.solve(new_target_1, depth=depth+1)
                ret_2, path_2 = self.solve(new_target_2, depth=depth+1)
                if ret_1 and ret_2:
                    rect_with_coord = Rect(left_top=[target_rect.left, target_rect.top],
                                           right_bottom=[target_rect.left + rect.width(), target_rect.top + rect.height()])
                    current_path.extend(path_1)
                    current_path.extend(path_2)
                    current_path.append(rect_with_coord)
                    return True, current_path
        return False, None

    def fill_area(self, current_rect, padding_rect):
        def spilt_vertical():
            new_rect_1 = Rect(left_top=(current_rect.left, current_rect.top + padding_rect.height()),
                            right_bottom=(current_rect.left + padding_rect.width(), current_rect.bottom))
            new_rect_2 = Rect(left_top=(current_rect.left + padding_rect.width(), current_rect.top),
                            right_bottom=(current_rect.right, current_rect.bottom))
            if random.random() < 0.5:
                return new_rect_1, new_rect_2
            return new_rect_2, new_rect_1

        def spilt_horizontal():
            new_rect_1 = Rect(left_top=(current_rect.left, current_rect.top + padding_rect.height()),
                            right_bottom=(current_rect.right, current_rect.bottom))
            new_rect_2 = Rect(left_top=(current_rect.left + padding_rect.width(), current_rect.top),
                            right_bottom=(current_rect.right, current_rect.bottom + padding_rect.height()))
            if random.random() < 0.5:
                return new_rect_1, new_rect_2
            return new_rect_2, new_rect_1

        if random.random() < 0.5:
            yield spilt_vertical()
            yield spilt_horizontal()
        else:
            yield spilt_horizontal()
            yield spilt_vertical()
        return

    def rect_valid(self, rect):
        if not self.target_rect.left <= rect.left <= rect.right <= self.target_rect.right:
            return False
        if not self.target_rect.top <= rect.top <= rect.bottom <= self.target_rect.bottom:
            return False
        return True

def check_no_overlap(big_rect, small_rect_list):
    pass

def main():
    #
    solver = BPPSolver()
    target_rect = Rect([16, 24])
    ret, result = solver.solve(target_rect=target_rect)
    print(ret, len(result))
    #
    plt.figure()
    plt.xlim(-5, target_rect.width() + 5)
    plt.ylim(-5, target_rect.height() + 5)
    currentAxis = plt.gca()
    for rect in result:
        currentAxis.add_patch(patches.Rectangle((rect.left, rect.top), rect.width(), rect.height(), alpha=1, color=np.random.rand(3)))
    plt.tight_layout()
    plt.show()

if __name__ == "__main__":
    # random.seed(4)
    main()
ytgui commented 5 years ago

image