Blog · Nesting · 1 Oct 2026 · 3 min read
How to nest cut parts on steel sheets with Python, from DXF profiles
Our nesting for 12000 x 2500 plates is a short Python program, not a commercial package. Profiles become polygons, polygons become rasters, and an FFT finds every free position at once. The method, the yield it gave against bounding boxes, and the bugs that showed 107% yield or overlapping parts with no error.
Short answer: turn each closed profile into a polygon (Shapely), rasterise it at 5 mm, dilate it by half the gap, and for each orientation compute the overlap with the sheet at every position in one shot with an FFT convolution (SciPy). Among the free positions, take the one that extends the sheet least. Use float64. On 10 mm plate it used 20.9% less sheet length than nesting bounding boxes.
Where the profiles come from
From the delivery booklet cells, not from the drawings: there the geometry is already 1:1 with one part per cell. Taking profiles from the drawings got 28 of 115 profiles wrong, 6 of them by picking the isometric view.
Marking lines (engraved codes) are drawn on the nest but kept out of the collision.
The method
- Each closed contour becomes a polygon (
shapely.ops.polygonize,unary_unionfor parts with several faces). - The polygon is rasterised into a boolean mask and dilated by half the gap (
scipy.ndimage.binary_dilation). - Orientations: 0°, 90°, 180°, 270°, plus the angle of the minimum bounding rectangle.
- For each orientation,
fftconvolvegives the overlap at every position at once. - Free positions are those with overlap below 0.5. Choose the one that extends the used sheet length least; on a tie, the lowest.
lim = max(mw, min(W, frontier + mw + 2))
conv = fftconvolve(occupied[:, :lim], m[::-1, ::-1].astype(np.float64), mode="valid")
free = np.argwhere(conv < 0.5)
if free.size == 0:
continue
# least extension of the sheet, then lowest: plain bottom-left on a
# 12000 x 2500 sheet fills by rows and leaves a ragged frontier
order = np.lexsort((free[:, 0], free[:, 1] + mw))
j, i = free[order[0]]
cost = (int(i) + mw, int(j))
if best is None or cost < best[0]:
best = (cost, angle, m, int(i), int(j))
Sheet 12000 × 2500, 10 mm border not cuttable, 10 mm minimum gap between parts. The usable height is 2470: 2500 minus 10 border per side, minus 5 dilation per side.
Yield, measured
10 mm plate on 12000 × 2500 sheets, total sheet length used:
| Method | Length | Saving |
|---|---|---|
| Bounding boxes | 33.9 m | — |
| Minimum rectangles | 31.3 m | 7.8% |
| Paired twins | 29.6 m | 12.7% |
| True shape, 5 mm raster | 26.8 m | 20.9% (197 s) |
Count metres, not sheets: the last sheet is almost always half empty. If sheets are already ordered, the leftover is an offcut back to stock.
Bugs that looked like results
| Bug | Symptom | Fix |
|---|---|---|
| FFT in float32 | Yield 107% | float64 |
| One sample per raster row | Parts overlap | 3 samples per row |
binary_dilation on an unpadded array |
Dilation cut at the edge | Pad first |
| Full gap as dilation radius | True shape lost to bounding boxes | Half the gap per part |
| Bottom-left fill | Ragged frontier on a long sheet | Least extension of the sheet |
| Cap on parts per sheet raised | 339 parts on one sheet at 65% yield: overlapping, no error | The cap is a bug guard, not a tuning knob |
| Engraved text rotated twice | ezdxf 1.4.4 transform() already rotates. The PNG cannot show it |
Check: output rotation = source + part angle |
| Micro-gap in a contour (0.1 mm) | Falls back to bounding box | Close small gaps before polygonising |
Acceptance checks
- 0 overlaps, 0 parts outside the sheet.
- Engraved code count equals the expected count. Which parts get a code is listed by a person: the automatic guess flagged 23, the real number was 1.
- Entity balance: everything in, nothing extra.
- ezdxf's audit is not a test: open the file in CAD.
Do and don't
Do
- Take profiles from 1:1 cells, one part each.
- Use float64 and a 5 mm raster.
- Measure yield in metres.
- Check overlaps independently of the nesting code.
Don't
- Don't nest from drawing views.
- Don't use bottom-left fill on long sheets.
- Don't raise safety caps to get a better number.
Questions people ask
Why not a commercial nesting program? Use one if you have it. This shows an AI agent can build a working nest for plate when the job needs it, and the checks are the same either way.
Does the gap include the kerf? Our code has a 10 mm minimum gap and no separate kerf value. Set the gap your cutting process needs.