From 360c7136dd98ec62853c72aefde593c6658523b9 Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Wed, 28 Oct 2026 22:16:36 -0400 Subject: [PATCH] feat(console): --outline makes the target plate an irregular sheet Co-Authored-By: Claude Fable 5.1 --- OpenNest.Console/Program.cs | 30 ++++++++++++++++++++++++++++-- docs/automatic-nesting.md | 2 ++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/OpenNest.Console/Program.cs b/OpenNest.Console/Program.cs index bc5a4a9a..ab82a3f8 100644 --- a/OpenNest.Console/Program.cs +++ b/OpenNest.Console/Program.cs @@ -132,14 +132,26 @@ static class NestConsole if (options.NoFill && (options.AutoNest || options.KeepParts || options.DrawingName != null || options.Quantity != 0 || options.Spacing.HasValue || options.PlateSize.HasValue - || options.TemplateFile != null || options.InputFiles.Any(IsCadFile))) + || options.OutlineFile != null || options.TemplateFile != null || options.InputFiles.Any(IsCadFile))) { Console.Error.WriteLine( - "Error: --no-fill keeps the nest as loaded; it cannot be combined with CAD inputs, --autonest, --keep-parts, --drawing, --quantity, --spacing, --size or --template." + "Error: --no-fill keeps the nest as loaded; it cannot be combined with CAD inputs, --autonest, --keep-parts, --drawing, --quantity, --spacing, --size, --outline or --template." ); return 1; } + if (options.OutlineFile != null && !options.AutoNest) + { + Console.Error.WriteLine("Error: --outline needs --autonest; single-plate fill strategies fill rectangles only."); + return 1; + } + + if (options.OutlineFile != null && !File.Exists(options.OutlineFile)) + { + Console.Error.WriteLine($"Error: Outline file not found: {options.OutlineFile}"); + return 1; + } + if (options.InputFiles.Count == 0) { PrintUsage(); @@ -276,6 +288,9 @@ static class NestConsole if (Size.TryParse(args[++i], out var sz)) o.PlateSize = sz; break; + case "--outline" when i + 1 < args.Length: + o.OutlineFile = args[++i]; + break; case "--check-overlaps": o.CheckOverlaps = true; break; @@ -526,6 +541,13 @@ static class NestConsole if (options.PlateSize.HasValue && !hasDxfOnly) plate.Size = options.PlateSize.Value; + + if (options.OutlineFile != null) + { + // The outline is the sheet: its bounds replace any size given. + plate.Outline = SheetOutline.FromGeometry(CadImporter.Import(options.OutlineFile).Entities); + Console.WriteLine($"Outline: {options.OutlineFile} ({plate.Outline})"); + } } static Drawing ResolveDrawing(Nest nest, Options options) @@ -996,6 +1018,9 @@ static class NestConsole --quantity Max parts to place (default: 0 = unlimited) --spacing Override part spacing --size Override plate size (e.g. 60x120); required for DXF-only mode + --outline Make the plate an irregular sheet: the file's largest closed contour is + the sheet, other closed contours are obstacles; sets the size. Only + --autonest with the Irregular (or Default) engine nests on it --output Output nest file path (default: -result.nest) --template Nest template for plate defaults (thickness, quadrant, material, spacing) --autonest Validated whole-job nesting (one sheet unless --max-sheets); replaces only after acceptance @@ -1037,6 +1062,7 @@ static class NestConsole public int Quantity; public double? Spacing; public Size? PlateSize; + public string OutlineFile; public bool CheckOverlaps; public bool NoSave; public bool KeepParts; diff --git a/docs/automatic-nesting.md b/docs/automatic-nesting.md index 2f360a1c..3ee14d0d 100644 --- a/docs/automatic-nesting.md +++ b/docs/automatic-nesting.md @@ -175,6 +175,8 @@ The pre-pipeline multi-plate orchestrator and plate-size optimizer have been rem Console `--autonest` uses the jobs engine named by `--engine` (Default when omitted) against one physical sheet unless `--max-sheets ` allows more. `--stock WxL[:cost],...` offers those sizes instead of the selected plate's size, each unlimited and built from the selected plate's spacing, quadrant and clamp rail; costs are all-or-none and a cost summary is printed for valid results. The selected plate's old parts are replaced by the first layout only after acceptance, further layouts are inserted as new plates right after it (with its grain and cutting parameters), and other plates are unchanged. Default demand is still one of each drawing unless `--quantity` is supplied. A partially fulfilled, valid result may be saved; a successful placement is not a claim that all demand was met. `--check-overlaps` checks every committed plate. +`--outline ` makes the target plate an irregular sheet (see [irregular stock](irregular-stock.md)): the file's largest closed contour is the sheet, other closed contours are obstacles, and the plate's size becomes the outline's bounds. It needs `--autonest` (fill strategies fill rectangles only) and is honored by the Irregular engine, or Default through it; other engines place nothing on the sheet. + Invalid output is printed and rejected with exit code 2 without saving or posting. `--allow-invalid` explicitly accepts representable layout violations. Malformed output, more physical sheets than `--max-sheets`, and zero placements are never saved by this path. `--max-sheets` and `--stock` without `--autonest`, or with malformed values, exit 1 before solving. Unknown engines exit 1. `--autonest --keep-parts` rejects an occupied target: use the plain interactive fill path for existing obstacles instead. MCP `autonest_plate` requires an empty target. The stdio server serializes all tool calls sharing its mutable session, so another request cannot change drawings or occupy a target during a solve. `allow_invalid` defaults to false. It reports violations and makes no changes on rejection, including with an override when the output is unrepresentable or contains multiple sheets. `engine` defaults to Default, and its description lists the built-in engines with what each suits. Fill tools (`fill_plate`, `fill_area`, `fill_remnants`, `pack_plate`) remain separate and default to the Fill strategy. Console and MCP load jobs plug-ins from `Engines/` beside their executable.