fix: correct Size width/length ordering and add CLI docs to README

Size.Parse and ToString now use WxL format (width first) matching the
natural convention. Fixed the Plate(w,l) constructor which was swapping
args when creating Size. Fixed PlateView.DrawPlate and DrawControl
ZoomToArea which had width/length mapped to the wrong screen axes.

Simplified Console --size parsing to use Size.TryParse instead of manual
split with confusing PlateHeight/PlateWidth fields. Added Command-Line
Interface section to README documenting all console options.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-17 10:29:13 -04:00
parent 21a16e5b7c
commit d4222db0e8
7 changed files with 72 additions and 37 deletions

View File

@@ -96,12 +96,8 @@ static class NestConsole
o.Spacing = double.Parse(args[++i]);
break;
case "--size" when i + 1 < args.Length:
var parts = args[++i].Split('x');
if (parts.Length == 2)
{
o.PlateHeight = double.Parse(parts[0]);
o.PlateWidth = double.Parse(parts[1]);
}
if (Size.TryParse(args[++i], out var sz))
o.PlateSize = sz;
break;
case "--check-overlaps":
o.CheckOverlaps = true;
@@ -198,14 +194,14 @@ static class NestConsole
return null;
}
if (!options.PlateWidth.HasValue || !options.PlateHeight.HasValue)
if (!options.PlateSize.HasValue)
{
Console.Error.WriteLine("Error: --size WxH is required when importing DXF files without a nest");
Console.Error.WriteLine("Error: --size WxL is required when importing DXF files without a nest");
return null;
}
var newNest = new Nest { Name = "DXF Import" };
var plate = new Plate { Size = new Size(options.PlateWidth.Value, options.PlateHeight.Value) };
var plate = new Plate { Size = options.PlateSize.Value };
newNest.Plates.Add(plate);
foreach (var dxf in dxfFiles)
@@ -278,8 +274,8 @@ static class NestConsole
// Only apply size override when it wasn't already used to create the plate.
var hasDxfOnly = !options.InputFiles.Any(f => f.EndsWith(".zip", StringComparison.OrdinalIgnoreCase));
if (options.PlateWidth.HasValue && options.PlateHeight.HasValue && !hasDxfOnly)
plate.Size = new Size(options.PlateWidth.Value, options.PlateHeight.Value);
if (options.PlateSize.HasValue && !hasDxfOnly)
plate.Size = options.PlateSize.Value;
}
static Drawing ResolveDrawing(Nest nest, Options options)
@@ -302,7 +298,7 @@ static class NestConsole
{
Console.WriteLine($"Nest: {nest.Name}");
var wa = plate.WorkArea();
Console.WriteLine($"Plate: {options.PlateIndex} ({plate.Size.Length:F1} x {plate.Size.Width:F1}), spacing={plate.PartSpacing:F2}, edge=({plate.EdgeSpacing.Left},{plate.EdgeSpacing.Bottom},{plate.EdgeSpacing.Right},{plate.EdgeSpacing.Top}), workArea={wa.Length:F1}x{wa.Width:F1}");
Console.WriteLine($"Plate: {options.PlateIndex} ({plate.Size.Width:F1} x {plate.Size.Length:F1}), spacing={plate.PartSpacing:F2}, edge=({plate.EdgeSpacing.Left},{plate.EdgeSpacing.Bottom},{plate.EdgeSpacing.Right},{plate.EdgeSpacing.Top}), workArea={wa.Width:F1}x{wa.Length:F1}");
Console.WriteLine($"Drawing: {drawing.Name}");
Console.WriteLine(options.KeepParts
? $"Keeping {existingCount} existing parts"
@@ -392,7 +388,7 @@ static class NestConsole
Console.Error.WriteLine();
Console.Error.WriteLine("Modes:");
Console.Error.WriteLine(" <nest.zip> Load nest and fill (existing behavior)");
Console.Error.WriteLine(" <part.dxf> --size LxW Import DXF, create plate, and fill");
Console.Error.WriteLine(" <part.dxf> --size WxL Import DXF, create plate, and fill");
Console.Error.WriteLine(" <nest.zip> <part.dxf> Load nest and add imported DXF drawings");
Console.Error.WriteLine();
Console.Error.WriteLine("Options:");
@@ -400,7 +396,7 @@ static class NestConsole
Console.Error.WriteLine(" --plate <index> Plate index to fill (default: 0)");
Console.Error.WriteLine(" --quantity <n> Max parts to place (default: 0 = unlimited)");
Console.Error.WriteLine(" --spacing <value> Override part spacing");
Console.Error.WriteLine(" --size <LxW> Override plate size (e.g. 120x60); required for DXF-only mode");
Console.Error.WriteLine(" --size <WxL> Override plate size (e.g. 60x120); required for DXF-only mode");
Console.Error.WriteLine(" --output <path> Output nest file path (default: <input>-result.zip)");
Console.Error.WriteLine(" --template <path> Nest template for plate defaults (thickness, quadrant, material, spacing)");
Console.Error.WriteLine(" --autonest Use NFP-based mixed-part autonesting instead of linear fill");
@@ -419,8 +415,7 @@ static class NestConsole
public string OutputFile;
public int Quantity;
public double? Spacing;
public double? PlateWidth;
public double? PlateHeight;
public Size? PlateSize;
public bool CheckOverlaps;
public bool NoSave;
public bool NoLog;

View File

@@ -6,14 +6,14 @@ namespace OpenNest.Geometry
{
public Size(double width, double length)
{
Length = length;
Width = width;
Length = length;
}
public double Length;
public double Width;
public double Length;
public static Size Parse(string size)
{
var a = size.ToUpper().Split('X');
@@ -21,8 +21,8 @@ namespace OpenNest.Geometry
if (a.Length > 2)
throw new FormatException("Invalid size format.");
var length = double.Parse(a[0]);
var width = double.Parse(a[1]);
var width = double.Parse(a[0]);
var length = double.Parse(a[1]);
return new Size(width, length);
}
@@ -42,14 +42,8 @@ namespace OpenNest.Geometry
return true;
}
public override string ToString()
{
return string.Format("{0} x {1}", Length, Width);
}
public string ToString(int decimalPlaces)
{
return string.Format("{0} x {1}", System.Math.Round(Length, decimalPlaces), System.Math.Round(Width, decimalPlaces));
}
public override string ToString() => $"{Width} x {Length}";
public string ToString(int decimalPlaces) => $"{System.Math.Round(Width, decimalPlaces)} x {System.Math.Round(Length, decimalPlaces)}";
}
}

View File

@@ -35,7 +35,7 @@ namespace OpenNest
}
public Plate(double width, double length)
: this(new Size(length, width))
: this(new Size(width, length))
{
}

View File

@@ -205,7 +205,7 @@ namespace OpenNest.Controls
public virtual void ZoomToArea(Box box, bool redraw = true)
{
ZoomToArea(box.X, box.Y, box.Width, box.Length, redraw);
ZoomToArea(box.X, box.Y, box.Length, box.Width, redraw);
}
public virtual void ZoomToArea(double x, double y, double width, double height, bool redraw = true)

View File

@@ -415,14 +415,14 @@ namespace OpenNest.Controls
{
var plateRect = new RectangleF
{
Width = LengthWorldToGui(Plate.Size.Width),
Height = LengthWorldToGui(Plate.Size.Length)
Width = LengthWorldToGui(Plate.Size.Length),
Height = LengthWorldToGui(Plate.Size.Width)
};
var edgeSpacingRect = new RectangleF
{
Width = LengthWorldToGui(Plate.Size.Width - Plate.EdgeSpacing.Left - Plate.EdgeSpacing.Right),
Height = LengthWorldToGui(Plate.Size.Length - Plate.EdgeSpacing.Top - Plate.EdgeSpacing.Bottom)
Width = LengthWorldToGui(Plate.Size.Length - Plate.EdgeSpacing.Left - Plate.EdgeSpacing.Right),
Height = LengthWorldToGui(Plate.Size.Width - Plate.EdgeSpacing.Top - Plate.EdgeSpacing.Bottom)
};
switch (Plate.Quadrant)

View File

@@ -53,6 +53,51 @@ Or open `OpenNest.sln` in Visual Studio and run the `OpenNest` project.
<!-- TODO: Add screenshots for each step -->
## Command-Line Interface
OpenNest includes a CLI for batch nesting without the GUI — useful for automation, scripting, and CI pipelines.
```bash
dotnet run --project OpenNest.Console/OpenNest.Console.csproj -- <input-files> [options]
```
**Import DXF files and nest onto a plate:**
```bash
# Import a DXF and fill a 60x120 plate
dotnet run --project OpenNest.Console/OpenNest.Console.csproj -- part.dxf --size 60x120
# Import multiple DXFs with NFP-based auto-nesting
dotnet run --project OpenNest.Console/OpenNest.Console.csproj -- part1.dxf part2.dxf --size 60x120 --autonest
```
**Work with existing nest files:**
```bash
# Re-fill an existing nest file
dotnet run --project OpenNest.Console/OpenNest.Console.csproj -- project.zip
# Add a new DXF to an existing nest and auto-nest
dotnet run --project OpenNest.Console/OpenNest.Console.csproj -- project.zip extra-part.dxf --autonest
```
**Options:**
| Option | Description |
|--------|-------------|
| `--size <WxL>` | Plate size (e.g. `60x120`). Required for DXF-only mode. |
| `--autonest` | Use NFP-based mixed-part nesting instead of linear fill |
| `--drawing <name>` | Select which drawing to fill with (default: first) |
| `--quantity <n>` | Max parts to place (default: unlimited) |
| `--spacing <value>` | Override part spacing |
| `--template <path>` | Load plate defaults (thickness, quadrant, material, spacing) from a nest file |
| `--output <path>` | Output file path (default: `<input>-result.zip`) |
| `--keep-parts` | Keep existing parts instead of clearing before fill |
| `--check-overlaps` | Run overlap detection after fill (exits with code 1 if found) |
| `--engine <name>` | Select a registered nesting engine |
| `--no-save` | Skip saving the output file |
| `--no-log` | Skip writing the debug log |
## Project Structure
```
@@ -68,11 +113,12 @@ OpenNest.sln
└── OpenNest.Tests/ # Unit tests
```
For most users, only the first four matter:
For most users, only these matter:
| Project | What it does |
|---------|-------------|
| **OpenNest** | The app you run. WinForms UI with plate viewer, drawing list, and dialogs. |
| **OpenNest.Console** | Command-line interface for batch nesting, scripting, and automation. |
| **OpenNest.Core** | The building blocks — parts, plates, drawings, geometry, G-code representation. |
| **OpenNest.Engine** | The brains — algorithms that decide where parts go on a plate. |
| **OpenNest.IO** | Reads and writes files — DXF (via ACadSharp), G-code, and the `.nest` ZIP format. |

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 55 KiB