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();
|
||||
}
|
||||
|
||||
public void SetLabel(string label)
|
||||
{
|
||||
Label = label;
|
||||
}
|
||||
|
||||
public static List<ContourInfo> Classify(List<Shape> shapes)
|
||||
{
|
||||
if (shapes.Count == 0)
|
||||
|
||||
34
OpenNest/Controls/ProgramEditorControl.Designer.cs
generated
34
OpenNest/Controls/ProgramEditorControl.Designer.cs
generated
@@ -21,6 +21,9 @@ namespace OpenNest.Controls
|
||||
contourList = new System.Windows.Forms.ListBox();
|
||||
contourMenu = new System.Windows.Forms.ContextMenuStrip(components);
|
||||
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();
|
||||
rightSplit = new System.Windows.Forms.SplitContainer();
|
||||
preview = new EntityView();
|
||||
@@ -90,15 +93,33 @@ namespace OpenNest.Controls
|
||||
//
|
||||
// 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.Size = new System.Drawing.Size(166, 26);
|
||||
//
|
||||
contourMenu.Size = new System.Drawing.Size(180, 120);
|
||||
//
|
||||
// menuReverse
|
||||
//
|
||||
//
|
||||
menuReverse.Name = "menuReverse";
|
||||
menuReverse.Size = new System.Drawing.Size(165, 22);
|
||||
menuReverse.Size = new System.Drawing.Size(179, 22);
|
||||
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
|
||||
//
|
||||
@@ -246,5 +267,8 @@ namespace OpenNest.Controls
|
||||
private System.Windows.Forms.TextBox gcodeEditor;
|
||||
private System.Windows.Forms.ContextMenuStrip contourMenu;
|
||||
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;
|
||||
reverseButton.Click += OnReverseClicked;
|
||||
menuReverse.Click += OnReverseClicked;
|
||||
menuMoveUp.Click += OnMoveUpClicked;
|
||||
menuMoveDown.Click += OnMoveDownClicked;
|
||||
menuSequence.Click += OnSequenceClicked;
|
||||
contourMenu.Opening += OnContourMenuOpening;
|
||||
applyButton.Click += OnApplyClicked;
|
||||
preview.PaintOverlay = OnPreviewPaintOverlay;
|
||||
}
|
||||
@@ -258,6 +262,143 @@ namespace OpenNest.Controls
|
||||
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)
|
||||
{
|
||||
if (contours.Count == 0) return;
|
||||
|
||||
Reference in New Issue
Block a user