feat: add contour reordering with auto-sequence and move up/down
This commit is contained in:
@@ -56,6 +56,11 @@ namespace OpenNest.Converters
|
|||||||
Shape.Reverse();
|
Shape.Reverse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetLabel(string label)
|
||||||
|
{
|
||||||
|
Label = label;
|
||||||
|
}
|
||||||
|
|
||||||
public static List<ContourInfo> Classify(List<Shape> shapes)
|
public static List<ContourInfo> Classify(List<Shape> shapes)
|
||||||
{
|
{
|
||||||
if (shapes.Count == 0)
|
if (shapes.Count == 0)
|
||||||
|
|||||||
+27
-3
@@ -21,6 +21,9 @@ namespace OpenNest.Controls
|
|||||||
contourList = new System.Windows.Forms.ListBox();
|
contourList = new System.Windows.Forms.ListBox();
|
||||||
contourMenu = new System.Windows.Forms.ContextMenuStrip(components);
|
contourMenu = new System.Windows.Forms.ContextMenuStrip(components);
|
||||||
menuReverse = new System.Windows.Forms.ToolStripMenuItem();
|
menuReverse = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
|
menuMoveUp = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
|
menuMoveDown = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
|
menuSequence = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
reverseButton = new System.Windows.Forms.Button();
|
reverseButton = new System.Windows.Forms.Button();
|
||||||
rightSplit = new System.Windows.Forms.SplitContainer();
|
rightSplit = new System.Windows.Forms.SplitContainer();
|
||||||
preview = new EntityView();
|
preview = new EntityView();
|
||||||
@@ -90,16 +93,34 @@ namespace OpenNest.Controls
|
|||||||
//
|
//
|
||||||
// contourMenu
|
// contourMenu
|
||||||
//
|
//
|
||||||
contourMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { menuReverse });
|
contourMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { menuReverse, menuMoveUp, menuMoveDown, new System.Windows.Forms.ToolStripSeparator(), menuSequence });
|
||||||
contourMenu.Name = "contourMenu";
|
contourMenu.Name = "contourMenu";
|
||||||
contourMenu.Size = new System.Drawing.Size(166, 26);
|
contourMenu.Size = new System.Drawing.Size(180, 120);
|
||||||
//
|
//
|
||||||
// menuReverse
|
// menuReverse
|
||||||
//
|
//
|
||||||
menuReverse.Name = "menuReverse";
|
menuReverse.Name = "menuReverse";
|
||||||
menuReverse.Size = new System.Drawing.Size(165, 22);
|
menuReverse.Size = new System.Drawing.Size(179, 22);
|
||||||
menuReverse.Text = "Reverse Direction";
|
menuReverse.Text = "Reverse Direction";
|
||||||
//
|
//
|
||||||
|
// menuMoveUp
|
||||||
|
//
|
||||||
|
menuMoveUp.Name = "menuMoveUp";
|
||||||
|
menuMoveUp.Size = new System.Drawing.Size(179, 22);
|
||||||
|
menuMoveUp.Text = "Move Up";
|
||||||
|
//
|
||||||
|
// menuMoveDown
|
||||||
|
//
|
||||||
|
menuMoveDown.Name = "menuMoveDown";
|
||||||
|
menuMoveDown.Size = new System.Drawing.Size(179, 22);
|
||||||
|
menuMoveDown.Text = "Move Down";
|
||||||
|
//
|
||||||
|
// menuSequence
|
||||||
|
//
|
||||||
|
menuSequence.Name = "menuSequence";
|
||||||
|
menuSequence.Size = new System.Drawing.Size(179, 22);
|
||||||
|
menuSequence.Text = "Auto Sequence";
|
||||||
|
//
|
||||||
// reverseButton
|
// reverseButton
|
||||||
//
|
//
|
||||||
reverseButton.Dock = System.Windows.Forms.DockStyle.Bottom;
|
reverseButton.Dock = System.Windows.Forms.DockStyle.Bottom;
|
||||||
@@ -246,5 +267,8 @@ namespace OpenNest.Controls
|
|||||||
private System.Windows.Forms.TextBox gcodeEditor;
|
private System.Windows.Forms.TextBox gcodeEditor;
|
||||||
private System.Windows.Forms.ContextMenuStrip contourMenu;
|
private System.Windows.Forms.ContextMenuStrip contourMenu;
|
||||||
private System.Windows.Forms.ToolStripMenuItem menuReverse;
|
private System.Windows.Forms.ToolStripMenuItem menuReverse;
|
||||||
|
private System.Windows.Forms.ToolStripMenuItem menuMoveUp;
|
||||||
|
private System.Windows.Forms.ToolStripMenuItem menuMoveDown;
|
||||||
|
private System.Windows.Forms.ToolStripMenuItem menuSequence;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,10 @@ namespace OpenNest.Controls
|
|||||||
contourList.SelectedIndexChanged += OnContourSelectionChanged;
|
contourList.SelectedIndexChanged += OnContourSelectionChanged;
|
||||||
reverseButton.Click += OnReverseClicked;
|
reverseButton.Click += OnReverseClicked;
|
||||||
menuReverse.Click += OnReverseClicked;
|
menuReverse.Click += OnReverseClicked;
|
||||||
|
menuMoveUp.Click += OnMoveUpClicked;
|
||||||
|
menuMoveDown.Click += OnMoveDownClicked;
|
||||||
|
menuSequence.Click += OnSequenceClicked;
|
||||||
|
contourMenu.Opening += OnContourMenuOpening;
|
||||||
applyButton.Click += OnApplyClicked;
|
applyButton.Click += OnApplyClicked;
|
||||||
preview.PaintOverlay = OnPreviewPaintOverlay;
|
preview.PaintOverlay = OnPreviewPaintOverlay;
|
||||||
}
|
}
|
||||||
@@ -258,6 +262,143 @@ namespace OpenNest.Controls
|
|||||||
ProgramChanged?.Invoke(this, EventArgs.Empty);
|
ProgramChanged?.Invoke(this, EventArgs.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnContourMenuOpening(object sender, System.ComponentModel.CancelEventArgs e)
|
||||||
|
{
|
||||||
|
var index = contourList.SelectedIndex;
|
||||||
|
var perimeterIndex = contours.FindIndex(c => c.Type == ContourClassification.Perimeter);
|
||||||
|
var isPerimeter = index >= 0 && index == perimeterIndex;
|
||||||
|
|
||||||
|
// Can't move perimeter, can't move past perimeter
|
||||||
|
menuMoveUp.Enabled = index > 0 && !isPerimeter;
|
||||||
|
menuMoveDown.Enabled = index >= 0 && index < perimeterIndex - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnMoveUpClicked(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
var index = contourList.SelectedIndex;
|
||||||
|
if (index <= 0) return;
|
||||||
|
if (contours[index].Type == ContourClassification.Perimeter) return;
|
||||||
|
|
||||||
|
(contours[index], contours[index - 1]) = (contours[index - 1], contours[index]);
|
||||||
|
RebuildAfterReorder(index - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnMoveDownClicked(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
var index = contourList.SelectedIndex;
|
||||||
|
if (index < 0 || index >= contours.Count - 1) return;
|
||||||
|
if (contours[index].Type == ContourClassification.Perimeter) return;
|
||||||
|
if (contours[index + 1].Type == ContourClassification.Perimeter) return;
|
||||||
|
|
||||||
|
(contours[index], contours[index + 1]) = (contours[index + 1], contours[index]);
|
||||||
|
RebuildAfterReorder(index + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSequenceClicked(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
// Nearest-neighbor sort for non-perimeter contours
|
||||||
|
var perimeterIndex = contours.FindIndex(c => c.Type == ContourClassification.Perimeter);
|
||||||
|
if (perimeterIndex < 0) return;
|
||||||
|
|
||||||
|
var nonPerimeter = contours.Where(c => c.Type != ContourClassification.Perimeter).ToList();
|
||||||
|
if (nonPerimeter.Count <= 1) return;
|
||||||
|
|
||||||
|
var sorted = new List<ContourInfo>();
|
||||||
|
var remaining = new List<ContourInfo>(nonPerimeter);
|
||||||
|
var pos = new Vector();
|
||||||
|
|
||||||
|
while (remaining.Count > 0)
|
||||||
|
{
|
||||||
|
var nearest = 0;
|
||||||
|
var nearestDist = double.MaxValue;
|
||||||
|
|
||||||
|
for (var i = 0; i < remaining.Count; i++)
|
||||||
|
{
|
||||||
|
var startPt = GetContourStartPoint(remaining[i]);
|
||||||
|
var dist = pos.DistanceTo(startPt);
|
||||||
|
if (dist < nearestDist)
|
||||||
|
{
|
||||||
|
nearestDist = dist;
|
||||||
|
nearest = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var next = remaining[nearest];
|
||||||
|
sorted.Add(next);
|
||||||
|
pos = GetContourEndPoint(next);
|
||||||
|
remaining.RemoveAt(nearest);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put perimeter back at the end
|
||||||
|
sorted.Add(contours[perimeterIndex]);
|
||||||
|
contours = sorted;
|
||||||
|
RebuildAfterReorder(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Vector GetContourStartPoint(ContourInfo contour)
|
||||||
|
{
|
||||||
|
var entity = contour.Shape.Entities[0];
|
||||||
|
return entity switch
|
||||||
|
{
|
||||||
|
Line line => line.StartPoint,
|
||||||
|
Arc arc => arc.StartPoint(),
|
||||||
|
Circle circle => new Vector(circle.Center.X + circle.Radius, circle.Center.Y),
|
||||||
|
_ => new Vector(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Vector GetContourEndPoint(ContourInfo contour)
|
||||||
|
{
|
||||||
|
var entity = contour.Shape.Entities[^1];
|
||||||
|
return entity switch
|
||||||
|
{
|
||||||
|
Line line => line.EndPoint,
|
||||||
|
Arc arc => arc.EndPoint(),
|
||||||
|
Circle circle => new Vector(circle.Center.X + circle.Radius, circle.Center.Y),
|
||||||
|
_ => new Vector(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RebuildAfterReorder(int selectIndex)
|
||||||
|
{
|
||||||
|
RelabelContours();
|
||||||
|
Program = BuildProgram(contours);
|
||||||
|
isDirty = true;
|
||||||
|
|
||||||
|
PopulateContourList();
|
||||||
|
if (selectIndex >= 0 && selectIndex < contourList.Items.Count)
|
||||||
|
contourList.SelectedIndex = selectIndex;
|
||||||
|
UpdateGcodeText();
|
||||||
|
RefreshPreview();
|
||||||
|
ProgramChanged?.Invoke(this, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RelabelContours()
|
||||||
|
{
|
||||||
|
var holeCount = 0;
|
||||||
|
var etchCount = 0;
|
||||||
|
var openCount = 0;
|
||||||
|
|
||||||
|
foreach (var contour in contours)
|
||||||
|
{
|
||||||
|
switch (contour.Type)
|
||||||
|
{
|
||||||
|
case ContourClassification.Hole:
|
||||||
|
holeCount++;
|
||||||
|
contour.SetLabel($"Hole {holeCount}");
|
||||||
|
break;
|
||||||
|
case ContourClassification.Etch:
|
||||||
|
etchCount++;
|
||||||
|
contour.SetLabel(etchCount == 1 ? "Etch" : $"Etch {etchCount}");
|
||||||
|
break;
|
||||||
|
case ContourClassification.Open:
|
||||||
|
openCount++;
|
||||||
|
contour.SetLabel(openCount == 1 ? "Open" : $"Open {openCount}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void OnPreviewPaintOverlay(Graphics g)
|
private void OnPreviewPaintOverlay(Graphics g)
|
||||||
{
|
{
|
||||||
if (contours.Count == 0) return;
|
if (contours.Count == 0) return;
|
||||||
|
|||||||
Reference in New Issue
Block a user