from ortools.sat.python import cp_model
from shapely.geometry import Polygon
from ortools.sat.python.cp_model import IntVar, IntervalVar
class Packing2D:
def __init__(self, bin_polygon: Polygon, items: list[Polygon]):
self.bin_polygon = bin_polygon
self.items = items
self.model = cp_model.CpModel()
self.solver = cp_model.CpSolver()
self.x: list[IntVar] = []
self.y: list[IntVar] = []
self.create_variables()
self.add_constraints()
def create_variables(self):
for i, item in enumerate(self.items):
min_x, min_y, max_x, max_y = item.bounds
x = self.model.new_int_var(int(min_x), int(self.bin_polygon.bounds[2] - (max_x - min_x)), f'x_{i}')
y = self.model.new_int_var(int(min_y), int(self.bin_polygon.bounds[3] - (max_y - min_y)), f'y_{i}')
self.x.append(x)
self.y.append(y)
def add_within_bin_constraint(self, i: int):
...
def add_constraints(self):
x_intervals: list[IntervalVar] = []
y_intervals: list[IntervalVar] = []
for i, item in enumerate(self.items):
min_x, min_y, max_x, max_y = item.bounds
x_interval = self.model.new_fixed_size_interval_var(self.x[i], int(max_x - min_x), f'x_interval_{i}')
y_interval = self.model.new_fixed_size_interval_var(self.y[i], int(max_y - min_y), f'y_interval_{i}')
x_intervals.append(x_interval)
y_intervals.append(y_interval)
self.model.add_no_overlap_2d(x_intervals, y_intervals)
for i in range(len(self.items)):
self.add_within_bin_constraint(i)
def solve(self) -> list[tuple[int, int]] | None:
status = self.solver.Solve(self.model)
if status == cp_model.OPTIMAL or status == cp_model.FEASIBLE:
solution = [(self.solver.Value(self.x[i]), self.solver.Value(self.y[i])) for i in range(len(self.items))]
return solution
return None