Add WIP code
This commit is contained in:
168
containers.py
Normal file
168
containers.py
Normal file
@ -0,0 +1,168 @@
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
|
||||
class Container:
|
||||
def __init__(self, length, width, height, capacity):
|
||||
self.length = length # Länge in m
|
||||
self.width = width # Breite in m
|
||||
self.height = height # Höhe in m
|
||||
self.capacity = capacity # Gewichtskapazität in t
|
||||
self.items = []
|
||||
self.fitting = []
|
||||
|
||||
def volume(self):
|
||||
return self.length * self.width * self.height
|
||||
|
||||
def used_volume(self):
|
||||
if len(self.items) == 0:
|
||||
raise Exception("Container empty, can't calculate used volume!")
|
||||
|
||||
return sum(item[0].volume() * item[1] for item in self.items)
|
||||
|
||||
def utilization_volume(self):
|
||||
return self.used_volume() / self.volume() * 100
|
||||
|
||||
def used_weight(self):
|
||||
if len(self.items) == 0:
|
||||
raise Exception("Container empty, can't calculate load weight!")
|
||||
|
||||
return sum(item[0].weight * item[1] for item in self.items)
|
||||
|
||||
def utilization_weight(self):
|
||||
return self.used_weight() / self.capacity * 100
|
||||
|
||||
def try_fit(self, items) -> bool:
|
||||
self.items = items
|
||||
if self.used_weight() > self.capacity:
|
||||
print("Load too heavy!")
|
||||
return False
|
||||
if self.used_volume() > self.volume():
|
||||
print("Load too large!")
|
||||
return False
|
||||
|
||||
fitting = []
|
||||
|
||||
# Packstücke, Packordnung: Y, Z, X (Breite -> Höhe -> Länge)
|
||||
x_offset = 0
|
||||
y_offset = 0
|
||||
z_offset = 0
|
||||
for i, item_ in enumerate(items):
|
||||
item, count = item_
|
||||
|
||||
for _ in range(count):
|
||||
fitting += [
|
||||
(
|
||||
x_offset,
|
||||
y_offset,
|
||||
z_offset,
|
||||
item.length,
|
||||
item.width,
|
||||
item.height,
|
||||
COLORS[i],
|
||||
)
|
||||
]
|
||||
|
||||
if y_offset + item.width < self.width:
|
||||
# Selbe "Zeile"
|
||||
y_offset += item.width
|
||||
elif z_offset + item.height < self.height:
|
||||
y_offset = 0
|
||||
# Neue "Ebene" (Höhe)
|
||||
z_offset += item.height
|
||||
elif x_offset + item.length < self.length:
|
||||
y_offset = 0
|
||||
z_offset = 0
|
||||
# Neue "Scheibe" (Länge)
|
||||
x_offset += item.length
|
||||
else:
|
||||
return False
|
||||
|
||||
if x_offset + item.length < self.length:
|
||||
x_offset += item.length
|
||||
else:
|
||||
return False
|
||||
y_offset = 0
|
||||
z_offset = 0
|
||||
|
||||
self.fitting = fitting
|
||||
return True
|
||||
|
||||
def visualize(self):
|
||||
if len(self.fitting) == 0:
|
||||
raise Exception("Container not fitted, can't visualize!")
|
||||
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(111, projection="3d")
|
||||
|
||||
# Container
|
||||
ax.bar3d(0, 0, 0, self.length, self.width, self.height, alpha=0.1, color="gray")
|
||||
|
||||
# TODO: Don't add individual packages, combine them into blocks (perf)
|
||||
# Packages
|
||||
for x, y, z, l, w, h, color in self.fitting:
|
||||
ax.bar3d(x, y, z, l, w, h, color=color)
|
||||
|
||||
ax.set_xlim([0, self.length])
|
||||
ax.set_ylim([0, self.width])
|
||||
ax.set_zlim([0, self.height])
|
||||
ax.set_xlabel("Länge (cm)")
|
||||
ax.set_ylabel("Breite (cm)")
|
||||
ax.set_zlabel("Höhe (cm)")
|
||||
plt.title(f"Container-Auslastung: {self.utilization_volume():.2f}%")
|
||||
plt.autoscale()
|
||||
plt.show()
|
||||
|
||||
|
||||
class Item:
|
||||
def __init__(self, length, width, height, weight):
|
||||
self.length = length # Länge in cm
|
||||
self.width = width # Breite in cm
|
||||
self.height = height # Höhe in cm
|
||||
self.weight = weight # Gewicht in kg
|
||||
|
||||
def volume(self):
|
||||
return self.length * self.width * self.height
|
||||
|
||||
|
||||
# Length (cm), Width (cm), Height (cm), Capacity (kg)
|
||||
CONTAINERS = {
|
||||
"20'": Container(590, 235, 239, 21670),
|
||||
"40'": Container(1203, 240, 239, 25680),
|
||||
"40'HQ": Container(1203, 235, 269, 26480),
|
||||
}
|
||||
|
||||
COLORS = ["red", "blue", "green", "yellow", "purple"]
|
||||
|
||||
|
||||
def main():
|
||||
print("Container-Auslastungsrechner")
|
||||
items = []
|
||||
|
||||
while True:
|
||||
print("\nNeues Packstück hinzufügen:")
|
||||
length = float(input("Länge (cm): "))
|
||||
width = float(input("Breite (cm): "))
|
||||
height = float(input("Höhe (cm): "))
|
||||
weight = float(input("Gewicht (kg): "))
|
||||
count = int(input("Anzahl der Packstücke: "))
|
||||
|
||||
item = Item(length, width, height, weight)
|
||||
items += [(item, count)]
|
||||
|
||||
more_items = input("Weitere Packstücke hinzufügen? (ja/nein): ").strip().lower()
|
||||
if more_items != "ja":
|
||||
break
|
||||
|
||||
for name, container in CONTAINERS.items():
|
||||
if container.try_fit(items):
|
||||
print(
|
||||
f"\nBenötigter Container: {name}, Gesamtauslastung (Volumen): {container.utilization_volume():.2f}%, Ladungsgewicht: {container.used_weight()}kg, Gesamtauslastung (Gewicht): {container.utilization_weight():.2f}%"
|
||||
)
|
||||
container.visualize()
|
||||
return
|
||||
|
||||
print("Couldn't find fitting container!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
138
containersV2.py
Normal file
138
containersV2.py
Normal file
@ -0,0 +1,138 @@
|
||||
from typing import Dict, List, Tuple
|
||||
import ezdxf
|
||||
from ezdxf import colors
|
||||
from ezdxf.addons import binpacking as bp
|
||||
from ezdxf.addons.drawing import RenderContext, Frontend, matplotlib
|
||||
from ezdxf.addons.drawing.matplotlib import MatplotlibBackend
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
# Length (cm), Width (cm), Height (cm), Capacity (kg)
|
||||
CONTAINERS: List[Tuple[str, float, float, float, float]] = [
|
||||
("20'", 590, 235, 239, 21670),
|
||||
("40'", 1203, 240, 239, 25680),
|
||||
("40'HQ", 1203, 235, 269, 26480),
|
||||
]
|
||||
|
||||
COLORS: List[str] = ["red", "blue", "green", "yellow", "purple"]
|
||||
|
||||
|
||||
def float_input(msg: str) -> float:
|
||||
read_str: str = input(f"{msg}: ")
|
||||
read_float: float
|
||||
|
||||
try:
|
||||
read_float = float(read_str)
|
||||
except:
|
||||
return float_input(msg)
|
||||
|
||||
return read_float
|
||||
|
||||
|
||||
def int_input(msg: str) -> int:
|
||||
read_str: str = input(f"{msg}: ")
|
||||
read_int: int
|
||||
|
||||
try:
|
||||
read_int = int(read_str)
|
||||
except:
|
||||
return int_input(msg)
|
||||
|
||||
return read_int
|
||||
|
||||
|
||||
def yes_no_input(msg: str) -> bool:
|
||||
read_str: str = input(f"{msg} (j/n): ").strip().lower()
|
||||
|
||||
if read_str == "j":
|
||||
return True
|
||||
elif read_str == "n":
|
||||
return False
|
||||
else:
|
||||
return yes_no_input(msg)
|
||||
|
||||
|
||||
def build_packer(
|
||||
items: List[Tuple[Tuple[float, float, float, float], int]]
|
||||
) -> bp.Packer:
|
||||
packer: bp.Packer = bp.Packer()
|
||||
|
||||
for i, ((length, width, height, weight), count) in enumerate(items):
|
||||
for ii in range(count):
|
||||
packer.add_item(f"Item {i}_{ii}", width, height, length, weight)
|
||||
|
||||
return packer
|
||||
|
||||
|
||||
def make_doc():
|
||||
doc = ezdxf.new()
|
||||
doc.layers.add("FRAME", color=colors.YELLOW)
|
||||
doc.layers.add("ITEMS")
|
||||
doc.layers.add("TEXT")
|
||||
return doc
|
||||
|
||||
|
||||
def main() -> None:
|
||||
print("Container-Auslastungsrechner")
|
||||
items: List[Tuple[Tuple[float, float, float, float], int]] = []
|
||||
|
||||
more_items: bool = True
|
||||
while more_items:
|
||||
print("\nNeues Packstück hinzufügen:")
|
||||
length = float_input("- Länge (cm)")
|
||||
width = float_input("- Breite (cm)")
|
||||
height = float_input("- Höhe (cm)")
|
||||
weight = float_input("- Gewicht (kg)")
|
||||
count = int_input("- Packstückzahl")
|
||||
|
||||
items += [((length, width, height, weight), count)]
|
||||
|
||||
more_items = yes_no_input("Weitere Packstücke hinzufügen?")
|
||||
|
||||
for name, length, width, height, capacity in CONTAINERS:
|
||||
print(
|
||||
f"Teste {name:>5} Container ({length / 100:>5.2f}m x {width / 100:>5.2f}m x {height / 100:>5.2f}m mit {capacity / 1000:>5.2f}t)..."
|
||||
)
|
||||
|
||||
packer: bp.Packer = build_packer(items)
|
||||
packer.add_bin(name, width, height, length, capacity)
|
||||
packer.pack(bp.PickStrategy.BIGGER_FIRST)
|
||||
|
||||
if len(packer.unfitted_items) > 0:
|
||||
print(f"{name} Container ist zu klein!")
|
||||
break
|
||||
|
||||
bins: List[bp.Bin] = []
|
||||
bins.extend(packer.bins)
|
||||
|
||||
doc = make_doc()
|
||||
bp.export_dxf(doc.modelspace(), bins, offset=(0, 20, 0))
|
||||
# doc.saveas("packing.dxf")
|
||||
print(
|
||||
f"{name} Container passt: Zu {packer.get_fill_ratio() * 100:.2f}% gefüllt ({packer.get_total_weight():.2f}kg)"
|
||||
)
|
||||
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(111, projection="3d")
|
||||
|
||||
# Container bounding box
|
||||
ax.bar3d(0, 0, 0, length, width, height, alpha=0.1, color="gray")
|
||||
|
||||
# Package bounding boxes
|
||||
for it in packer.bins[0].items:
|
||||
x, y, z = it.position
|
||||
w, h, l = it.width, it.height, it.depth
|
||||
ax.bar3d(x, y, z, l, w, h, color="red")
|
||||
|
||||
plt.title(
|
||||
f"{name} Container mit {packer.get_fill_ratio() * 100:.2f}% Auslastung"
|
||||
)
|
||||
plt.autoscale()
|
||||
plt.show()
|
||||
|
||||
return
|
||||
|
||||
print("Konnte keinen passenden Container ermitteln!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Reference in New Issue
Block a user