Mathematical Foundations of TITAN PACK — 2D Strip Packing & MILP Linearization
Chapter 011. What is the 2D Packing Problem?
The 2D Packing Problem is a combinatorial optimization challenge where rectangular parts of various dimensions must be placed inside a bounding sheet or plate without overlapping or exceeding boundary limits. It has extensive real-world applications in sheet-metal manufacturing, woodworking, cutting-stock layouts, and VLSI floorplanning.
Deciding whether a set of items can be packed with zero scrap (100% material efficiency) without void is known to be strongly NP-hard.
Chapter 022. Linearizing Geometric Non-Overlap Constraints (Big-M Formulation)
The condition that two rectangles $i$ and $j$ (placed at $(x_i, y_i)$ with dimensions $w_i \times h_i$) do not overlap is inherently disjunctive (connected by logical OR):
In Mixed-Integer Linear Programming (MILP), we linearize this disjunction using four binary indicator variables $\delta_{ij,1}, \delta_{ij,2}, \delta_{ij,3}, \delta_{ij,4} \in \{0, 1\}$ and tight axis-specific constants derived from plate dimensions $W, H$:
Chapter 033. Geometric Guarantees for Fault-Line-Free Layouts
If a rectangle is naively sliced with full-width or full-height cuts, continuous fault lines (guillotine cuts) emerge, degrading structural rigidity and breaking the puzzle into trivial independent sub-puzzles.
TITAN PACK uses a Multi-Scale Interlocking & Core Partitioning algorithm that guarantees every interior grid line $c \in \{1, \dots, W-1\}$ and $r \in \{1, \dots, H-1\}$ is crossed by at least one overlapping metal girder ($x_i < c < x_i + w_i$), ensuring an interlocking puzzle with zero continuous fault lines (furthermore, when identical parts exist, adding symmetry-breaking constraints like $x_i \le x_j$ eliminates redundant permutation branches in solver search).
Chapter 044. Implementation with WaSHI
Here is how to formulate and solve this 2D packing model using the @ohtaman/washi TypeScript mathematical modeling library:
import { Model, expr } from "@ohtaman/washi";
const model = new Model("TitanPacking2D");
// 1. Continuous coordinate variables for each part
const x = parts.map((p, i) => model.continuous(`x_${i}`, { lb: 0, ub: plateW - p.w }));
const y = parts.map((p, i) => model.continuous(`y_${i}`, { lb: 0, ub: plateH - p.h }));
// 2. Pairwise non-overlap constraints with axis-specific tight Big-M (plateW, plateH)
for (let i = 0; i < parts.length; i++) {
for (let j = i + 1; j < parts.length; j++) {
const d1 = model.binary(`d_${i}_${j}_left`);
const d2 = model.binary(`d_${i}_${j}_right`);
const d3 = model.binary(`d_${i}_${j}_below`);
const d4 = model.binary(`d_${i}_${j}_above`);
model.constraint(expr`${x[i]} + ${parts[i].w} - ${x[j]} + ${plateW} * ${d1} <= ${plateW}`);
model.constraint(expr`${x[j]} + ${parts[j].w} - ${x[i]} + ${plateW} * ${d2} <= ${plateW}`);
model.constraint(expr`${y[i]} + ${parts[i].h} - ${y[j]} + ${plateH} * ${d3} <= ${plateH}`);
model.constraint(expr`${y[j]} + ${parts[j].h} - ${y[i]} + ${plateH} * ${d4} <= ${plateH}`);
model.constraint(expr`${d1} + ${d2} + ${d3} + ${d4} >= 1`);
}
}
const status = await model.solve({ solver: "highs" });
console.log("Status:", status);