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