enzoruiz / 3dbinpacking

A python library for 3D Bin Packing
MIT License
346 stars 87 forks source link

When reusing bins and items in a packer old data persists #33

Open Kyle772 opened 2 years ago

Kyle772 commented 2 years ago

This isn't so much of an issue moreso a problem I ran into which took some time to debug.

Since the three objects are built out as standalone classes bins keep their mutated data between runs. To work around this I had to manually reset the data back to the init values on every loop in each bin. This isn't really an issue with the code but more an issue with the implementation/design. If a new packer is initialized and bins are added those bins should be reinitialized as well.

More of a PSA I suppose since nobody in the gh issues brought this up (Except maybe https://github.com/enzoruiz/3dbinpacking/issues/15 ?)

deydist commented 1 year ago

I am experiencing this same strange "issue". What is the best way to manually reset the data?

Johetan commented 1 year ago

I ran into the same issue, trying to use it for the repo for simplicity. How would i go about that in the loop?

deydist commented 1 year ago

@Kyle772 Can you share your method for resetting the data back to the init values? In my implementation I am having to use 3 different Bin lists but would like to standardize to one.

Kyle772 commented 1 year ago

Sorry for the late response I needed to hunt down the code for this and kept putting it off.

If you persist the packer between different bins it will maintain the values.

To avoid this what I ended up doing was reinitializing a new packer and bins with each request/cycle. You could also manually go through the init variables (found in main.py Packer.init()) and reset them manually. I don't have a huge data set so recreating the classes wasn't an issue for me but if you need the original set you could deep copy it into a variable and pull values from there on reset.

If you're still having troubles consider reading through this article, it's not immediately relevant to this library but may give you the insight as to why this needs to happen https://realpython.com/python-pass-by-reference/

Johetan commented 1 year ago

Hi, thank you for the reply! This worked for me. Had to initialize every object inside the loop. Items, Bins and Backer. I'll post the snippet here later on.

deydist commented 1 year ago

Thanks to both of you! I'm curious how your solution looks Johetan. I'm currently reinitializing the list of Bins in each function... however ideally I want to have just 1 list of bins that reset so I can use that to manage changes when box sizes change.

Johetan commented 1 year ago

It's a bit rough


import os
import trimesh
import pandas as pd
import py3dbp as packing
from typing import Tuple

# Define the folder path containing the STL files
cwd = os.getcwd()
folder_path = os.path.join(cwd, "DataFolder")
# Init Dataframe containing the bbox data

# Bedinung einbauen, bin muss größer sein als größte dimension einzelteil

#define the size of the box in mm
max_x_dim = 150
max_y_dim = 150
max_z_dim = 120
max_weight = 1

class Drawable_Box:
    def __init__(self, depth, height, width, name, items):
        self.depth = depth
        self.height = height
        self.width = width
        self.name = name
        self.items = items

class Drawable_Item:
    def __init__(self, depth, height, width, x_pos, y_pos, z_pos, name):
        self.depth = depth
        self.height = height
        self.width = width
        self.x_pos = x_pos
        self.y_pos = y_pos
        self.z_pos = z_pos
        self.name = name

def get_bounding_box_dimensions(mesh_data: trimesh.Trimesh)-> pd.DataFrame:
    x_min, y_min, z_min = mesh_data.bounds[0]
    x_max, y_max, z_max = mesh_data.bounds[1]
    x_size, y_size, z_size = x_max - x_min, y_max - y_min, z_max - z_min
    return x_size, y_size, z_size

def create_packing_items(dataframe_items: pd.DataFrame) -> list:
    return [packing.Item(row["part_id"], row["x_size"], row["z_size"], row["y_size"], 0) for index, row in dataframe_items.iterrows()]

def import_files(folder_path: str) -> pd.DataFrame:    
    results_df = pd.DataFrame(columns=["part_id", "x_size", "y_size", "z_size"])
    for filename in os.listdir(folder_path):
        if filename.endswith(".stl"):
            part_id = os.path.splitext(filename)[0]
            mesh_data = trimesh.load(os.path.join(folder_path, filename))
            x_size, y_size, z_size = get_bounding_box_dimensions(mesh_data)
            series = pd.Series({"part_id": part_id, "x_size": x_size, "y_size": y_size, "z_size": z_size})
            results_df = pd.concat([results_df, series.to_frame().T], ignore_index=True)
            results_df = results_df.sort_values(by=["x_size", "y_size", "z_size"])
    return results_df

def pack_it(item_list: list) -> Tuple[list, list]:
    standart_bin = packing.Bin("printerbin", max_x_dim, max_y_dim, max_z_dim, max_weight)
    packer = packing.Packer()
    not_fitted = []
    not_fitted.clear()
    for item in item_list:
        packer.add_item(item)
    packer.add_bin(standart_bin)
    packer.pack(bigger_first=True)
    for b in packer.bins:
        not_fitted = b.unfitted_items
    return packer.bins, not_fitted

def pack_list_from_unfit(not_fitted_items_list: list) -> list:
    new_list = []
    new_list.clear()
    for parts in not_fitted_items_list:
        item = packing.Item(parts.name, parts.width, parts.height, parts.depth, parts.weight)
        new_list.append(item)
        new_list
    return new_list

results_df = import_files(folder_path)
item_list = create_packing_items(results_df)
print(type(item_list))
packed_containers = []
remaining_items_list = [1]
drawable_bins = []

while len(remaining_items_list) > 0:
    print("Packing")
    container, remaining_items_list = pack_it(item_list)
    print("Bin done")
    item_list = pack_list_from_unfit(remaining_items_list)
    print("Remaining Items")
    packed_containers.append(container)

for objects in packed_containers:
    print("::::: A Container ::::::")
    for single_bin in objects:
        print(single_bin.string())
        # drawable_bins.append(single_bin)
        print("FITTED ITEMS:")
        for item in single_bin.items:
            print("====> ", item.string())
        print("UNFITTED ITEMS:")
        for item in single_bin.unfitted_items:
            print("====> ", item.string())

# for bins in packed_containers:
#     box = Drawable_Box(bins[0].depth, bins[0].height, bins[0].width, bins[0].name, bins[0].items )
#     drawable_bins.append(box)

# for drawings in drawable_bins:
#     for items in drawings.items:
#         print(items.depth)
#         print(items.height)
#         print(items.width)
#         print(float(items.position[0]))
#         print(float(items.position[1]))
#         print(float(items.position[2]))
#         print(items.name)    

# for i, filename in enumerate(os.listdir(folder_path)):
#     if filename.endswith(".stl"):  
#         print(filename)
#         os.rename(folder_path + "\\" + filename, folder_path + "\\" + str(i)+ ".stl")