diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/PSACodeSprint.iml b/.idea/PSACodeSprint.iml new file mode 100644 index 0000000..d6ebd48 --- /dev/null +++ b/.idea/PSACodeSprint.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..266346e --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..7fb634e --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,18 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..c070446 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,15 @@ + + + + \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 0000000..fdc392f --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..fa45db1 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..a07ec54 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/CSVParser/3DBinPacker.py b/CSVParser/3DBinPacker.py new file mode 100644 index 0000000..afed7f4 --- /dev/null +++ b/CSVParser/3DBinPacker.py @@ -0,0 +1,123 @@ +import random +import csv + +from py3dbp import Packer, Bin, Item, Painter + +COLORS = ["yellow", "olive", "pink", "brown", "red", "blue", "green", "purple", "orange", "gray"] +# Initialize variables to store bins and items +bins = [] +bin_size = -1 +items = [] +item_size = 0 +counter = 0 + +# Parse through the CSV file +with open('testval3.csv', mode='r', encoding='utf-8-sig') as csv_file: + csv_reader = csv.reader(csv_file) + # Iterate through the rows and parse bins and items + for row in csv_reader: + # Check if the row is empty + if not any(row): + continue + + # If there is a single number in the row, it represents the number of bins or items + if row[1] == "": + num = int(row[0]) + if bin_size == -1: + bin_size = num + counter = bin_size + else: + item_size = num + else: + a = row[0] + b = row[1] + values = tuple(int(val) for val in row if val) + if counter > 0: + bins.append(values) + counter -= 1 + else: + items.append(values) + +# init packing function +packer = Packer() +# init bin +for i in range(len(bins)): + box = Bin('Bin{}'.format(str(i+1)), bins[i], 100, 0, 0) + packer.addBin(box) + +# add item +for i in range(len(items)): + packer.addItem(Item( + partno='Box-{}'.format(str(i+1)), + name='test{}'.format(str(i+1)), + typeof='cube', + WHD=items[i], + weight=1, + level=1, + loadbear=100, + updown=True, + color=random.choice(COLORS) + ) + ) + +# calculate packing +packer.pack( + bigger_first=True, + distribute_items=True, + fix_point=True, + check_stable=True, + support_surface_ratio=0.75, + number_of_decimals=0 +) + +# put order +packer.putOrder() + +output = "***************************************************\n" +for idx, b in enumerate(packer.bins): + output += f"** {b.string()} **\n" + output += "***************************************************\n" + output += "FITTED ITEMS:\n" + output += "***************************************************\n" + volume = b.width * b.height * b.depth + volume_t = 0 + volume_f = 0 + unfitted_name = '' + for item in b.items: + output += f"partno : {item.partno}\n" + output += f"position : {item.position}\n" + output += f"W*H*D : {item.width} * {item.height} * {item.depth}\n" + output += f"volume : {float(item.width) * float(item.height) * float(item.depth)}\n" + volume_t += float(item.width) * float(item.height) * float(item.depth) + output += "***************************************************\n" + + output += f'space utilization : {round(volume_t / float(volume) * 100, 2)}%\n' + output += f'residual volume : {float(volume) - volume_t}\n' + output += "***************************************************\n" + # draw results + painter = Painter(b) + fig = painter.plotBoxAndItems( + title=b.partno, + alpha=0.8, + write_num=False, + fontsize=10 + ) + +output += "***************************************************\n" +output += "UNFITTED ITEMS:\n" +for item in packer.unfit_items: + output += "***************************************************\n" + output += f"partno : {item.partno}\n" + output += f"W*H*D : {item.width} * {item.height} * {item.depth}\n" + output += f"volume : {float(item.width) * float(item.height) * float(item.depth)}\n" + volume_f += float(item.width) * float(item.height) * float(item.depth) + unfitted_name += f'{item.partno},' + output += "***************************************************\n" +output += "***************************************************\n" +output += f'unpacked items : {unfitted_name}\n' +output += f'unpacked items volume : {volume_f}\n' + +# Print the entire output +print(output) + +fig.show() \ No newline at end of file diff --git a/CSVParser/testval.csv b/CSVParser/testval.csv new file mode 100644 index 0000000..1ca74c4 --- /dev/null +++ b/CSVParser/testval.csv @@ -0,0 +1,36 @@ +2,, +5,10,5 +6,10,6 +32,, +5,4,1 +1,2,4 +1,2,3 +1,2,2 +1,2,3 +1,2,4 +1,2,2 +1,2,4 +1,2,3 +1,2,2 +5,4,1 +1,1,4 +1,2,1 +1,2,1 +1,1,4 +5,4,2 +5,4,2 +5,4,2 +5,4,2 +5,4,2 +5,4,2 +5,4,2 +5,4,2 +5,4,2 +5,4,2 +5,4,2 +5,4,1 +5,4,1 +5,4,1 +5,4,1 +5,4,1 +5,4,1 \ No newline at end of file diff --git a/CSVParser/testval2.csv b/CSVParser/testval2.csv new file mode 100644 index 0000000..a356212 --- /dev/null +++ b/CSVParser/testval2.csv @@ -0,0 +1,20 @@ +2,, +5,10,5 +6,10,6 +16,, +5,4,1 +1,2,4 +1,2,3 +1,2,2 +1,2,3 +1,2,4 +1,2,2 +1,2,4 +1,2,3 +1,2,2 +5,4,1 +1,1,4 +1,2,1 +1,2,1 +1,1,4 +5,4,2 \ No newline at end of file diff --git a/CSVParser/testval3.csv b/CSVParser/testval3.csv new file mode 100644 index 0000000..9f4be75 --- /dev/null +++ b/CSVParser/testval3.csv @@ -0,0 +1,37 @@ +3,, +5,10,5 +5,10,5 +5,10,5 +32,, +5,4,1 +1,2,4 +1,2,3 +1,2,2 +1,2,3 +1,2,4 +1,2,2 +1,2,4 +1,2,3 +1,2,2 +5,4,1 +1,1,4 +1,2,1 +1,2,1 +1,1,4 +5,4,2 +5,4,2 +5,4,2 +5,4,2 +5,4,2 +5,4,2 +5,4,2 +5,4,2 +5,4,2 +5,4,2 +5,4,2 +5,4,1 +5,4,1 +5,4,1 +5,4,1 +5,4,1 +5,4,1 \ No newline at end of file diff --git a/app.py b/app.py new file mode 100644 index 0000000..88086c8 --- /dev/null +++ b/app.py @@ -0,0 +1,173 @@ +from py3dbp import Packer, Bin, Item, Painter +import streamlit as st +import matplotlib.pyplot as plt +import random +import csv +from PIL import Image +import subprocess + +st.set_page_config(page_title="Streamlit App", page_icon=":smiley:", layout="wide") +col1, col2 = st.columns((1,2)) +with col1: + st.title("Codesprint 3D Bin Packer") + uploaded_file = st.file_uploader("Choose a file") + + + + +COLORS = ["yellow", "olive", "pink", "brown", "red", + "blue", "green", "purple", "orange", "gray"] +# Initialize variables to store bins and items +bins = [] +bin_size = -1 +items = [] +item_size = 0 +counter = 0 + +if uploaded_file is not None: + # Use uploaded file content instead of hardcoded path + uploaded_data = uploaded_file.read().decode('utf-8').splitlines() + csv_reader = csv.reader(uploaded_data) + # Iterate through the rows and parse bins and items + for row in csv_reader: + # Check if the row is empty + if not any(row): + continue + + # If there is a single number in the row, it represents the number of bins or items + if row[1] == "": + num = int(row[0].strip('\ufeff')) + if bin_size == -1: + bin_size = num + counter = bin_size + else: + item_size = num + else: + a = row[0] + b = row[1] + values = tuple(int(val) for val in row if val) + if counter > 0: + bins.append(values) + counter -= 1 + else: + items.append(values) + + # init packing function + packer = Packer() + # init bin + for i in range(len(bins)): + box = Bin('Container {}'.format(str(i+1)), bins[i], 100, 0, 0) + packer.addBin(box) + + # add item + for i in range(len(items)): + packer.addItem(Item( + partno='Box-{}'.format(str(i+1)), + name='test{}'.format(str(i+1)), + typeof='cube', + WHD=items[i], + weight=1, + level=1, + loadbear=100, + updown=True, + color=random.choice(COLORS) + ) + ) + + # calculate packing + packer.pack( + bigger_first=True, + distribute_items=True, + fix_point=True, + check_stable=True, + support_surface_ratio=0.75, + number_of_decimals=0 + ) + + + + # put order + packer.putOrder() + with col1: + st.title("Packing information:") + + # output = "***************************************************\n" + # output = '' + for idx, b in enumerate(packer.bins): + # output += f"** {b.string()} **\n" + with col1: + st.header(f"{b.string()} \n") + # output = f"{b.string()} \n" + # output += "***************************************************\n" + with col1: + st.subheader("FITTED ITEMS") + output = "" + # output += "***************************************************\n" + volume = b.width * b.height * b.depth + volume_t = 0 + volume_f = 0 + unfitted_name = '' + for item in b.items: + output = f"partno : {item.partno}\n" + with col1: + st.write(output) + output = f"position : {item.position}\n" + with col1: + st.write(output) + output = f"W*H*D : {item.width} * {item.height} * {item.depth}\n" + with col1: + st.write(output) + output = f"volume : {float(item.width) * float(item.height) * float(item.depth)}\n" + with col1: + st.write(output) + volume_t += float(item.width) * \ + float(item.height) * float(item.depth) + output = "***************************************************\n" + with col1: + st.write(output) + + output = f'space utilization : {round(volume_t / float(volume) * 100, 2)}%\n' + output += f'residual volume : {float(volume) - volume_t}\n' + # output += "***************************************************\n" + # draw results + with col1: + st.markdown(output) + painter = Painter(b) + fig = painter.plotBoxAndItems( + title=b.partno, + alpha=0.8, + write_num=False, + fontsize=10 + ) + + + with col2: + fig_name = "fig{index}.png".format(index=idx) + fig.savefig(fig_name) + st.image(Image.open(fig_name)) + + + output += "***************************************************\n" + output = "UNFITTED ITEMS:\n" + for item in packer.unfit_items: + # output += "***************************************************\n" + output += f"partno : {item.partno}\n" + output += f"W*H*D : {item.width} * {item.height} * {item.depth}\n" + output += f"volume : {float(item.width) * float(item.height) * float(item.depth)}\n" + volume_f += float(item.width) * \ + float(item.height) * float(item.depth) + unfitted_name += f'{item.partno},' + # output += "***************************************************\n" + # output += "***************************************************\n" + output += f'unpacked items : {unfitted_name}\n' + output += f'unpacked items volume : {volume_f}\n' + with col1: + st.write(output) + + + # Print the entire output + # print(output) + # st.title("Packing information:") + # st.text(output) + #st.pyplot(fig) + diff --git a/py3dbp/main.py b/py3dbp/main.py index b3c228e..a22ec40 100644 --- a/py3dbp/main.py +++ b/py3dbp/main.py @@ -117,12 +117,14 @@ def formatNumbers(self, number_of_decimals): self.number_of_decimals = number_of_decimals + # def string(self): + # ''' ''' + # return "%s(%sx%sx%s, max_weight:%s) vol(%s)" % ( + # self.partno, self.width, self.height, self.depth, self.max_weight, + # self.getVolume() + # ) def string(self): - ''' ''' - return "%s(%sx%sx%s, max_weight:%s) vol(%s)" % ( - self.partno, self.width, self.height, self.depth, self.max_weight, - self.getVolume() - ) + return self.partno def getVolume(self): @@ -538,7 +540,10 @@ def gravityCenter(self,bin): r = [area[0][2],area[1][2],area[2][2],area[3][2]] result = [] for i in r : - result.append(round(i / sum(r) * 100,2)) + try: + result.append(round(i / sum(r) * 100,2)) + except ZeroDivisionError: + result.append(0) return result