├── tempCodeRunnerFile.py ├── py3dbp ├── __init__.py ├── __pycache__ │ ├── main.cpython-36.pyc │ ├── main.cpython-37.pyc │ ├── main.cpython-38.pyc │ ├── __init__.cpython-36.pyc │ ├── __init__.cpython-37.pyc │ ├── __init__.cpython-38.pyc │ ├── constants.cpython-36.pyc │ ├── constants.cpython-37.pyc │ ├── constants.cpython-38.pyc │ ├── auxiliary_methods.cpython-36.pyc │ ├── auxiliary_methods.cpython-37.pyc │ └── auxiliary_methods.cpython-38.pyc ├── constants.py ├── auxiliary_methods.py └── main.py ├── makale.pdf ├── Screens ├── 1_1.png ├── 1_2.png ├── 1_3.png ├── 1_4.png ├── 1_5.png ├── 1_6.png ├── 2_1.png ├── 2_2.png ├── 2_3.png ├── 2_4.png ├── 2_5.png ├── 2_6.png ├── 3_1.png ├── 3_2.png ├── 3_3.png ├── 3_4.png ├── 3_5.png └── 3_6.png ├── .idea ├── misc.xml ├── vcs.xml ├── .gitignore ├── inspectionProfiles │ └── profiles_settings.xml ├── other.xml ├── modules.xml └── BB-container-problem.iml ├── setup.py ├── LICENSE ├── README.md └── visualize.py /tempCodeRunnerFile.py: -------------------------------------------------------------------------------- 1 | 240 -------------------------------------------------------------------------------- /py3dbp/__init__.py: -------------------------------------------------------------------------------- 1 | from .main import Packer, Bin, Item 2 | -------------------------------------------------------------------------------- /makale.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ylmz-dev/3d-bin-packing-problem/HEAD/makale.pdf -------------------------------------------------------------------------------- /Screens/1_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ylmz-dev/3d-bin-packing-problem/HEAD/Screens/1_1.png -------------------------------------------------------------------------------- /Screens/1_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ylmz-dev/3d-bin-packing-problem/HEAD/Screens/1_2.png -------------------------------------------------------------------------------- /Screens/1_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ylmz-dev/3d-bin-packing-problem/HEAD/Screens/1_3.png -------------------------------------------------------------------------------- /Screens/1_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ylmz-dev/3d-bin-packing-problem/HEAD/Screens/1_4.png -------------------------------------------------------------------------------- /Screens/1_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ylmz-dev/3d-bin-packing-problem/HEAD/Screens/1_5.png -------------------------------------------------------------------------------- /Screens/1_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ylmz-dev/3d-bin-packing-problem/HEAD/Screens/1_6.png -------------------------------------------------------------------------------- /Screens/2_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ylmz-dev/3d-bin-packing-problem/HEAD/Screens/2_1.png -------------------------------------------------------------------------------- /Screens/2_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ylmz-dev/3d-bin-packing-problem/HEAD/Screens/2_2.png -------------------------------------------------------------------------------- /Screens/2_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ylmz-dev/3d-bin-packing-problem/HEAD/Screens/2_3.png -------------------------------------------------------------------------------- /Screens/2_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ylmz-dev/3d-bin-packing-problem/HEAD/Screens/2_4.png -------------------------------------------------------------------------------- /Screens/2_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ylmz-dev/3d-bin-packing-problem/HEAD/Screens/2_5.png -------------------------------------------------------------------------------- /Screens/2_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ylmz-dev/3d-bin-packing-problem/HEAD/Screens/2_6.png -------------------------------------------------------------------------------- /Screens/3_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ylmz-dev/3d-bin-packing-problem/HEAD/Screens/3_1.png -------------------------------------------------------------------------------- /Screens/3_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ylmz-dev/3d-bin-packing-problem/HEAD/Screens/3_2.png -------------------------------------------------------------------------------- /Screens/3_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ylmz-dev/3d-bin-packing-problem/HEAD/Screens/3_3.png -------------------------------------------------------------------------------- /Screens/3_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ylmz-dev/3d-bin-packing-problem/HEAD/Screens/3_4.png -------------------------------------------------------------------------------- /Screens/3_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ylmz-dev/3d-bin-packing-problem/HEAD/Screens/3_5.png -------------------------------------------------------------------------------- /Screens/3_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ylmz-dev/3d-bin-packing-problem/HEAD/Screens/3_6.png -------------------------------------------------------------------------------- /py3dbp/__pycache__/main.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ylmz-dev/3d-bin-packing-problem/HEAD/py3dbp/__pycache__/main.cpython-36.pyc -------------------------------------------------------------------------------- /py3dbp/__pycache__/main.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ylmz-dev/3d-bin-packing-problem/HEAD/py3dbp/__pycache__/main.cpython-37.pyc -------------------------------------------------------------------------------- /py3dbp/__pycache__/main.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ylmz-dev/3d-bin-packing-problem/HEAD/py3dbp/__pycache__/main.cpython-38.pyc -------------------------------------------------------------------------------- /py3dbp/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ylmz-dev/3d-bin-packing-problem/HEAD/py3dbp/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /py3dbp/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ylmz-dev/3d-bin-packing-problem/HEAD/py3dbp/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /py3dbp/__pycache__/__init__.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ylmz-dev/3d-bin-packing-problem/HEAD/py3dbp/__pycache__/__init__.cpython-38.pyc -------------------------------------------------------------------------------- /py3dbp/__pycache__/constants.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ylmz-dev/3d-bin-packing-problem/HEAD/py3dbp/__pycache__/constants.cpython-36.pyc -------------------------------------------------------------------------------- /py3dbp/__pycache__/constants.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ylmz-dev/3d-bin-packing-problem/HEAD/py3dbp/__pycache__/constants.cpython-37.pyc -------------------------------------------------------------------------------- /py3dbp/__pycache__/constants.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ylmz-dev/3d-bin-packing-problem/HEAD/py3dbp/__pycache__/constants.cpython-38.pyc -------------------------------------------------------------------------------- /py3dbp/__pycache__/auxiliary_methods.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ylmz-dev/3d-bin-packing-problem/HEAD/py3dbp/__pycache__/auxiliary_methods.cpython-36.pyc -------------------------------------------------------------------------------- /py3dbp/__pycache__/auxiliary_methods.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ylmz-dev/3d-bin-packing-problem/HEAD/py3dbp/__pycache__/auxiliary_methods.cpython-37.pyc -------------------------------------------------------------------------------- /py3dbp/__pycache__/auxiliary_methods.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ylmz-dev/3d-bin-packing-problem/HEAD/py3dbp/__pycache__/auxiliary_methods.cpython-38.pyc -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Datasource local storage ignored files 5 | /dataSources/ 6 | /dataSources.local.xml 7 | # Editor-based HTTP Client requests 8 | /httpRequests/ 9 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/other.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /py3dbp/constants.py: -------------------------------------------------------------------------------- 1 | class RotationType: 2 | RT_WHD = 0 3 | RT_HWD = 1 4 | RT_HDW = 2 5 | RT_DHW = 3 6 | RT_DWH = 4 7 | RT_WDH = 5 8 | 9 | ALL = [RT_WHD, RT_HWD, RT_HDW, RT_DHW, RT_DWH, RT_WDH] 10 | 11 | 12 | class Axis: 13 | WIDTH = 0 14 | HEIGHT = 1 15 | DEPTH = 2 16 | 17 | ALL = [WIDTH, HEIGHT, DEPTH] 18 | -------------------------------------------------------------------------------- /.idea/BB-container-problem.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 11 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import setuptools 2 | 3 | with open("README.md", "r") as fh: 4 | long_description = fh.read() 5 | 6 | setuptools.setup( 7 | name='py3dbp', 8 | version='1.1', 9 | author="Enzo Ruiz Pelaez", 10 | author_email="enzo.rp.90@gmail.com", 11 | description="3D Bin Packing", 12 | long_description=long_description, 13 | long_description_content_type="text/markdown", 14 | url="https://github.com/enzoruiz/3dbinpacking", 15 | packages=setuptools.find_packages(), 16 | classifiers=[ 17 | "Programming Language :: Python :: 3", 18 | "License :: OSI Approved :: MIT License", 19 | "Operating System :: OS Independent", 20 | ], 21 | ) 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /py3dbp/auxiliary_methods.py: -------------------------------------------------------------------------------- 1 | from decimal import Decimal 2 | from .constants import Axis 3 | 4 | 5 | def rect_intersect(item1, item2, x, y): 6 | d1 = item1.get_dimension() 7 | d2 = item2.get_dimension() 8 | 9 | cx1 = item1.position[x] + d1[x]/2 10 | cy1 = item1.position[y] + d1[y]/2 11 | cx2 = item2.position[x] + d2[x]/2 12 | cy2 = item2.position[y] + d2[y]/2 13 | 14 | ix = max(cx1, cx2) - min(cx1, cx2) 15 | iy = max(cy1, cy2) - min(cy1, cy2) 16 | 17 | return ix < (d1[x]+d2[x])/2 and iy < (d1[y]+d2[y])/2 18 | 19 | 20 | def intersect(item1, item2): 21 | return ( 22 | rect_intersect(item1, item2, Axis.WIDTH, Axis.HEIGHT) and 23 | rect_intersect(item1, item2, Axis.HEIGHT, Axis.DEPTH) and 24 | rect_intersect(item1, item2, Axis.WIDTH, Axis.DEPTH) 25 | ) 26 | 27 | 28 | def get_limit_number_of_decimals(number_of_decimals): 29 | return Decimal('1.{}'.format('0' * number_of_decimals)) 30 | 31 | 32 | def set_to_decimal(value, number_of_decimals): 33 | number_of_decimals = get_limit_number_of_decimals(number_of_decimals) 34 | 35 | return Decimal(value).quantize(number_of_decimals) 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 3D Bin Packing Problem 2 | ==== 3 | 4 | 3D Bin Packing implementasyonu bu makale baz alınarak geliştirilmiştir. [Makale](makale.pdf). Referans kod: [gedex](https://github.com/gedex/bp3d). 5 | 6 | ## Açıklama 7 | 8 | Bin ve item parametrelerinin tanımlanması: 9 | ```py 10 | my_bin = Bin(name, width, height, depth, max_weight) 11 | my_item = Item(name, width, height, depth, weight) 12 | ``` 13 | Packer 3 temel fonksiyona sahip: 14 | ```py 15 | packer = Packer() # PACKER DEFINITION 16 | 17 | packer.add_bin(my_bin) # ADDING BINS TO PACKER 18 | packer.add_item(my_item) # ADDING ITEMS TO PACKER 19 | 20 | packer.pack() # PACKING - by default (bigger_first=False, distribute_items=False, number_of_decimals=3) 21 | ``` 22 | 23 | Packing işlemi sonrasında: 24 | ```py 25 | packer.bins # GET ALL BINS OF PACKER 26 | my_bin.items # GET ALL FITTED ITEMS IN EACH BIN 27 | my_bin.unfitted_items # GET ALL UNFITTED ITEMS IN EACH BIN 28 | ``` 29 | 30 | 31 | ## Kullanım 32 | 33 | Packer oluşturulup istenilen sayıda bin ve istenilen binlere gerekli itemlar eklenebilir. .pack() fonksiyonu verilen bilgiler doğrusunda packing işlemini geçekleştirecektir. 34 | 35 | ```py 36 | from py3dbp import Packer, Bin, Item 37 | 38 | packer = Packer() 39 | 40 | packer.add_bin(Bin('small-envelope', 11.5, 6.125, 0.25, 10)) 41 | packer.add_bin(Bin('large-envelope', 15.0, 12.0, 0.75, 15)) 42 | packer.add_bin(Bin('small-box', 8.625, 5.375, 1.625, 70.0)) 43 | packer.add_bin(Bin('medium-box', 11.0, 8.5, 5.5, 70.0)) 44 | packer.add_bin(Bin('medium-2-box', 13.625, 11.875, 3.375, 70.0)) 45 | packer.add_bin(Bin('large-box', 12.0, 12.0, 5.5, 70.0)) 46 | packer.add_bin(Bin('large-2-box', 23.6875, 11.75, 3.0, 70.0)) 47 | 48 | packer.add_item(Item('50g [powder 1]', 3.9370, 1.9685, 1.9685, 1)) 49 | packer.add_item(Item('50g [powder 2]', 3.9370, 1.9685, 1.9685, 2)) 50 | packer.add_item(Item('50g [powder 3]', 3.9370, 1.9685, 1.9685, 3)) 51 | packer.add_item(Item('250g [powder 4]', 7.8740, 3.9370, 1.9685, 4)) 52 | packer.add_item(Item('250g [powder 5]', 7.8740, 3.9370, 1.9685, 5)) 53 | packer.add_item(Item('250g [powder 6]', 7.8740, 3.9370, 1.9685, 6)) 54 | packer.add_item(Item('250g [powder 7]', 7.8740, 3.9370, 1.9685, 7)) 55 | packer.add_item(Item('250g [powder 8]', 7.8740, 3.9370, 1.9685, 8)) 56 | packer.add_item(Item('250g [powder 9]', 7.8740, 3.9370, 1.9685, 9)) 57 | 58 | packer.pack() 59 | 60 | for b in packer.bins: 61 | print(":::::::::::", b.string()) 62 | 63 | print("FITTED ITEMS:") 64 | for item in b.items: 65 | print("====> ", item.string()) 66 | 67 | print("UNFITTED ITEMS:") 68 | for item in b.unfitted_items: 69 | print("====> ", item.string()) 70 | 71 | print("***************************************************") 72 | print("***************************************************") 73 | 74 | ``` 75 | ## GÖRSELLEŞTİRME 76 | visualize.py dosyası kullanılarak 3 boyutlu görselleştirilmiş sonuçlara doğrudan ulaşılabilir. 77 | 78 | ### Örnek görselleştirmeler 79 | ![Örnek Görselleştirme - 1](Screens/1_5.png) 80 | ![Örnek Görselleştirme - 1](Screens/1_1.png) 81 | 82 | 83 | ## TEST DENEMELERİ 84 | Denenen test case senaryolarının sonuçlarına ve görselleştirmelerine aşağıda linkteki Python Repository sekmesinden ulaşabilirsiniz. 85 | > https://docs.google.com/spreadsheets/d/1GicB3rwzPJe_Hlm_xAzUpQnWtp2qObtKjfGarEcGwDI/edit?usp=sharing 86 | 87 | ## REFERANSLAR 88 | 89 | * https://github.com/bom-d-van/binpacking 90 | * https://github.com/gedex/bp3d 91 | * [MAKALE: Optimizing three-dimensional bin packing through simulation](makale.pdf) 92 | -------------------------------------------------------------------------------- /visualize.py: -------------------------------------------------------------------------------- 1 | from py3dbp import Packer, Bin, Item 2 | from mpl_toolkits.mplot3d import Axes3D 3 | from mpl_toolkits.mplot3d.art3d import Poly3DCollection 4 | import numpy as np 5 | import matplotlib.pyplot as plt 6 | import random 7 | 8 | trucks = [ 9 | [250, 250, 500], 10 | [500, 500, 400], 11 | [300, 300, 300], 12 | [300, 300, 200], 13 | [300, 300, 100], 14 | [500, 500, 500] 15 | ] 16 | 17 | 18 | 19 | 20 | for t in range(len(trucks)): 21 | packer = Packer() 22 | 23 | #packer.add_bin(Bin('small-envelope', 11.5, 6.125, 0.25, 10)) 24 | #packer.add_bin(Bin('large-envelope', 15.0, 12.0, 0.75, 15)) 25 | #packer.add_bin(Bin('small-box', 8.625, 5.375, 1.625, 70.0)) 26 | #packer.add_bin(Bin('medium-box', 11.0, 8.5, 5.5, 70.0)) 27 | #packer.add_bin(Bin('medium-2-box', 13.625, 11.875, 3.375, 70.0)) 28 | #packer.add_bin(Bin('large-box', 240, 244, 1360, 70.0)) 29 | #packer.add_bin(Bin('large-2-box', 23.6875, 11.75, 3.0, 70.0)) 30 | 31 | truckX = trucks[t][0] 32 | truckY = trucks[t][1] 33 | truckZ = trucks[t][2] 34 | 35 | packer.add_bin(Bin('LB', truckX, truckY, truckZ, 3000.0)) 36 | 37 | 38 | for i in range(300): 39 | packer.add_item(Item('boxL'+str(i), 20, 40, 20, 1)) 40 | 41 | for i in range(10): 42 | packer.add_item(Item('boxU'+str(i), 100, 100, 100, 1)) 43 | 44 | for i in range(5): 45 | packer.add_item(Item('boxU'+str(i), 200, 100, 50, 1)) 46 | 47 | for i in range(10): 48 | packer.add_item(Item('boxU'+str(i), 40, 40, 20, 1)) 49 | 50 | 51 | #packer.pack() 52 | packer.pack(bigger_first=False) 53 | 54 | positions = [] 55 | sizes = [] 56 | colors = [] 57 | 58 | 59 | for b in packer.bins: 60 | print(":::::::::::", b.string()) 61 | 62 | print("FITTED ITEMS:") 63 | for item in b.items: 64 | print("====> ", item.string()) 65 | x = float(item.position[0]) 66 | y = float(item.position[1]) 67 | z = float(item.position[2]) 68 | positions.append((x,y,z)) 69 | sizes.append((float(item.get_dimension()[0]), float(item.get_dimension()[1]), float(item.get_dimension()[2]))) 70 | 71 | print("UNFITTED ITEMS:") 72 | for item in b.unfitted_items: 73 | print("====> ", item.string()) 74 | 75 | 76 | print("***************************************************") 77 | print("***************************************************") 78 | 79 | 80 | 81 | def cuboid_data2(o, size=(1,1,1)): 82 | X = [[[0, 1, 0], [0, 0, 0], [1, 0, 0], [1, 1, 0]], 83 | [[0, 0, 0], [0, 0, 1], [1, 0, 1], [1, 0, 0]], 84 | [[1, 0, 1], [1, 0, 0], [1, 1, 0], [1, 1, 1]], 85 | [[0, 0, 1], [0, 0, 0], [0, 1, 0], [0, 1, 1]], 86 | [[0, 1, 0], [0, 1, 1], [1, 1, 1], [1, 1, 0]], 87 | [[0, 1, 1], [0, 0, 1], [1, 0, 1], [1, 1, 1]]] 88 | X = np.array(X).astype(float) 89 | for i in range(3): 90 | X[:,:,i] *= size[i] 91 | X += np.array(o) 92 | return X 93 | 94 | def plotCubeAt2(positions,sizes=None,colors=None, **kwargs): 95 | if not isinstance(colors,(list,np.ndarray)): colors=["C0"]*len(positions) 96 | if not isinstance(sizes,(list,np.ndarray)): sizes=[(1,1,1)]*len(positions) 97 | g = [] 98 | for p,s,c in zip(positions,sizes,colors): 99 | g.append( cuboid_data2(p, size=s) ) 100 | return Poly3DCollection(np.concatenate(g), 101 | facecolors=np.repeat(colors,6), **kwargs) 102 | 103 | 104 | colorList = ["crimson","limegreen","g","r","c","m","y","k"] 105 | 106 | for i in range(len(b.items)): 107 | f = random.randint(0,7) 108 | colors.append(colorList[f]) 109 | 110 | print(colors) 111 | 112 | fig = plt.figure() 113 | ax = fig.gca(projection='3d') 114 | ax.set_aspect('auto') 115 | 116 | pc = plotCubeAt2(positions,sizes,colors=colors, edgecolor="k") 117 | ax.add_collection3d(pc) 118 | 119 | ax.set_xlim([0,truckX]) 120 | ax.set_ylim([0,truckY]) 121 | ax.set_zlim([0,truckZ]) 122 | 123 | plt.show() 124 | 125 | 126 | -------------------------------------------------------------------------------- /py3dbp/main.py: -------------------------------------------------------------------------------- 1 | from .constants import RotationType, Axis 2 | from .auxiliary_methods import intersect, set_to_decimal 3 | 4 | DEFAULT_NUMBER_OF_DECIMALS = 3 5 | START_POSITION = [0, 0, 0] 6 | 7 | 8 | class Item: 9 | def __init__(self, name, width, height, depth, weight): 10 | self.name = name 11 | self.width = width 12 | self.height = height 13 | self.depth = depth 14 | self.weight = weight 15 | self.rotation_type = 0 16 | self.position = START_POSITION 17 | self.number_of_decimals = DEFAULT_NUMBER_OF_DECIMALS 18 | 19 | def format_numbers(self, number_of_decimals): 20 | self.width = set_to_decimal(self.width, number_of_decimals) 21 | self.height = set_to_decimal(self.height, number_of_decimals) 22 | self.depth = set_to_decimal(self.depth, number_of_decimals) 23 | self.weight = set_to_decimal(self.weight, number_of_decimals) 24 | self.number_of_decimals = number_of_decimals 25 | 26 | def string(self): 27 | return "%s(%sx%sx%s, weight: %s) pos(%s) rt(%s) vol(%s)" % ( 28 | self.name, self.width, self.height, self.depth, self.weight, 29 | self.position, self.rotation_type, self.get_volume() 30 | ) 31 | 32 | def get_volume(self): 33 | return set_to_decimal( 34 | self.width * self.height * self.depth, self.number_of_decimals 35 | ) 36 | 37 | def get_dimension(self): 38 | if self.rotation_type == RotationType.RT_WHD: 39 | dimension = [self.width, self.height, self.depth] 40 | elif self.rotation_type == RotationType.RT_HWD: 41 | dimension = [self.height, self.width, self.depth] 42 | elif self.rotation_type == RotationType.RT_HDW: 43 | dimension = [self.height, self.depth, self.width] 44 | elif self.rotation_type == RotationType.RT_DHW: 45 | dimension = [self.depth, self.height, self.width] 46 | elif self.rotation_type == RotationType.RT_DWH: 47 | dimension = [self.depth, self.width, self.height] 48 | elif self.rotation_type == RotationType.RT_WDH: 49 | dimension = [self.width, self.depth, self.height] 50 | else: 51 | dimension = [] 52 | 53 | return dimension 54 | 55 | 56 | class Bin: 57 | def __init__(self, name, width, height, depth, max_weight): 58 | self.name = name 59 | self.width = width 60 | self.height = height 61 | self.depth = depth 62 | self.max_weight = max_weight 63 | self.items = [] 64 | self.unfitted_items = [] 65 | self.number_of_decimals = DEFAULT_NUMBER_OF_DECIMALS 66 | 67 | def format_numbers(self, number_of_decimals): 68 | self.width = set_to_decimal(self.width, number_of_decimals) 69 | self.height = set_to_decimal(self.height, number_of_decimals) 70 | self.depth = set_to_decimal(self.depth, number_of_decimals) 71 | self.max_weight = set_to_decimal(self.max_weight, number_of_decimals) 72 | self.number_of_decimals = number_of_decimals 73 | 74 | def string(self): 75 | return "%s(%sx%sx%s, max_weight:%s) vol(%s)" % ( 76 | self.name, self.width, self.height, self.depth, self.max_weight, 77 | self.get_volume() 78 | ) 79 | 80 | def get_volume(self): 81 | return set_to_decimal( 82 | self.width * self.height * self.depth, self.number_of_decimals 83 | ) 84 | 85 | def get_total_weight(self): 86 | total_weight = 0 87 | 88 | for item in self.items: 89 | total_weight += item.weight 90 | 91 | return set_to_decimal(total_weight, self.number_of_decimals) 92 | 93 | def put_item(self, item, pivot): 94 | fit = False 95 | valid_item_position = item.position 96 | item.position = pivot 97 | 98 | for i in range(0, len(RotationType.ALL)): 99 | item.rotation_type = i 100 | dimension = item.get_dimension() 101 | if ( 102 | self.width < pivot[0] + dimension[0] or 103 | self.height < pivot[1] + dimension[1] or 104 | self.depth < pivot[2] + dimension[2] 105 | ): 106 | continue 107 | 108 | fit = True 109 | 110 | for current_item_in_bin in self.items: 111 | if intersect(current_item_in_bin, item): 112 | fit = False 113 | break 114 | 115 | if fit: 116 | if self.get_total_weight() + item.weight > self.max_weight: 117 | fit = False 118 | return fit 119 | 120 | self.items.append(item) 121 | 122 | if not fit: 123 | item.position = valid_item_position 124 | 125 | return fit 126 | 127 | if not fit: 128 | item.position = valid_item_position 129 | 130 | return fit 131 | 132 | 133 | class Packer: 134 | def __init__(self): 135 | self.bins = [] 136 | self.items = [] 137 | self.unfit_items = [] 138 | self.total_items = 0 139 | 140 | def add_bin(self, bin): 141 | return self.bins.append(bin) 142 | 143 | def add_item(self, item): 144 | self.total_items = len(self.items) + 1 145 | 146 | return self.items.append(item) 147 | 148 | def pack_to_bin(self, bin, item): 149 | fitted = False 150 | 151 | if not bin.items: 152 | response = bin.put_item(item, START_POSITION) 153 | 154 | if not response: 155 | bin.unfitted_items.append(item) 156 | 157 | return 158 | 159 | for axis in range(0, 3): 160 | items_in_bin = bin.items 161 | 162 | for ib in items_in_bin: 163 | pivot = [0, 0, 0] 164 | w, h, d = ib.get_dimension() 165 | if axis == Axis.WIDTH: 166 | pivot = [ 167 | ib.position[0] + w, 168 | ib.position[1], 169 | ib.position[2] 170 | ] 171 | elif axis == Axis.HEIGHT: 172 | pivot = [ 173 | ib.position[0], 174 | ib.position[1] + h, 175 | ib.position[2] 176 | ] 177 | elif axis == Axis.DEPTH: 178 | pivot = [ 179 | ib.position[0], 180 | ib.position[1], 181 | ib.position[2] + d 182 | ] 183 | 184 | if bin.put_item(item, pivot): 185 | fitted = True 186 | break 187 | if fitted: 188 | break 189 | 190 | if not fitted: 191 | bin.unfitted_items.append(item) 192 | 193 | def pack( 194 | self, bigger_first=False, distribute_items=False, 195 | number_of_decimals=DEFAULT_NUMBER_OF_DECIMALS 196 | ): 197 | for bin in self.bins: 198 | bin.format_numbers(number_of_decimals) 199 | 200 | for item in self.items: 201 | item.format_numbers(number_of_decimals) 202 | 203 | self.bins.sort( 204 | key=lambda bin: bin.get_volume(), reverse=bigger_first 205 | ) 206 | self.items.sort( 207 | key=lambda item: item.get_volume(), reverse=bigger_first 208 | ) 209 | 210 | for bin in self.bins: 211 | for item in self.items: 212 | self.pack_to_bin(bin, item) 213 | 214 | if distribute_items: 215 | for item in bin.items: 216 | self.items.remove(item) 217 | --------------------------------------------------------------------------------