2D Irregular Nesting
No-Fit Polygon (NFP for 2D Nesting): In laser cutting, sheet metal stamping, and CNC fabrication, minimizing scrap material requires packing irregular, non-convex parts tightly. The No-Fit Polygon (NFP) represents the exact locus of all positions where an orbiting polygon $B$ touches a stationary polygon $A$ without intersecting. If the reference origin of $B$ lies outside the NFP, collision is mathematically impossible.
NFP Orbital Invariant:
1import numpy as np23def compute_nfp_minkowski(poly_a, poly_b):4 """5 Computes No-Fit Polygon (NFP) between stationary polygon A and orbiting polygon B.6 NFP_AB = A Minkowski_Sum (-B).7 If reference point of B is:8 - Strictly inside NFP: A and B overlap (collision).9 - On boundary of NFP: A and B touch without overlapping.10 - Outside NFP: A and B are completely disjoint.11 """12 neg_b = -np.array(poly_b)13 # Minkowski sum of A and (-B)14 # In practice, computed via orbital sliding trace or slope diagram convolution15 return "NFP Polygon Boundary"
Bottom-Left-Fill (BLF) Sheet Nesting: 2D strip packing is strongly NP-hard. In production, heuristic algorithms place parts sequentially, dropping each piece to the lowest available vertical level (minimum $Y$) and shifting it as far left as possible (minimum $X$). Combined with genetic angle permutation, this packs sheet stock to over 85% material utilization efficiency.
BLF Heuristic Rules:
1def bottom_left_fill_nesting(parts, sheet_width, sheet_height):2 """3 Bottom-Left-Fill (BLF) nesting heuristic.4 Places each incoming part at the lowest possible Y coordinate,5 then slides it as far left (minimum X) as possible without colliding with prior parts.6 """7 placed_parts = []89 for part in parts:10 best_pos = None11 best_y = float('inf')12 best_x = float('inf')1314 # Search candidate positions on sheet15 for candidate_pos in generate_candidate_positions(placed_parts, sheet_width, sheet_height):16 if not overlaps_any(part, candidate_pos, placed_parts):17 cx, cy = candidate_pos18 if (cy < best_y) or (cy == best_y and cx < best_x):19 best_y = cy20 best_x = cx21 best_pos = candidate_pos2223 if best_pos:24 placed_parts.append(part.translate(best_pos))2526 return placed_parts