fix: correct Width/Length axis mapping and add spiral center-fill
Box constructor and derived properties (Right, Top, Center, Translate, Offset) had Width and Length swapped — Length is X axis, Width is Y axis. Corrected across Core geometry, plate bounding box, rectangle packing, fill algorithms, tests, and UI renderers. Added FillSpiral with center remnant detection and recursive FillBest on the gap between the 4 spiral quadrants. RectFill.FillBest now compares spiral+center vs full best-fit fairly. BestCombination returns a CombinationResult record instead of out params. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -198,9 +198,9 @@ namespace OpenNest.Actions
|
||||
Box cutoffBox;
|
||||
|
||||
if (cutoff.Axis == CutOffAxis.Vertical)
|
||||
cutoffBox = new Box(cutoff.Position.X, plateBounds.Y, 0, plateBounds.Length);
|
||||
cutoffBox = new Box(cutoff.Position.X, plateBounds.Y, 0, plateBounds.Width);
|
||||
else
|
||||
cutoffBox = new Box(plateBounds.X, cutoff.Position.Y, plateBounds.Width, 0);
|
||||
cutoffBox = new Box(plateBounds.X, cutoff.Position.Y, plateBounds.Length, 0);
|
||||
|
||||
boxes.Add(cutoffBox.Offset(plate.PartSpacing));
|
||||
}
|
||||
|
||||
@@ -92,8 +92,8 @@ namespace OpenNest.Actions
|
||||
|
||||
var location = plateView.PointWorldToGraph(SelectedArea.Location);
|
||||
var size = new SizeF(
|
||||
plateView.LengthWorldToGui(SelectedArea.Width),
|
||||
plateView.LengthWorldToGui(SelectedArea.Length));
|
||||
plateView.LengthWorldToGui(SelectedArea.Length),
|
||||
plateView.LengthWorldToGui(SelectedArea.Width));
|
||||
|
||||
var rect = new System.Drawing.RectangleF(location.X, location.Y - size.Height, size.Width, size.Height);
|
||||
|
||||
@@ -176,9 +176,9 @@ namespace OpenNest.Actions
|
||||
Box cutoffBox;
|
||||
|
||||
if (cutoff.Axis == CutOffAxis.Vertical)
|
||||
cutoffBox = new Box(cutoff.Position.X, plateBounds.Y, 0, plateBounds.Length);
|
||||
cutoffBox = new Box(cutoff.Position.X, plateBounds.Y, 0, plateBounds.Width);
|
||||
else
|
||||
cutoffBox = new Box(plateBounds.X, cutoff.Position.Y, plateBounds.Width, 0);
|
||||
cutoffBox = new Box(plateBounds.X, cutoff.Position.Y, plateBounds.Length, 0);
|
||||
|
||||
boxes.Add(cutoffBox.Offset(plateView.Plate.PartSpacing));
|
||||
}
|
||||
|
||||
@@ -206,7 +206,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)
|
||||
|
||||
@@ -190,8 +190,8 @@ namespace OpenNest.Controls
|
||||
var rect = new RectangleF
|
||||
{
|
||||
Location = view.PointWorldToGraph(workArea.Location),
|
||||
Width = view.LengthWorldToGui(workArea.Width),
|
||||
Height = view.LengthWorldToGui(workArea.Length)
|
||||
Width = view.LengthWorldToGui(workArea.Length),
|
||||
Height = view.LengthWorldToGui(workArea.Width)
|
||||
};
|
||||
rect.Y -= rect.Height;
|
||||
|
||||
@@ -226,8 +226,8 @@ namespace OpenNest.Controls
|
||||
{
|
||||
var box = remnants[i];
|
||||
var loc = view.PointWorldToGraph(box.Location);
|
||||
var w = view.LengthWorldToGui(box.Width);
|
||||
var h = view.LengthWorldToGui(box.Length);
|
||||
var w = view.LengthWorldToGui(box.Length);
|
||||
var h = view.LengthWorldToGui(box.Width);
|
||||
var rect = new RectangleF(loc.X, loc.Y - h, w, h);
|
||||
|
||||
var priority = view.DebugRemnantPriorities != null && i < view.DebugRemnantPriorities.Count
|
||||
@@ -355,7 +355,7 @@ namespace OpenNest.Controls
|
||||
var location = part.Location;
|
||||
var pt1 = view.PointWorldToGraph(location);
|
||||
var pt2 = view.PointWorldToGraph(new Vector(
|
||||
location.X + box.Width, location.Y + box.Length));
|
||||
location.X + box.Length, location.Y + box.Width));
|
||||
using var warnPen = new Pen(Color.FromArgb(180, 255, 140, 0), 2f);
|
||||
g.DrawRectangle(warnPen, pt1.X, pt2.Y,
|
||||
System.Math.Abs(pt2.X - pt1.X), System.Math.Abs(pt2.Y - pt1.Y));
|
||||
@@ -542,8 +542,8 @@ namespace OpenNest.Controls
|
||||
var rect = new RectangleF
|
||||
{
|
||||
Location = view.PointWorldToGraph(box.Location),
|
||||
Width = view.LengthWorldToGui(box.Width),
|
||||
Height = view.LengthWorldToGui(box.Length)
|
||||
Width = view.LengthWorldToGui(box.Length),
|
||||
Height = view.LengthWorldToGui(box.Width)
|
||||
};
|
||||
|
||||
g.DrawRectangle(view.ColorScheme.BoundingBoxPen, rect.X, rect.Y - rect.Height, rect.Width, rect.Height);
|
||||
|
||||
@@ -173,13 +173,13 @@ namespace OpenNest.Forms
|
||||
return;
|
||||
}
|
||||
|
||||
if (LeftSpacing + RightSpacing >= size.Width)
|
||||
if (LeftSpacing + RightSpacing >= size.Length)
|
||||
{
|
||||
applyButton.Enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (TopSpacing + BottomSpacing >= size.Length)
|
||||
if (TopSpacing + BottomSpacing >= size.Width)
|
||||
{
|
||||
applyButton.Enabled = false;
|
||||
return;
|
||||
|
||||
@@ -131,13 +131,13 @@ namespace OpenNest.Forms
|
||||
return;
|
||||
}
|
||||
|
||||
if (LeftSpacing + RightSpacing >= size.Width)
|
||||
if (LeftSpacing + RightSpacing >= size.Length)
|
||||
{
|
||||
applyButton.Enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (TopSpacing + BottomSpacing >= size.Length)
|
||||
if (TopSpacing + BottomSpacing >= size.Width)
|
||||
{
|
||||
applyButton.Enabled = false;
|
||||
return;
|
||||
|
||||
18
OpenNest/Forms/OptionsForm.Designer.cs
generated
18
OpenNest/Forms/OptionsForm.Designer.cs
generated
@@ -42,6 +42,7 @@
|
||||
this.saveButton = new System.Windows.Forms.Button();
|
||||
this.cancelButton = new System.Windows.Forms.Button();
|
||||
this.bottomPanel1 = new OpenNest.Controls.BottomPanel();
|
||||
this.strategyGrid = new System.Windows.Forms.DataGridView();
|
||||
this.strategyGroupBox = new System.Windows.Forms.GroupBox();
|
||||
((System.ComponentModel.ISupportInitialize)(this.numericUpDown1)).BeginInit();
|
||||
this.tableLayoutPanel1.SuspendLayout();
|
||||
@@ -212,8 +213,24 @@
|
||||
this.bottomPanel1.Size = new System.Drawing.Size(708, 50);
|
||||
this.bottomPanel1.TabIndex = 1;
|
||||
//
|
||||
// strategyGrid
|
||||
//
|
||||
this.strategyGrid.AllowUserToAddRows = false;
|
||||
this.strategyGrid.AllowUserToDeleteRows = false;
|
||||
this.strategyGrid.AllowUserToResizeRows = false;
|
||||
this.strategyGrid.BackgroundColor = System.Drawing.SystemColors.Window;
|
||||
this.strategyGrid.BorderStyle = System.Windows.Forms.BorderStyle.None;
|
||||
this.strategyGrid.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
|
||||
this.strategyGrid.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.strategyGrid.Location = new System.Drawing.Point(3, 18);
|
||||
this.strategyGrid.Name = "strategyGrid";
|
||||
this.strategyGrid.RowHeadersVisible = false;
|
||||
this.strategyGrid.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect;
|
||||
this.strategyGrid.TabIndex = 0;
|
||||
//
|
||||
// strategyGroupBox
|
||||
//
|
||||
this.strategyGroupBox.Controls.Add(this.strategyGrid);
|
||||
this.strategyGroupBox.Location = new System.Drawing.Point(12, 178);
|
||||
this.strategyGroupBox.Name = "strategyGroupBox";
|
||||
this.strategyGroupBox.Size = new System.Drawing.Size(684, 180);
|
||||
@@ -263,6 +280,7 @@
|
||||
private System.Windows.Forms.TextBox textBox1;
|
||||
private System.Windows.Forms.Label label3;
|
||||
private System.Windows.Forms.Button button1;
|
||||
private System.Windows.Forms.DataGridView strategyGrid;
|
||||
private System.Windows.Forms.GroupBox strategyGroupBox;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using OpenNest.Engine.Strategies;
|
||||
using OpenNest.Engine.Strategies;
|
||||
using OpenNest.Properties;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -9,12 +9,10 @@ namespace OpenNest.Forms
|
||||
{
|
||||
public partial class OptionsForm : Form
|
||||
{
|
||||
private readonly List<CheckBox> _strategyCheckBoxes = new();
|
||||
|
||||
public OptionsForm()
|
||||
{
|
||||
InitializeComponent();
|
||||
BuildStrategyCheckBoxes();
|
||||
BuildStrategyGrid();
|
||||
}
|
||||
|
||||
protected override void OnLoad(EventArgs e)
|
||||
@@ -23,23 +21,44 @@ namespace OpenNest.Forms
|
||||
LoadSettings();
|
||||
}
|
||||
|
||||
private void BuildStrategyCheckBoxes()
|
||||
private void BuildStrategyGrid()
|
||||
{
|
||||
var strategies = FillStrategyRegistry.AllStrategies;
|
||||
var y = 20;
|
||||
strategyGrid.AutoGenerateColumns = false;
|
||||
|
||||
foreach (var strategy in strategies)
|
||||
strategyGrid.Columns.Add(new DataGridViewCheckBoxColumn
|
||||
{
|
||||
var cb = new CheckBox
|
||||
{
|
||||
Text = strategy.Name,
|
||||
Tag = strategy.Name,
|
||||
AutoSize = true,
|
||||
Location = new System.Drawing.Point(10, y),
|
||||
};
|
||||
strategyGroupBox.Controls.Add(cb);
|
||||
_strategyCheckBoxes.Add(cb);
|
||||
y += 24;
|
||||
Name = "Enabled",
|
||||
HeaderText = "",
|
||||
Width = 30,
|
||||
});
|
||||
|
||||
strategyGrid.Columns.Add(new DataGridViewTextBoxColumn
|
||||
{
|
||||
Name = "Name",
|
||||
HeaderText = "Strategy",
|
||||
ReadOnly = true,
|
||||
AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill,
|
||||
});
|
||||
|
||||
strategyGrid.Columns.Add(new DataGridViewTextBoxColumn
|
||||
{
|
||||
Name = "Phase",
|
||||
HeaderText = "Phase",
|
||||
ReadOnly = true,
|
||||
Width = 100,
|
||||
});
|
||||
|
||||
strategyGrid.Columns.Add(new DataGridViewTextBoxColumn
|
||||
{
|
||||
Name = "Order",
|
||||
HeaderText = "Order",
|
||||
ReadOnly = true,
|
||||
Width = 55,
|
||||
});
|
||||
|
||||
foreach (var strategy in FillStrategyRegistry.AllStrategies)
|
||||
{
|
||||
strategyGrid.Rows.Add(true, strategy.Name, strategy.Phase, strategy.Order);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,8 +70,8 @@ namespace OpenNest.Forms
|
||||
numericUpDown2.Value = (decimal)Settings.Default.ImportSplinePrecision;
|
||||
|
||||
var disabledNames = ParseDisabledStrategies(Settings.Default.DisabledStrategies);
|
||||
foreach (var cb in _strategyCheckBoxes)
|
||||
cb.Checked = !disabledNames.Contains((string)cb.Tag);
|
||||
foreach (DataGridViewRow row in strategyGrid.Rows)
|
||||
row.Cells["Enabled"].Value = !disabledNames.Contains((string)row.Cells["Name"].Value);
|
||||
}
|
||||
|
||||
private void SaveSettings()
|
||||
@@ -62,9 +81,12 @@ namespace OpenNest.Forms
|
||||
Settings.Default.AutoSizePlateFactor = (double)numericUpDown1.Value;
|
||||
Settings.Default.ImportSplinePrecision = (int)numericUpDown2.Value;
|
||||
|
||||
var disabledNames = _strategyCheckBoxes
|
||||
.Where(cb => !cb.Checked)
|
||||
.Select(cb => (string)cb.Tag);
|
||||
var disabledNames = new List<string>();
|
||||
foreach (DataGridViewRow row in strategyGrid.Rows)
|
||||
{
|
||||
if (row.Cells["Enabled"].Value is false)
|
||||
disabledNames.Add((string)row.Cells["Name"].Value);
|
||||
}
|
||||
Settings.Default.DisabledStrategies = string.Join(",", disabledNames);
|
||||
|
||||
Settings.Default.Save();
|
||||
|
||||
@@ -112,8 +112,8 @@ public partial class SimplifierViewerForm : Form
|
||||
var padded = new Box(
|
||||
candidate.BoundingBox.X - tol * 2,
|
||||
candidate.BoundingBox.Y - tol * 2,
|
||||
candidate.BoundingBox.Width + tol * 4,
|
||||
candidate.BoundingBox.Length + tol * 4);
|
||||
candidate.BoundingBox.Length + tol * 4,
|
||||
candidate.BoundingBox.Width + tol * 4);
|
||||
entityView.ZoomToArea(padded);
|
||||
}
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ public partial class SplitDrawingForm : Form
|
||||
var usable = plateW - 2 * spacing - overhang;
|
||||
if (usable > 0)
|
||||
{
|
||||
var splits = (int)System.Math.Ceiling(_drawingBounds.Width / usable) - 1;
|
||||
var splits = (int)System.Math.Ceiling(_drawingBounds.Length / usable) - 1;
|
||||
for (var i = 1; i <= splits; i++)
|
||||
_splitLines.Add(new SplitLine(_drawingBounds.X + usable * i, CutOffAxis.Vertical));
|
||||
}
|
||||
@@ -88,7 +88,7 @@ public partial class SplitDrawingForm : Form
|
||||
var usable = plateH - 2 * spacing - overhang;
|
||||
if (usable > 0)
|
||||
{
|
||||
var splits = (int)System.Math.Ceiling(_drawingBounds.Length / usable) - 1;
|
||||
var splits = (int)System.Math.Ceiling(_drawingBounds.Width / usable) - 1;
|
||||
for (var i = 1; i <= splits; i++)
|
||||
_splitLines.Add(new SplitLine(_drawingBounds.Y + usable * i, CutOffAxis.Horizontal));
|
||||
}
|
||||
|
||||
@@ -128,7 +128,7 @@ namespace OpenNest
|
||||
if (shapes.Count == 0)
|
||||
{
|
||||
var bbox = BasePart.BaseDrawing.Program.BoundingBox();
|
||||
return new Vector(bbox.Location.X + bbox.Width / 2, bbox.Location.Y + bbox.Length / 2);
|
||||
return new Vector(bbox.Location.X + bbox.Length / 2, bbox.Location.Y + bbox.Width / 2);
|
||||
}
|
||||
|
||||
var profile = new ShapeProfile(nonRapid);
|
||||
|
||||
Reference in New Issue
Block a user