MCP fill_remnants gets 7 parts vs UI Ctrl+F gets 9 in the same strip. Documents root cause analysis and files to investigate. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
3.4 KiB
Remnant Fill Optimization Investigation
Problem
When filling remnant strips on a partially-nested plate, NestEngine.Fill(NestItem, Box) produces fewer parts than the UI's Ctrl+F fill. On a test case (N0308-008.zip, 36x36 plate, "Converto 3 YRD DUMPERSTER HINGE PLATE #2" 5.89x3.39):
- MCP fill_remnants: 7 parts in the right-side strip
- UI Ctrl+F (ActionClone.Fill): 9 parts in the same strip
Test Setup
Load C:/Users/AJ/Desktop/N0308-008.zip — 75 parts on plate 0, 5 columns of 15. The remnant strip is at (31.0, 0.8) 4.7x35.0.
Use the OpenNest MCP tools to reproduce:
load_nest("C:/Users/AJ/Desktop/N0308-008.zip")
get_plate_info(0) → should show 75 parts, remnant at (31.0, 0.8) 4.7x35.0
fill_remnants(0, "Converto 3 YRD DUMPERSTER HINGE PLATE #2") → gets 7 parts
Root Cause Analysis
The MCP's fill_remnants calls NestEngine.Fill(NestItem, Box) which:
- Tries
FillLinearwith best rotation + 90° (and angle sweep if strip is narrow) - Tries
FillRectangleBestFit(mixes horizontal/vertical in bin packing) - Tries
FillWithPairs(paired part combinations viaBestFitCache) - Picks the best result
The UI's ActionClone.Fill() (OpenNest/Actions/ActionClone.cs:171-201) does something different:
- Gets
Helper.GetLargestBoxVertically(pt, bounds, boxes)andGetLargestBoxHorizontally(pt, bounds, boxes)from the cursor position - Picks the largest area
- Calls
NestEngine.Fill(groupParts, bestArea)— note: passesList<Part>notNestItem
The Fill(List<Part>, Box) overload uses RotationAnalysis.FindHullEdgeAngles(groupParts) which may produce different/better rotation candidates than Fill(NestItem, Box) which uses RotationAnalysis.FindBestRotation(item).
Key Differences to Investigate
1. Rotation candidate generation
Fill(NestItem, Box)usesFindBestRotation→ best angle + 90° + optional sweepFill(List<Part>, Box)usesFindHullEdgeAngles→ edge angles from convex hull- The hull edge angles may include rotation angles that pack better in the narrow strip
2. Region selection
- UI uses cursor position to find the largest obstacle-free rectangle via
GetLargestBoxVertically/GetLargestBoxHorizontally - MCP uses
Plate.GetRemnants()which returns edge strips from global part boundaries - The UI region may differ in exact bounds from the remnant strip
3. Part grouping
- UI's
ActionClonecan pass multi-part groups toFill(List<Part>, Box), enabling pattern-based tiling - MCP passes single
NestItemtoFill(NestItem, Box)
Files to Read
OpenNest.Engine/NestEngine.cs— bothFilloverloads,FillWithPairs,FillPatternOpenNest/Actions/ActionClone.cs:171-201— the UI fill pathOpenNest.Engine/BestFit/RotationAnalysis.cs—FindBestRotationvsFindHullEdgeAnglesOpenNest.Engine/FillLinear.cs— the linear tiling engineOpenNest.Core/Helper.cs:1098+—GetLargestBoxVertically/GetLargestBoxHorizontally
Possible Fixes
- Use hull edge angles in the NestItem overload — merge rotation candidates from both
FindBestRotationandFindHullEdgeAngles - Improve GetRemnants — instead of global edge strips, scan per-column to find the actual free space shape
- Add a smarter fill_remnants — have the MCP tool use
GetLargestBox*helpers to find free rectangles from multiple scan points, similar to how the UI does it