Compare commits

..

18 Commits

Author SHA1 Message Date
aj 5bd4c89999 chore: add missing designer resource files
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 00:27:14 -04:00
aj dd93c230dd test: add bending test data for 4526 A14 PT45
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 00:27:14 -04:00
aj d6ffd8efc9 refactor: move lead-in buttons from plates tab to menubar
Move Assign/Place/Remove Lead-ins from EditNestForm toolstrip to the
Plate menu in the main menubar. Add nest-wide Assign/Remove Lead-ins
to the Nest menu for applying to all plates at once.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 00:27:14 -04:00
aj 68c3a904e8 refactor: move filter panel into CAD View tab, file list fills sidebar 2026-04-01 00:27:14 -04:00
aj d57e2ca54b feat: add contour reordering with auto-sequence and move up/down 2026-04-01 00:27:14 -04:00
aj 904eeb38c2 fix: adjust arrow size and color, designer reformat 2026-04-01 00:27:14 -04:00
aj e1bb723169 feat: apply contour-type colors in CAD view on file load 2026-04-01 00:27:14 -04:00
aj aa156fff57 fix: draw direction arrows after origin transform so they track pan correctly 2026-04-01 00:27:14 -04:00
aj d3a439181c fix: use two-line V arrowheads with dark pen for cut direction 2026-04-01 00:27:14 -04:00
aj bb70ae26d3 refactor: extract CutDirectionArrows and reuse in program editor preview 2026-04-01 00:25:48 -04:00
aj 35dc954017 feat: move G-code editor side by side with preview 2026-04-01 00:12:36 -04:00
aj 0cae9e88e7 fix: improve program editor formatting, file switching, and entity colors
- Replace Program.ToString() with Cincinnati-style formatter (spaced
  coordinates, blank lines between contours, trailing zero suppression)
- Fix empty Program tab when switching files while on the tab by
  loading immediately instead of only marking stale
- Set contour-type colors on entities at load time and restore base
  colors before selection highlight to prevent color bleed to CAD view

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 00:12:36 -04:00
aj 5d824a1aff feat: integrate ProgramEditorControl into CadConverterForm with tab view
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 00:12:35 -04:00
aj 8a293bcc9d feat: implement G-code editor with Apply parsing
Wire up the Apply button to parse the G-code text back into a Program,
rebuild contours via ConvertProgram/ShapeBuilder/ContourInfo, and fire
ProgramChanged so callers receive the updated program.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 00:12:35 -04:00
aj 24b89689c5 feat: add direction arrows and reverse direction to program editor
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 00:12:35 -04:00
aj 3da5d1c70c feat: implement contour list display and entity loading
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 00:12:35 -04:00
aj d3ec4eb3e2 feat: add ProgramEditorControl layout skeleton
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 00:12:35 -04:00
aj cb446e1057 feat: add ContourInfo model with shape classification logic
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 00:12:35 -04:00
16 changed files with 4947 additions and 230 deletions
+137
View File
@@ -0,0 +1,137 @@
using OpenNest.Geometry;
using System;
using System.Collections.Generic;
using System.Linq;
namespace OpenNest.Converters
{
public enum ContourClassification
{
Perimeter,
Hole,
Etch,
Open
}
public sealed class ContourInfo
{
public Shape Shape { get; }
public ContourClassification Type { get; private set; }
public string Label { get; private set; }
private ContourInfo(Shape shape, ContourClassification type, string label)
{
Shape = shape;
Type = type;
Label = label;
}
public string DirectionLabel
{
get
{
if (Type == ContourClassification.Open || Type == ContourClassification.Etch)
return "Open";
var poly = Shape.ToPolygon();
if (poly == null || poly.Vertices.Count < 3)
return "?";
return poly.RotationDirection() == RotationType.CW ? "CW" : "CCW";
}
}
public string DimensionLabel
{
get
{
if (Shape.Entities.Count == 1 && Shape.Entities[0] is Circle c)
return $"Circle R{c.Radius:0.#}";
Shape.UpdateBounds();
var box = Shape.BoundingBox;
return $"{box.Width:0.#} x {box.Length:0.#}";
}
}
public void Reverse()
{
Shape.Reverse();
}
public void SetLabel(string label)
{
Label = label;
}
public static List<ContourInfo> Classify(List<Shape> shapes)
{
if (shapes.Count == 0)
return new List<ContourInfo>();
// Ensure bounding boxes are up to date before comparing
foreach (var s in shapes)
s.UpdateBounds();
// Find perimeter — largest bounding box area
var perimeterIndex = 0;
var maxArea = shapes[0].BoundingBox.Area();
for (var i = 1; i < shapes.Count; i++)
{
var area = shapes[i].BoundingBox.Area();
if (area > maxArea)
{
maxArea = area;
perimeterIndex = i;
}
}
var result = new List<ContourInfo>();
var holeCount = 0;
var etchCount = 0;
var openCount = 0;
// Non-perimeter shapes first (matches CNC cut order: holes before perimeter)
for (var i = 0; i < shapes.Count; i++)
{
if (i == perimeterIndex) continue;
var shape = shapes[i];
var type = ClassifyShape(shape);
string label;
switch (type)
{
case ContourClassification.Hole:
holeCount++;
label = $"Hole {holeCount}";
break;
case ContourClassification.Etch:
etchCount++;
label = etchCount == 1 ? "Etch" : $"Etch {etchCount}";
break;
default:
openCount++;
label = openCount == 1 ? "Open" : $"Open {openCount}";
break;
}
result.Add(new ContourInfo(shape, type, label));
}
// Perimeter last
result.Add(new ContourInfo(shapes[perimeterIndex], ContourClassification.Perimeter, "Perimeter"));
return result;
}
private static ContourClassification ClassifyShape(Shape shape)
{
// Check etch layer — all entities must be on ETCH layer
if (shape.Entities.Count > 0 &&
shape.Entities.All(e => string.Equals(e.Layer?.Name, "ETCH", StringComparison.OrdinalIgnoreCase)))
return ContourClassification.Etch;
if (shape.IsClosed())
return ContourClassification.Hole;
return ContourClassification.Open;
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,157 @@
using OpenNest.Converters;
using OpenNest.Geometry;
namespace OpenNest.Tests;
public class ContourClassificationTests
{
private static Shape MakeRectShape(double x, double y, double w, double h)
{
var shape = new Shape();
shape.Entities.Add(new Line(new Vector(x, y), new Vector(x + w, y)));
shape.Entities.Add(new Line(new Vector(x + w, y), new Vector(x + w, y + h)));
shape.Entities.Add(new Line(new Vector(x + w, y + h), new Vector(x, y + h)));
shape.Entities.Add(new Line(new Vector(x, y + h), new Vector(x, y)));
return shape;
}
private static Shape MakeCircleShape(double cx, double cy, double r)
{
var shape = new Shape();
shape.Entities.Add(new Circle(new Vector(cx, cy), r));
return shape;
}
private static Shape MakeEtchShape()
{
var etchLayer = new Layer("ETCH");
var shape = new Shape();
shape.Entities.Add(new Line(new Vector(10, 10), new Vector(50, 10)) { Layer = etchLayer });
return shape;
}
[Fact]
public void Classify_identifies_largest_shape_as_perimeter()
{
var shapes = new List<Shape>
{
MakeCircleShape(25, 25, 5),
MakeRectShape(0, 0, 100, 50),
MakeCircleShape(75, 25, 5),
};
var contours = ContourInfo.Classify(shapes);
Assert.Equal(3, contours.Count);
Assert.Single(contours, c => c.Type == ContourClassification.Perimeter);
var perimeter = contours.First(c => c.Type == ContourClassification.Perimeter);
Assert.Same(shapes[1], perimeter.Shape);
}
[Fact]
public void Classify_identifies_closed_non_perimeter_as_holes()
{
var shapes = new List<Shape>
{
MakeCircleShape(25, 25, 5),
MakeRectShape(0, 0, 100, 50),
MakeCircleShape(75, 25, 5),
};
var contours = ContourInfo.Classify(shapes);
var holes = contours.Where(c => c.Type == ContourClassification.Hole).ToList();
Assert.Equal(2, holes.Count);
}
[Fact]
public void Classify_identifies_etch_layer_shapes()
{
var shapes = new List<Shape>
{
MakeRectShape(0, 0, 100, 50),
MakeEtchShape(),
};
var contours = ContourInfo.Classify(shapes);
Assert.Single(contours, c => c.Type == ContourClassification.Etch);
}
[Fact]
public void Classify_identifies_open_shapes()
{
var openShape = new Shape();
openShape.Entities.Add(new Line(new Vector(0, 0), new Vector(10, 0)));
openShape.Entities.Add(new Line(new Vector(10, 0), new Vector(10, 5)));
// Not closed — doesn't return to (0,0)
var shapes = new List<Shape>
{
MakeRectShape(0, 0, 100, 50),
openShape,
};
var contours = ContourInfo.Classify(shapes);
Assert.Single(contours, c => c.Type == ContourClassification.Open);
}
[Fact]
public void Classify_orders_holes_first_perimeter_last()
{
var shapes = new List<Shape>
{
MakeRectShape(0, 0, 100, 50),
MakeCircleShape(25, 25, 5),
};
var contours = ContourInfo.Classify(shapes);
Assert.Equal(ContourClassification.Hole, contours[0].Type);
Assert.Equal(ContourClassification.Perimeter, contours[^1].Type);
}
[Fact]
public void Classify_labels_holes_sequentially()
{
var shapes = new List<Shape>
{
MakeRectShape(0, 0, 100, 50),
MakeCircleShape(25, 25, 5),
MakeCircleShape(75, 25, 5),
};
var contours = ContourInfo.Classify(shapes);
var holes = contours.Where(c => c.Type == ContourClassification.Hole).ToList();
Assert.Equal("Hole 1", holes[0].Label);
Assert.Equal("Hole 2", holes[1].Label);
}
[Fact]
public void Classify_single_shape_is_perimeter()
{
var shapes = new List<Shape> { MakeRectShape(0, 0, 50, 30) };
var contours = ContourInfo.Classify(shapes);
Assert.Single(contours);
Assert.Equal(ContourClassification.Perimeter, contours[0].Type);
Assert.Equal("Perimeter", contours[0].Label);
}
[Fact]
public void Reverse_changes_direction_label()
{
var shape = MakeRectShape(0, 0, 100, 50);
var contours = ContourInfo.Classify(new List<Shape> { shape });
var contour = contours[0];
var originalDirection = contour.DirectionLabel;
contour.Reverse();
var newDirection = contour.DirectionLabel;
Assert.NotEqual(originalDirection, newDirection);
}
}
+144
View File
@@ -0,0 +1,144 @@
using OpenNest.CNC;
using OpenNest.Geometry;
using OpenNest.Math;
using System.Drawing;
namespace OpenNest.Controls
{
internal static class CutDirectionArrows
{
public static void DrawProgram(Graphics g, DrawControl view, Program pgm, ref Vector pos,
Pen pen, double spacing, float arrowSize)
{
for (var i = 0; i < pgm.Length; ++i)
{
var code = pgm[i];
if (code.Type == CodeType.SubProgramCall)
{
var subpgm = (SubProgramCall)code;
if (subpgm.Program != null)
DrawProgram(g, view, subpgm.Program, ref pos, pen, spacing, arrowSize);
continue;
}
if (code is not Motion motion) continue;
var endpt = pgm.Mode == Mode.Incremental
? motion.EndPoint + pos
: motion.EndPoint;
if (code.Type == CodeType.LinearMove)
{
var line = (LinearMove)code;
if (!line.Suppressed)
DrawLineArrows(g, view, pos, endpt, pen, spacing, arrowSize);
}
else if (code.Type == CodeType.ArcMove)
{
var arc = (ArcMove)code;
if (!arc.Suppressed)
{
var center = pgm.Mode == Mode.Incremental
? arc.CenterPoint + pos
: arc.CenterPoint;
DrawArcArrows(g, view, pos, endpt, center, arc.Rotation, pen, spacing, arrowSize);
}
}
pos = endpt;
}
}
private static void DrawLineArrows(Graphics g, DrawControl view, Vector start, Vector end,
Pen pen, double spacing, float arrowSize)
{
var dx = end.X - start.X;
var dy = end.Y - start.Y;
var length = System.Math.Sqrt(dx * dx + dy * dy);
if (length < spacing * 0.5) return;
var dirX = dx / length;
var dirY = dy / length;
var count = System.Math.Max(1, (int)(length / spacing));
var step = length / (count + 1);
for (var i = 1; i <= count; i++)
{
var t = step * i;
var pt = new Vector(start.X + dirX * t, start.Y + dirY * t);
var screenPt = view.PointWorldToGraph(pt);
var angle = System.Math.Atan2(-dirY, dirX);
DrawArrowHead(g, pen, screenPt, angle, arrowSize);
}
}
private static void DrawArcArrows(Graphics g, DrawControl view, Vector start, Vector end, Vector center,
RotationType rotation, Pen pen, double spacing, float arrowSize)
{
var radius = center.DistanceTo(start);
if (radius < Tolerance.Epsilon) return;
var startAngle = System.Math.Atan2(start.Y - center.Y, start.X - center.X);
var endAngle = System.Math.Atan2(end.Y - center.Y, end.X - center.X);
double sweep;
if (rotation == RotationType.CCW)
{
sweep = endAngle - startAngle;
if (sweep <= 0) sweep += 2 * System.Math.PI;
}
else
{
sweep = startAngle - endAngle;
if (sweep <= 0) sweep += 2 * System.Math.PI;
}
var arcLength = radius * System.Math.Abs(sweep);
if (arcLength < spacing * 0.5) return;
var count = System.Math.Max(1, (int)(arcLength / spacing));
var stepAngle = sweep / (count + 1);
for (var i = 1; i <= count; i++)
{
double angle;
if (rotation == RotationType.CCW)
angle = startAngle + stepAngle * i;
else
angle = startAngle - stepAngle * i;
var pt = new Vector(
center.X + radius * System.Math.Cos(angle),
center.Y + radius * System.Math.Sin(angle));
var screenPt = view.PointWorldToGraph(pt);
double tangent;
if (rotation == RotationType.CCW)
tangent = angle + System.Math.PI / 2;
else
tangent = angle - System.Math.PI / 2;
var screenAngle = System.Math.Atan2(-System.Math.Sin(tangent), System.Math.Cos(tangent));
DrawArrowHead(g, pen, screenPt, screenAngle, arrowSize);
}
}
private static void DrawArrowHead(Graphics g, Pen pen, PointF tip, double angle, float size)
{
var leftAngle = angle + System.Math.PI + 0.5;
var rightAngle = angle + System.Math.PI - 0.5;
var left = new PointF(
tip.X + size * (float)System.Math.Cos(leftAngle),
tip.Y + size * (float)System.Math.Sin(leftAngle));
var right = new PointF(
tip.X + size * (float)System.Math.Cos(rightAngle),
tip.Y + size * (float)System.Math.Sin(rightAngle));
g.DrawLine(pen, left, tip);
g.DrawLine(pen, right, tip);
}
}
}
+4
View File
@@ -152,8 +152,12 @@ namespace OpenNest.Controls
foreach (var entity in ((Shape)offsetShape.OffsetEntity(0.25, OffsetSide.Left)).Entities)
DrawEntity(e.Graphics, entity, Pens.RoyalBlue);
#endif
PaintOverlay?.Invoke(e.Graphics);
}
public Action<Graphics> PaintOverlay { get; set; }
protected override void OnMouseWheel(MouseEventArgs e)
{
base.OnMouseWheel(e);
+2 -137
View File
@@ -515,7 +515,7 @@ namespace OpenNest.Controls
private void DrawAllCutDirectionArrows(Graphics g)
{
using var pen = new Pen(Color.FromArgb(220, Color.Black), 1.5f);
using var pen = new Pen(Color.FromArgb(60, 60, 60), 1.5f);
var arrowSpacingWorld = view.LengthGuiToWorld(60f);
var arrowSize = 6f;
@@ -525,145 +525,10 @@ namespace OpenNest.Controls
var part = view.Plate.Parts[i];
var pgm = part.Program;
var pos = part.Location;
DrawProgramCutDirectionArrows(g, pgm, ref pos, pen, arrowSpacingWorld, arrowSize);
CutDirectionArrows.DrawProgram(g, view, pgm, ref pos, pen, arrowSpacingWorld, arrowSize);
}
}
private void DrawProgramCutDirectionArrows(Graphics g, Program pgm, ref Vector pos,
Pen pen, double spacing, float arrowSize)
{
for (var i = 0; i < pgm.Length; ++i)
{
var code = pgm[i];
if (code.Type == CodeType.SubProgramCall)
{
var subpgm = (SubProgramCall)code;
if (subpgm.Program != null)
DrawProgramCutDirectionArrows(g, subpgm.Program, ref pos, pen, spacing, arrowSize);
continue;
}
if (code is not Motion motion) continue;
var endpt = pgm.Mode == Mode.Incremental
? motion.EndPoint + pos
: motion.EndPoint;
if (code.Type == CodeType.LinearMove)
{
var line = (LinearMove)code;
if (!line.Suppressed)
DrawLineDirectionArrows(g, pos, endpt, pen, spacing, arrowSize);
}
else if (code.Type == CodeType.ArcMove)
{
var arc = (ArcMove)code;
if (!arc.Suppressed)
{
var center = pgm.Mode == Mode.Incremental
? arc.CenterPoint + pos
: arc.CenterPoint;
DrawArcDirectionArrows(g, pos, endpt, center, arc.Rotation, pen, spacing, arrowSize);
}
}
pos = endpt;
}
}
private void DrawLineDirectionArrows(Graphics g, Vector start, Vector end,
Pen pen, double spacing, float arrowSize)
{
var dx = end.X - start.X;
var dy = end.Y - start.Y;
var length = System.Math.Sqrt(dx * dx + dy * dy);
if (length < spacing * 0.5) return;
var dirX = dx / length;
var dirY = dy / length;
var count = System.Math.Max(1, (int)(length / spacing));
var step = length / (count + 1);
for (var i = 1; i <= count; i++)
{
var t = step * i;
var pt = new Vector(start.X + dirX * t, start.Y + dirY * t);
var screenPt = view.PointWorldToGraph(pt);
var angle = System.Math.Atan2(-dirY, dirX);
DrawArrowHead(g, pen, screenPt, angle, arrowSize);
}
}
private void DrawArcDirectionArrows(Graphics g, Vector start, Vector end, Vector center,
RotationType rotation, Pen pen, double spacing, float arrowSize)
{
var radius = center.DistanceTo(start);
if (radius < Tolerance.Epsilon) return;
var startAngle = System.Math.Atan2(start.Y - center.Y, start.X - center.X);
var endAngle = System.Math.Atan2(end.Y - center.Y, end.X - center.X);
double sweep;
if (rotation == RotationType.CCW)
{
sweep = endAngle - startAngle;
if (sweep <= 0) sweep += 2 * System.Math.PI;
}
else
{
sweep = startAngle - endAngle;
if (sweep <= 0) sweep += 2 * System.Math.PI;
}
var arcLength = radius * System.Math.Abs(sweep);
if (arcLength < spacing * 0.5) return;
var count = System.Math.Max(1, (int)(arcLength / spacing));
var stepAngle = sweep / (count + 1);
for (var i = 1; i <= count; i++)
{
double angle;
if (rotation == RotationType.CCW)
angle = startAngle + stepAngle * i;
else
angle = startAngle - stepAngle * i;
var pt = new Vector(
center.X + radius * System.Math.Cos(angle),
center.Y + radius * System.Math.Sin(angle));
var screenPt = view.PointWorldToGraph(pt);
double tangent;
if (rotation == RotationType.CCW)
tangent = angle + System.Math.PI / 2;
else
tangent = angle - System.Math.PI / 2;
var screenAngle = System.Math.Atan2(-System.Math.Sin(tangent), System.Math.Cos(tangent));
DrawArrowHead(g, pen, screenPt, screenAngle, arrowSize);
}
}
private static void DrawArrowHead(Graphics g, Pen pen, PointF tip, double angle, float size)
{
var sin = (float)System.Math.Sin(angle);
var cos = (float)System.Math.Cos(angle);
var backX = -size * cos;
var backY = -size * sin;
var wingX = size * 0.5f * sin;
var wingY = -size * 0.5f * cos;
var wing1 = new PointF(tip.X + backX + wingX, tip.Y + backY + wingY);
var wing2 = new PointF(tip.X + backX - wingX, tip.Y + backY - wingY);
g.DrawLine(pen, wing1, tip);
g.DrawLine(pen, wing2, tip);
}
private void DrawLine(Graphics g, Vector pt1, Vector pt2, Pen pen)
{
var point1 = view.PointWorldToGraph(pt1);
+274
View File
@@ -0,0 +1,274 @@
namespace OpenNest.Controls
{
partial class ProgramEditorControl
{
private System.ComponentModel.IContainer components = null;
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
components.Dispose();
base.Dispose(disposing);
}
#region Component Designer generated code
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
mainSplit = new System.Windows.Forms.SplitContainer();
contourPanel = new System.Windows.Forms.Panel();
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();
editorPanel = new System.Windows.Forms.Panel();
gcodeEditor = new System.Windows.Forms.TextBox();
editorToolbar = new System.Windows.Forms.Panel();
applyButton = new System.Windows.Forms.Button();
lblGcode = new System.Windows.Forms.Label();
((System.ComponentModel.ISupportInitialize)mainSplit).BeginInit();
mainSplit.Panel1.SuspendLayout();
mainSplit.Panel2.SuspendLayout();
mainSplit.SuspendLayout();
contourPanel.SuspendLayout();
contourMenu.SuspendLayout();
((System.ComponentModel.ISupportInitialize)rightSplit).BeginInit();
rightSplit.Panel1.SuspendLayout();
rightSplit.Panel2.SuspendLayout();
rightSplit.SuspendLayout();
editorPanel.SuspendLayout();
editorToolbar.SuspendLayout();
SuspendLayout();
//
// mainSplit
//
mainSplit.Dock = System.Windows.Forms.DockStyle.Fill;
mainSplit.FixedPanel = System.Windows.Forms.FixedPanel.Panel1;
mainSplit.Location = new System.Drawing.Point(0, 0);
mainSplit.Name = "mainSplit";
//
// mainSplit.Panel1
//
mainSplit.Panel1.Controls.Add(contourPanel);
mainSplit.Panel1MinSize = 150;
//
// mainSplit.Panel2
//
mainSplit.Panel2.Controls.Add(rightSplit);
mainSplit.Size = new System.Drawing.Size(800, 500);
mainSplit.SplitterDistance = 180;
mainSplit.SplitterWidth = 5;
mainSplit.TabIndex = 0;
//
// contourPanel
//
contourPanel.Controls.Add(contourList);
contourPanel.Controls.Add(reverseButton);
contourPanel.Dock = System.Windows.Forms.DockStyle.Fill;
contourPanel.Location = new System.Drawing.Point(0, 0);
contourPanel.Name = "contourPanel";
contourPanel.Size = new System.Drawing.Size(180, 500);
contourPanel.TabIndex = 0;
//
// contourList
//
contourList.BackColor = System.Drawing.Color.White;
contourList.BorderStyle = System.Windows.Forms.BorderStyle.None;
contourList.ContextMenuStrip = contourMenu;
contourList.Dock = System.Windows.Forms.DockStyle.Fill;
contourList.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawVariable;
contourList.Font = new System.Drawing.Font("Segoe UI", 9F);
contourList.IntegralHeight = false;
contourList.Location = new System.Drawing.Point(0, 0);
contourList.Name = "contourList";
contourList.SelectionMode = System.Windows.Forms.SelectionMode.MultiExtended;
contourList.Size = new System.Drawing.Size(180, 462);
contourList.TabIndex = 0;
//
// contourMenu
//
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(180, 120);
//
// menuReverse
//
menuReverse.Name = "menuReverse";
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
//
reverseButton.Dock = System.Windows.Forms.DockStyle.Bottom;
reverseButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
reverseButton.Font = new System.Drawing.Font("Segoe UI", 9F);
reverseButton.Location = new System.Drawing.Point(0, 462);
reverseButton.Name = "reverseButton";
reverseButton.Size = new System.Drawing.Size(180, 38);
reverseButton.TabIndex = 1;
reverseButton.Text = "⇅ Reverse Direction";
//
// rightSplit
//
rightSplit.Dock = System.Windows.Forms.DockStyle.Fill;
rightSplit.Location = new System.Drawing.Point(0, 0);
rightSplit.Name = "rightSplit";
//
// rightSplit.Panel1
//
rightSplit.Panel1.Controls.Add(preview);
//
// rightSplit.Panel2
//
rightSplit.Panel2.Controls.Add(editorPanel);
rightSplit.Size = new System.Drawing.Size(615, 500);
rightSplit.SplitterDistance = 350;
rightSplit.SplitterWidth = 5;
rightSplit.TabIndex = 0;
//
// preview
//
preview.BackColor = System.Drawing.Color.FromArgb(33, 40, 48);
preview.Cursor = System.Windows.Forms.Cursors.Cross;
preview.Dock = System.Windows.Forms.DockStyle.Fill;
preview.IsPickingBendLine = false;
preview.Location = new System.Drawing.Point(0, 0);
preview.Name = "preview";
preview.OriginalEntities = null;
preview.PaintOverlay = null;
preview.ShowEntityLabels = false;
preview.SimplifierHighlight = null;
preview.SimplifierPreview = null;
preview.SimplifierToleranceLeft = null;
preview.SimplifierToleranceRight = null;
preview.Size = new System.Drawing.Size(350, 500);
preview.TabIndex = 0;
//
// editorPanel
//
editorPanel.Controls.Add(gcodeEditor);
editorPanel.Controls.Add(editorToolbar);
editorPanel.Dock = System.Windows.Forms.DockStyle.Fill;
editorPanel.Location = new System.Drawing.Point(0, 0);
editorPanel.Name = "editorPanel";
editorPanel.Size = new System.Drawing.Size(260, 500);
editorPanel.TabIndex = 0;
//
// gcodeEditor
//
gcodeEditor.BackColor = System.Drawing.Color.FromArgb(30, 30, 45);
gcodeEditor.BorderStyle = System.Windows.Forms.BorderStyle.None;
gcodeEditor.Dock = System.Windows.Forms.DockStyle.Fill;
gcodeEditor.Font = new System.Drawing.Font("Consolas", 10F);
gcodeEditor.ForeColor = System.Drawing.Color.FromArgb(180, 200, 180);
gcodeEditor.Location = new System.Drawing.Point(0, 30);
gcodeEditor.Multiline = true;
gcodeEditor.Name = "gcodeEditor";
gcodeEditor.ScrollBars = System.Windows.Forms.ScrollBars.Both;
gcodeEditor.Size = new System.Drawing.Size(260, 470);
gcodeEditor.TabIndex = 1;
gcodeEditor.WordWrap = false;
//
// editorToolbar
//
editorToolbar.BackColor = System.Drawing.Color.FromArgb(245, 245, 245);
editorToolbar.Controls.Add(applyButton);
editorToolbar.Controls.Add(lblGcode);
editorToolbar.Dock = System.Windows.Forms.DockStyle.Top;
editorToolbar.Location = new System.Drawing.Point(0, 0);
editorToolbar.Name = "editorToolbar";
editorToolbar.Padding = new System.Windows.Forms.Padding(6, 4, 6, 4);
editorToolbar.Size = new System.Drawing.Size(260, 30);
editorToolbar.TabIndex = 0;
//
// applyButton
//
applyButton.Dock = System.Windows.Forms.DockStyle.Right;
applyButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
applyButton.Font = new System.Drawing.Font("Segoe UI", 9F);
applyButton.Location = new System.Drawing.Point(184, 4);
applyButton.Name = "applyButton";
applyButton.Size = new System.Drawing.Size(70, 22);
applyButton.TabIndex = 1;
applyButton.Text = "Apply";
//
// lblGcode
//
lblGcode.AutoSize = true;
lblGcode.Dock = System.Windows.Forms.DockStyle.Left;
lblGcode.Font = new System.Drawing.Font("Segoe UI", 9F);
lblGcode.ForeColor = System.Drawing.Color.Gray;
lblGcode.Location = new System.Drawing.Point(6, 4);
lblGcode.Name = "lblGcode";
lblGcode.Padding = new System.Windows.Forms.Padding(0, 3, 0, 0);
lblGcode.Size = new System.Drawing.Size(48, 18);
lblGcode.TabIndex = 0;
lblGcode.Text = "G-Code";
//
// ProgramEditorControl
//
AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
Controls.Add(mainSplit);
Name = "ProgramEditorControl";
Size = new System.Drawing.Size(800, 500);
mainSplit.Panel1.ResumeLayout(false);
mainSplit.Panel2.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)mainSplit).EndInit();
mainSplit.ResumeLayout(false);
contourPanel.ResumeLayout(false);
contourMenu.ResumeLayout(false);
rightSplit.Panel1.ResumeLayout(false);
rightSplit.Panel2.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)rightSplit).EndInit();
rightSplit.ResumeLayout(false);
editorPanel.ResumeLayout(false);
editorPanel.PerformLayout();
editorToolbar.ResumeLayout(false);
editorToolbar.PerformLayout();
ResumeLayout(false);
}
#endregion
private System.Windows.Forms.SplitContainer mainSplit;
private System.Windows.Forms.Panel contourPanel;
private System.Windows.Forms.ListBox contourList;
private System.Windows.Forms.Button reverseButton;
private System.Windows.Forms.SplitContainer rightSplit;
private Controls.EntityView preview;
private System.Windows.Forms.Panel editorPanel;
private System.Windows.Forms.Panel editorToolbar;
private System.Windows.Forms.Label lblGcode;
private System.Windows.Forms.Button applyButton;
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;
}
}
+470
View File
@@ -0,0 +1,470 @@
using OpenNest.CNC;
using OpenNest.Converters;
using OpenNest.Geometry;
using OpenNest.IO;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
namespace OpenNest.Controls
{
public partial class ProgramEditorControl : UserControl
{
private List<ContourInfo> contours = new();
private bool isDirty;
private bool isLoaded;
public ProgramEditorControl()
{
InitializeComponent();
contourList.DrawItem += OnDrawContourItem;
contourList.MeasureItem += OnMeasureContourItem;
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;
}
public Program Program { get; private set; }
public bool IsDirty => isDirty;
public bool IsLoaded => isLoaded;
public event EventHandler ProgramChanged;
public void LoadEntities(List<Entity> entities)
{
var shapes = ShapeBuilder.GetShapes(entities);
if (shapes.Count == 0)
{
Clear();
return;
}
contours = ContourInfo.Classify(shapes);
// Assign contour-type colors once so the CAD view also picks them up
foreach (var contour in contours)
{
var color = GetContourColor(contour.Type, false);
foreach (var entity in contour.Shape.Entities)
entity.Color = color;
}
Program = BuildProgram(contours);
isDirty = false;
isLoaded = true;
PopulateContourList();
UpdateGcodeText();
RefreshPreview();
}
public void Clear()
{
contours.Clear();
contourList.Items.Clear();
preview.Entities.Clear();
preview.Invalidate();
gcodeEditor.Clear();
Program = null;
isDirty = false;
isLoaded = false;
}
private static Program BuildProgram(List<ContourInfo> contours)
{
var pgm = new Program();
foreach (var contour in contours)
{
var sub = ConvertGeometry.ToProgram(contour.Shape);
pgm.Merge(sub);
}
pgm.Mode = Mode.Incremental;
return pgm;
}
private void PopulateContourList()
{
contourList.Items.Clear();
foreach (var contour in contours)
contourList.Items.Add(contour);
}
private void UpdateGcodeText()
{
gcodeEditor.Text = Program != null ? FormatProgram(Program) : string.Empty;
}
private static string FormatProgram(Program pgm)
{
var sb = new System.Text.StringBuilder();
sb.AppendLine(pgm.Mode == Mode.Absolute ? "G90" : "G91");
var lastWasRapid = false;
foreach (var code in pgm.Codes)
{
if (code is RapidMove rapid)
{
if (!lastWasRapid && sb.Length > 0)
sb.AppendLine();
sb.AppendLine($"G00 X{FormatCoord(rapid.EndPoint.X)} Y{FormatCoord(rapid.EndPoint.Y)}");
lastWasRapid = true;
}
else if (code is ArcMove arc)
{
var g = arc.Rotation == RotationType.CW ? "G02" : "G03";
sb.AppendLine($"{g} X{FormatCoord(arc.EndPoint.X)} Y{FormatCoord(arc.EndPoint.Y)} I{FormatCoord(arc.CenterPoint.X)} J{FormatCoord(arc.CenterPoint.Y)}");
lastWasRapid = false;
}
else if (code is LinearMove linear)
{
sb.AppendLine($"G01 X{FormatCoord(linear.EndPoint.X)} Y{FormatCoord(linear.EndPoint.Y)}");
lastWasRapid = false;
}
}
return sb.ToString();
}
private static string FormatCoord(double value)
{
return System.Math.Round(value, 4).ToString("0.####", System.Globalization.CultureInfo.InvariantCulture);
}
private void RefreshPreview()
{
preview.ClearPenCache();
preview.Entities.Clear();
// Restore base colors first (undo any selection highlight)
foreach (var contour in contours)
{
var baseColor = GetContourColor(contour.Type, false);
foreach (var entity in contour.Shape.Entities)
entity.Color = baseColor;
}
for (var i = 0; i < contours.Count; i++)
{
var contour = contours[i];
var selected = contourList.SelectedIndices.Contains(i);
if (selected)
{
var selColor = GetContourColor(contour.Type, true);
foreach (var entity in contour.Shape.Entities)
entity.Color = selColor;
}
preview.Entities.AddRange(contour.Shape.Entities);
}
preview.ZoomToFit();
preview.Invalidate();
}
private static Color GetContourColor(ContourClassification type, bool selected)
{
if (selected)
return Color.White;
return type switch
{
ContourClassification.Perimeter => Color.FromArgb(80, 180, 120),
ContourClassification.Hole => Color.FromArgb(100, 140, 255),
ContourClassification.Etch => Color.FromArgb(255, 170, 50),
ContourClassification.Open => Color.FromArgb(200, 200, 100),
_ => Color.Gray,
};
}
private void OnMeasureContourItem(object sender, MeasureItemEventArgs e)
{
e.ItemHeight = 42;
}
private void OnDrawContourItem(object sender, DrawItemEventArgs e)
{
if (e.Index < 0 || e.Index >= contours.Count) return;
var contour = contours[e.Index];
var selected = (e.State & DrawItemState.Selected) != 0;
var bounds = e.Bounds;
// Background
using var bgBrush = new SolidBrush(selected
? Color.FromArgb(230, 238, 255)
: Color.White);
e.Graphics.FillRectangle(bgBrush, bounds);
// Accent bar
var accentColor = GetContourColor(contour.Type, false);
using var accentBrush = new SolidBrush(accentColor);
e.Graphics.FillRectangle(accentBrush, bounds.X, bounds.Y + 4, 3, bounds.Height - 8);
// Direction icon
var icon = contour.Type switch
{
ContourClassification.Perimeter or ContourClassification.Hole =>
contour.DirectionLabel == "CW" ? "\u21BB" : "\u21BA",
ContourClassification.Etch => "\u2014",
_ => "\u2014",
};
using var iconFont = new Font("Segoe UI", 14f);
using var iconBrush = new SolidBrush(accentColor);
e.Graphics.DrawString(icon, iconFont, iconBrush, bounds.X + 8, bounds.Y + 6);
// Label
using var labelFont = new Font("Segoe UI", 9f, FontStyle.Bold);
using var labelBrush = new SolidBrush(Color.FromArgb(40, 40, 40));
e.Graphics.DrawString(contour.Label, labelFont, labelBrush, bounds.X + 32, bounds.Y + 4);
// Info line
var info = $"{contour.DirectionLabel} \u00B7 {contour.DimensionLabel}";
using var infoFont = new Font("Segoe UI", 8f);
using var infoBrush = new SolidBrush(Color.Gray);
e.Graphics.DrawString(info, infoFont, infoBrush, bounds.X + 32, bounds.Y + 22);
// Separator
using var sepPen = new Pen(Color.FromArgb(230, 230, 230));
e.Graphics.DrawLine(sepPen, bounds.X + 8, bounds.Bottom - 1, bounds.Right - 8, bounds.Bottom - 1);
}
private void OnContourSelectionChanged(object sender, EventArgs e)
{
RefreshPreview();
}
private void OnReverseClicked(object sender, EventArgs e)
{
if (contourList.SelectedIndices.Count == 0) return;
foreach (int index in contourList.SelectedIndices)
{
if (index >= 0 && index < contours.Count)
contours[index].Reverse();
}
Program = BuildProgram(contours);
isDirty = true;
contourList.Invalidate();
UpdateGcodeText();
RefreshPreview();
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;
var spacing = preview.LengthGuiToWorld(60f);
var arrowSize = 8f;
using var pen = new Pen(Color.LightGray, 1.5f);
for (var i = 0; i < contours.Count; i++)
{
if (!contourList.SelectedIndices.Contains(i)) continue;
var contour = contours[i];
var pgm = ConvertGeometry.ToProgram(contour.Shape);
if (pgm == null) continue;
var pos = new Vector();
CutDirectionArrows.DrawProgram(g, preview, pgm, ref pos, pen, spacing, arrowSize);
}
}
private void OnApplyClicked(object sender, EventArgs e)
{
var text = gcodeEditor.Text;
if (string.IsNullOrWhiteSpace(text))
{
MessageBox.Show("G-code is empty.", "Apply", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
try
{
using var stream = new System.IO.MemoryStream(System.Text.Encoding.UTF8.GetBytes(text));
var reader = new ProgramReader(stream);
var parsed = reader.Read();
if (parsed == null || parsed.Length == 0)
{
MessageBox.Show("No valid G-code found.", "Apply", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
// Rebuild shapes from the parsed program
var entities = ConvertProgram.ToGeometry(parsed);
var shapes = ShapeBuilder.GetShapes(entities);
if (shapes.Count == 0)
{
MessageBox.Show("No contours found in parsed G-code.", "Apply", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
contours = ContourInfo.Classify(shapes);
Program = parsed;
isDirty = true;
PopulateContourList();
RefreshPreview();
ProgramChanged?.Invoke(this, EventArgs.Empty);
}
catch (Exception ex)
{
MessageBox.Show($"Error parsing G-code: {ex.Message}", "Apply",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
}
+123
View File
@@ -0,0 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="contourMenu.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
</root>
+120
View File
@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>
+104 -48
View File
@@ -16,11 +16,15 @@ namespace OpenNest.Forms
private void InitializeComponent()
{
mainSplit = new System.Windows.Forms.SplitContainer();
sidebarSplit = new System.Windows.Forms.SplitContainer();
fileList = new OpenNest.Controls.FileListControl();
cadViewSplit = new System.Windows.Forms.SplitContainer();
filterPanel = new OpenNest.Controls.FilterPanel();
entityView1 = new OpenNest.Controls.EntityView();
detailBar = new System.Windows.Forms.FlowLayoutPanel();
viewTabs = new System.Windows.Forms.TabControl();
tabCadView = new System.Windows.Forms.TabPage();
tabProgram = new System.Windows.Forms.TabPage();
programEditor = new OpenNest.Controls.ProgramEditorControl();
lblQty = new System.Windows.Forms.Label();
numQuantity = new System.Windows.Forms.NumericUpDown();
lblCust = new System.Windows.Forms.Label();
@@ -41,74 +45,78 @@ namespace OpenNest.Forms
mainSplit.Panel1.SuspendLayout();
mainSplit.Panel2.SuspendLayout();
mainSplit.SuspendLayout();
((System.ComponentModel.ISupportInitialize)sidebarSplit).BeginInit();
sidebarSplit.Panel1.SuspendLayout();
sidebarSplit.Panel2.SuspendLayout();
sidebarSplit.SuspendLayout();
((System.ComponentModel.ISupportInitialize)cadViewSplit).BeginInit();
cadViewSplit.Panel1.SuspendLayout();
cadViewSplit.Panel2.SuspendLayout();
cadViewSplit.SuspendLayout();
detailBar.SuspendLayout();
viewTabs.SuspendLayout();
tabCadView.SuspendLayout();
tabProgram.SuspendLayout();
((System.ComponentModel.ISupportInitialize)numQuantity).BeginInit();
bottomPanel1.SuspendLayout();
SuspendLayout();
//
//
// mainSplit
//
//
mainSplit.Dock = System.Windows.Forms.DockStyle.Fill;
mainSplit.FixedPanel = System.Windows.Forms.FixedPanel.Panel1;
mainSplit.Location = new System.Drawing.Point(0, 0);
mainSplit.Name = "mainSplit";
//
//
// mainSplit.Panel1
//
mainSplit.Panel1.Controls.Add(sidebarSplit);
//
mainSplit.Panel1.Controls.Add(fileList);
mainSplit.Panel1MinSize = 200;
//
//
// mainSplit.Panel2
//
mainSplit.Panel2.Controls.Add(entityView1);
mainSplit.Panel2.Controls.Add(detailBar);
//
mainSplit.Panel2.Controls.Add(viewTabs);
mainSplit.Size = new System.Drawing.Size(1024, 670);
mainSplit.SplitterDistance = 260;
mainSplit.SplitterWidth = 5;
mainSplit.TabIndex = 2;
//
// sidebarSplit
//
sidebarSplit.Dock = System.Windows.Forms.DockStyle.Fill;
sidebarSplit.Location = new System.Drawing.Point(0, 0);
sidebarSplit.Name = "sidebarSplit";
sidebarSplit.Orientation = System.Windows.Forms.Orientation.Horizontal;
//
// sidebarSplit.Panel1
//
sidebarSplit.Panel1.Controls.Add(fileList);
//
// sidebarSplit.Panel2
//
sidebarSplit.Panel2.Controls.Add(filterPanel);
sidebarSplit.Size = new System.Drawing.Size(260, 670);
sidebarSplit.SplitterDistance = 300;
sidebarSplit.SplitterWidth = 5;
sidebarSplit.TabIndex = 0;
//
//
// fileList
//
//
fileList.AllowDrop = true;
fileList.BackColor = System.Drawing.Color.White;
fileList.Dock = System.Windows.Forms.DockStyle.Fill;
fileList.Font = new System.Drawing.Font("Segoe UI", 9F);
fileList.Location = new System.Drawing.Point(0, 0);
fileList.Name = "fileList";
fileList.Size = new System.Drawing.Size(260, 300);
fileList.Size = new System.Drawing.Size(260, 670);
fileList.TabIndex = 0;
//
//
// cadViewSplit
//
cadViewSplit.Dock = System.Windows.Forms.DockStyle.Fill;
cadViewSplit.FixedPanel = System.Windows.Forms.FixedPanel.Panel1;
cadViewSplit.Location = new System.Drawing.Point(0, 0);
cadViewSplit.Name = "cadViewSplit";
//
// cadViewSplit.Panel1 — filter panel
//
cadViewSplit.Panel1.Controls.Add(filterPanel);
cadViewSplit.Panel1MinSize = 150;
//
// cadViewSplit.Panel2 — entity view + detail bar
//
cadViewSplit.Panel2.Controls.Add(entityView1);
cadViewSplit.Panel2.Controls.Add(detailBar);
cadViewSplit.Size = new System.Drawing.Size(751, 642);
cadViewSplit.SplitterDistance = 200;
cadViewSplit.SplitterWidth = 5;
cadViewSplit.TabIndex = 0;
//
// filterPanel
//
//
filterPanel.AutoScroll = true;
filterPanel.BackColor = System.Drawing.Color.White;
filterPanel.Dock = System.Windows.Forms.DockStyle.Fill;
filterPanel.Location = new System.Drawing.Point(0, 0);
filterPanel.Name = "filterPanel";
filterPanel.Size = new System.Drawing.Size(260, 365);
filterPanel.Size = new System.Drawing.Size(200, 642);
filterPanel.TabIndex = 0;
//
// entityView1
@@ -125,7 +133,7 @@ namespace OpenNest.Forms
entityView1.SimplifierPreview = null;
entityView1.SimplifierToleranceLeft = null;
entityView1.SimplifierToleranceRight = null;
entityView1.Size = new System.Drawing.Size(759, 634);
entityView1.Size = new System.Drawing.Size(546, 606);
entityView1.TabIndex = 0;
//
// detailBar
@@ -148,7 +156,7 @@ namespace OpenNest.Forms
detailBar.Location = new System.Drawing.Point(0, 634);
detailBar.Name = "detailBar";
detailBar.Padding = new System.Windows.Forms.Padding(4, 6, 4, 4);
detailBar.Size = new System.Drawing.Size(759, 36);
detailBar.Size = new System.Drawing.Size(546, 36);
detailBar.TabIndex = 1;
detailBar.WrapContents = false;
//
@@ -333,9 +341,50 @@ namespace OpenNest.Forms
acceptButton.Size = new System.Drawing.Size(90, 28);
acceptButton.TabIndex = 1;
acceptButton.Text = "Accept";
//
//
// viewTabs
//
viewTabs.Controls.Add(tabCadView);
viewTabs.Controls.Add(tabProgram);
viewTabs.Dock = System.Windows.Forms.DockStyle.Fill;
viewTabs.Location = new System.Drawing.Point(0, 0);
viewTabs.Name = "viewTabs";
viewTabs.SelectedIndex = 0;
viewTabs.Size = new System.Drawing.Size(759, 670);
viewTabs.TabIndex = 0;
//
// tabCadView
//
tabCadView.Controls.Add(cadViewSplit);
tabCadView.Location = new System.Drawing.Point(4, 24);
tabCadView.Name = "tabCadView";
tabCadView.Padding = new System.Windows.Forms.Padding(0);
tabCadView.Size = new System.Drawing.Size(751, 642);
tabCadView.TabIndex = 0;
tabCadView.Text = "CAD View";
tabCadView.UseVisualStyleBackColor = true;
//
// tabProgram
//
tabProgram.Controls.Add(programEditor);
tabProgram.Location = new System.Drawing.Point(4, 24);
tabProgram.Name = "tabProgram";
tabProgram.Padding = new System.Windows.Forms.Padding(0);
tabProgram.Size = new System.Drawing.Size(751, 642);
tabProgram.TabIndex = 1;
tabProgram.Text = "Program";
tabProgram.UseVisualStyleBackColor = true;
//
// programEditor
//
programEditor.Dock = System.Windows.Forms.DockStyle.Fill;
programEditor.Location = new System.Drawing.Point(0, 0);
programEditor.Name = "programEditor";
programEditor.Size = new System.Drawing.Size(751, 642);
programEditor.TabIndex = 0;
//
// CadConverterForm
//
//
AllowDrop = true;
AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
ClientSize = new System.Drawing.Size(1024, 720);
@@ -353,13 +402,16 @@ namespace OpenNest.Forms
mainSplit.Panel2.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)mainSplit).EndInit();
mainSplit.ResumeLayout(false);
sidebarSplit.Panel1.ResumeLayout(false);
sidebarSplit.Panel2.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)sidebarSplit).EndInit();
sidebarSplit.ResumeLayout(false);
cadViewSplit.Panel1.ResumeLayout(false);
cadViewSplit.Panel2.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)cadViewSplit).EndInit();
cadViewSplit.ResumeLayout(false);
detailBar.ResumeLayout(false);
detailBar.PerformLayout();
((System.ComponentModel.ISupportInitialize)numQuantity).EndInit();
viewTabs.ResumeLayout(false);
tabCadView.ResumeLayout(false);
tabProgram.ResumeLayout(false);
bottomPanel1.ResumeLayout(false);
ResumeLayout(false);
}
@@ -367,7 +419,7 @@ namespace OpenNest.Forms
#endregion
private System.Windows.Forms.SplitContainer mainSplit;
private System.Windows.Forms.SplitContainer sidebarSplit;
private System.Windows.Forms.SplitContainer cadViewSplit;
private Controls.FileListControl fileList;
private Controls.FilterPanel filterPanel;
private Controls.EntityView entityView1;
@@ -388,5 +440,9 @@ namespace OpenNest.Forms
private Controls.BottomPanel bottomPanel1;
private System.Windows.Forms.Button acceptButton;
private System.Windows.Forms.Button cancelButton;
private System.Windows.Forms.TabControl viewTabs;
private System.Windows.Forms.TabPage tabCadView;
private System.Windows.Forms.TabPage tabProgram;
private Controls.ProgramEditorControl programEditor;
}
}
+72 -1
View File
@@ -21,6 +21,7 @@ namespace OpenNest.Forms
{
private static int colorIndex;
private SimplifierViewerForm simplifierViewer;
private bool staleProgram = true;
public CadConverterForm()
{
@@ -44,6 +45,8 @@ namespace OpenNest.Forms
cboBendDetector.Items.Add(detector.Name);
cboBendDetector.SelectedIndex = 0;
viewTabs.SelectedIndexChanged += OnViewTabChanged;
// Drag & drop
AllowDrop = true;
DragEnter += OnDragEnter;
@@ -133,6 +136,11 @@ namespace OpenNest.Forms
}
LoadItem(item);
staleProgram = true;
if (viewTabs.SelectedTab == tabProgram)
LoadProgramTab();
else
programEditor.Clear();
}
private void LoadItem(FileListItem item)
@@ -153,6 +161,8 @@ namespace OpenNest.Forms
item.Entities.ForEach(e => e.Layer.IsVisible = true);
ReHidePromotedEntities(item.Bends);
ApplyContourColors(item.Entities);
filterPanel.LoadItem(item.Entities, item.Bends);
numQuantity.Value = item.Quantity;
@@ -168,6 +178,30 @@ namespace OpenNest.Forms
CheckSimplifiable(item);
}
private static void ApplyContourColors(List<Entity> entities)
{
var visible = entities.Where(e => e.IsVisible && e.Layer != null && e.Layer.IsVisible).ToList();
if (visible.Count == 0) return;
var shapes = ShapeBuilder.GetShapes(visible);
if (shapes.Count == 0) return;
var contours = ContourInfo.Classify(shapes);
foreach (var contour in contours)
{
var color = contour.Type switch
{
ContourClassification.Perimeter => System.Drawing.Color.FromArgb(80, 180, 120),
ContourClassification.Hole => System.Drawing.Color.FromArgb(100, 140, 255),
ContourClassification.Etch => System.Drawing.Color.FromArgb(255, 170, 50),
ContourClassification.Open => System.Drawing.Color.FromArgb(200, 200, 100),
_ => System.Drawing.Color.Gray,
};
foreach (var entity in contour.Shape.Entities)
entity.Color = color;
}
}
private void CheckSimplifiable(FileListItem item)
{
ResetSimplifyButton();
@@ -229,6 +263,40 @@ namespace OpenNest.Forms
filterPanel.ApplyFilters(item.Entities);
ReHidePromotedEntities(item.Bends);
entityView1.Invalidate();
staleProgram = true;
}
private void OnViewTabChanged(object sender, EventArgs e)
{
if (viewTabs.SelectedTab == tabProgram && staleProgram)
LoadProgramTab();
}
private void LoadProgramTab()
{
var item = CurrentItem;
if (item == null)
{
programEditor.Clear();
staleProgram = false;
return;
}
var entities = item.Entities.Where(en => en.Layer.IsVisible && en.IsVisible).ToList();
if (entities.Count == 0)
{
programEditor.Clear();
staleProgram = false;
return;
}
var normalized = ShapeProfile.NormalizeEntities(entities);
programEditor.LoadEntities(normalized);
staleProgram = false;
// Refresh CAD view to show contour-type colors
entityView1.ClearPenCache();
entityView1.Invalidate();
}
private void OnBendLineSelected(object sender, int index)
@@ -595,7 +663,10 @@ namespace OpenNest.Forms
pgm.Codes.RemoveAt(0);
}
drawing.Program = pgm;
if (item == CurrentItem && programEditor.IsDirty && programEditor.Program != null)
drawing.Program = programEditor.Program;
else
drawing.Program = pgm;
drawings.Add(drawing);
Thread.Sleep(20);
+1 -37
View File
@@ -38,9 +38,6 @@
this.qtyColumn = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
this.toolStrip1 = new System.Windows.Forms.ToolStrip();
this.toolStripButton1 = new System.Windows.Forms.ToolStripButton();
this.btnAssignLeadIns = new System.Windows.Forms.ToolStripButton();
this.btnPlaceLeadIn = new System.Windows.Forms.ToolStripButton();
this.btnRemoveLeadIns = new System.Windows.Forms.ToolStripButton();
this.tabPage2 = new System.Windows.Forms.TabPage();
this.drawingListBox1 = new OpenNest.Controls.DrawingListBox();
this.toolStrip2 = new System.Windows.Forms.ToolStrip();
@@ -136,10 +133,7 @@
this.toolStrip1.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden;
this.toolStrip1.ImageScalingSize = new System.Drawing.Size(20, 20);
this.toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.toolStripButton1,
this.btnAssignLeadIns,
this.btnPlaceLeadIn,
this.btnRemoveLeadIns});
this.toolStripButton1});
this.toolStrip1.Location = new System.Drawing.Point(3, 3);
this.toolStrip1.Name = "toolStrip1";
this.toolStrip1.Size = new System.Drawing.Size(227, 31);
@@ -158,33 +152,6 @@
this.toolStripButton1.Text = "Calculate Cut Time";
this.toolStripButton1.Click += new System.EventHandler(this.CalculateSelectedPlateCutTime_Click);
//
// btnAssignLeadIns
//
this.btnAssignLeadIns.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
this.btnAssignLeadIns.ImageTransparentColor = System.Drawing.Color.Magenta;
this.btnAssignLeadIns.Name = "btnAssignLeadIns";
this.btnAssignLeadIns.Size = new System.Drawing.Size(96, 28);
this.btnAssignLeadIns.Text = "Assign Lead-ins";
this.btnAssignLeadIns.Click += new System.EventHandler(this.AssignLeadIns_Click);
//
// btnPlaceLeadIn
//
this.btnPlaceLeadIn.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
this.btnPlaceLeadIn.ImageTransparentColor = System.Drawing.Color.Magenta;
this.btnPlaceLeadIn.Name = "btnPlaceLeadIn";
this.btnPlaceLeadIn.Size = new System.Drawing.Size(90, 28);
this.btnPlaceLeadIn.Text = "Place Lead-in";
this.btnPlaceLeadIn.Click += new System.EventHandler(this.PlaceLeadIn_Click);
//
// btnRemoveLeadIns
//
this.btnRemoveLeadIns.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
this.btnRemoveLeadIns.ImageTransparentColor = System.Drawing.Color.Magenta;
this.btnRemoveLeadIns.Name = "btnRemoveLeadIns";
this.btnRemoveLeadIns.Size = new System.Drawing.Size(104, 28);
this.btnRemoveLeadIns.Text = "Remove Lead-ins";
this.btnRemoveLeadIns.Click += new System.EventHandler(this.RemoveLeadIns_Click);
//
// tabPage2
//
this.tabPage2.Controls.Add(this.drawingListBox1);
@@ -299,8 +266,5 @@
private System.Windows.Forms.ToolStripButton toolStripButton2;
private System.Windows.Forms.ToolStripSeparator toolStripSeparator1;
private System.Windows.Forms.ToolStripButton toolStripButton3;
private System.Windows.Forms.ToolStripButton btnAssignLeadIns;
private System.Windows.Forms.ToolStripButton btnPlaceLeadIn;
private System.Windows.Forms.ToolStripButton btnRemoveLeadIns;
}
}
+52 -3
View File
@@ -718,7 +718,7 @@ namespace OpenNest.Forms
CalculateCurrentPlateCutTime();
}
private void AssignLeadIns_Click(object sender, EventArgs e)
public void AssignLeadIns_Click(object sender, EventArgs e)
{
if (PlateView?.Plate == null)
return;
@@ -747,7 +747,7 @@ namespace OpenNest.Forms
PlateView.Invalidate();
}
private void RemoveLeadIns_Click(object sender, EventArgs e)
public void RemoveLeadIns_Click(object sender, EventArgs e)
{
if (PlateView?.Plate == null)
return;
@@ -779,7 +779,56 @@ namespace OpenNest.Forms
PlateView.Invalidate();
}
private void PlaceLeadIn_Click(object sender, EventArgs e)
public void AssignLeadInsAllPlates()
{
if (Nest == null)
return;
using var form = new CuttingParametersForm();
if (form.ShowDialog(this) != DialogResult.OK)
return;
var parameters = form.BuildParameters();
var assigner = new LeadInAssigner
{
Sequencer = new LeftSideSequencer()
};
foreach (var plate in Nest.Plates)
{
plate.CuttingParameters = parameters;
assigner.Assign(plate);
}
PlateView.Invalidate();
}
public void RemoveLeadInsAllPlates()
{
if (Nest == null)
return;
foreach (var plate in Nest.Plates)
{
foreach (var part in plate.Parts)
{
if (part.HasManualLeadIns)
part.RemoveLeadIns();
}
plate.CuttingParameters = null;
}
foreach (var lp in PlateView.Parts)
{
lp.IsDirty = true;
lp.Update();
}
PlateView.Invalidate();
}
public void PlaceLeadIn_Click(object sender, EventArgs e)
{
if (PlateView?.Plate == null)
return;
+63 -4
View File
@@ -98,6 +98,9 @@
mnuNestPost = new System.Windows.Forms.ToolStripMenuItem();
toolStripMenuItem19 = new System.Windows.Forms.ToolStripSeparator();
calculateCutTimeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
toolStripMenuItem22 = new System.Windows.Forms.ToolStripSeparator();
mnuNestAssignLeadIns = new System.Windows.Forms.ToolStripMenuItem();
mnuNestRemoveLeadIns = new System.Windows.Forms.ToolStripMenuItem();
mnuPlate = new System.Windows.Forms.ToolStripMenuItem();
mnuPlateEdit = new System.Windows.Forms.ToolStripMenuItem();
mnuPlateSetAsDefault = new System.Windows.Forms.ToolStripMenuItem();
@@ -120,6 +123,10 @@
calculateCutTimeToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem();
centerPartsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
mnuPlateCutOff = new System.Windows.Forms.ToolStripMenuItem();
toolStripMenuItem21 = new System.Windows.Forms.ToolStripSeparator();
mnuPlateAssignLeadIns = new System.Windows.Forms.ToolStripMenuItem();
mnuPlatePlaceLeadIn = new System.Windows.Forms.ToolStripMenuItem();
mnuPlateRemoveLeadIns = new System.Windows.Forms.ToolStripMenuItem();
mnuWindow = new System.Windows.Forms.ToolStripMenuItem();
mnuWindowCascade = new System.Windows.Forms.ToolStripMenuItem();
mnuWindowTileVertical = new System.Windows.Forms.ToolStripMenuItem();
@@ -552,7 +559,7 @@
//
// mnuNest
//
mnuNest.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { mnuNestEdit, mnuNestImportDrawing, toolStripMenuItem7, mnuNestFirstPlate, mnuNestLastPlate, toolStripMenuItem6, mnuNestNextPlate, mnuNestPreviousPlate, toolStripMenuItem12, runAutoNestToolStripMenuItem, autoSequenceAllPlatesToolStripMenuItem, mnuNestRemoveEmptyPlates, mnuNestPost, toolStripMenuItem19, calculateCutTimeToolStripMenuItem });
mnuNest.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { mnuNestEdit, mnuNestImportDrawing, toolStripMenuItem7, mnuNestFirstPlate, mnuNestLastPlate, toolStripMenuItem6, mnuNestNextPlate, mnuNestPreviousPlate, toolStripMenuItem12, runAutoNestToolStripMenuItem, autoSequenceAllPlatesToolStripMenuItem, mnuNestRemoveEmptyPlates, mnuNestPost, toolStripMenuItem19, calculateCutTimeToolStripMenuItem, toolStripMenuItem22, mnuNestAssignLeadIns, mnuNestRemoveLeadIns });
mnuNest.Name = "mnuNest";
mnuNest.Size = new System.Drawing.Size(43, 20);
mnuNest.Text = "&Nest";
@@ -659,10 +666,29 @@
calculateCutTimeToolStripMenuItem.Size = new System.Drawing.Size(205, 22);
calculateCutTimeToolStripMenuItem.Text = "Calculate Cut Time";
calculateCutTimeToolStripMenuItem.Click += CalculateNestCutTime_Click;
//
//
// toolStripMenuItem22
//
toolStripMenuItem22.Name = "toolStripMenuItem22";
toolStripMenuItem22.Size = new System.Drawing.Size(202, 6);
//
// mnuNestAssignLeadIns
//
mnuNestAssignLeadIns.Name = "mnuNestAssignLeadIns";
mnuNestAssignLeadIns.Size = new System.Drawing.Size(205, 22);
mnuNestAssignLeadIns.Text = "Assign Lead-ins (All Plates)";
mnuNestAssignLeadIns.Click += NestAssignLeadIns_Click;
//
// mnuNestRemoveLeadIns
//
mnuNestRemoveLeadIns.Name = "mnuNestRemoveLeadIns";
mnuNestRemoveLeadIns.Size = new System.Drawing.Size(205, 22);
mnuNestRemoveLeadIns.Text = "Remove Lead-ins (All Plates)";
mnuNestRemoveLeadIns.Click += NestRemoveLeadIns_Click;
//
// mnuPlate
//
mnuPlate.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { mnuPlateEdit, mnuPlateSetAsDefault, toolStripMenuItem18, mnuPlateAdd, mnuPlateRemove, toolStripMenuItem16, mnuPlateCutOff, mnuPlateRotate, mnuResizeToFitParts, toolStripMenuItem13, mnuPlateViewInCad, toolStripMenuItem20, mnuSequenceParts, calculateCutTimeToolStripMenuItem1, centerPartsToolStripMenuItem });
mnuPlate.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { mnuPlateEdit, mnuPlateSetAsDefault, toolStripMenuItem18, mnuPlateAdd, mnuPlateRemove, toolStripMenuItem16, mnuPlateCutOff, mnuPlateRotate, mnuResizeToFitParts, toolStripMenuItem13, mnuPlateViewInCad, toolStripMenuItem20, mnuSequenceParts, calculateCutTimeToolStripMenuItem1, centerPartsToolStripMenuItem, toolStripMenuItem21, mnuPlateAssignLeadIns, mnuPlatePlaceLeadIn, mnuPlateRemoveLeadIns });
mnuPlate.Name = "mnuPlate";
mnuPlate.Size = new System.Drawing.Size(45, 20);
mnuPlate.Text = "&Plate";
@@ -808,7 +834,33 @@
centerPartsToolStripMenuItem.Size = new System.Drawing.Size(177, 22);
centerPartsToolStripMenuItem.Text = "Center Parts";
centerPartsToolStripMenuItem.Click += centerPartsToolStripMenuItem_Click;
//
//
// toolStripMenuItem21
//
toolStripMenuItem21.Name = "toolStripMenuItem21";
toolStripMenuItem21.Size = new System.Drawing.Size(174, 6);
//
// mnuPlateAssignLeadIns
//
mnuPlateAssignLeadIns.Name = "mnuPlateAssignLeadIns";
mnuPlateAssignLeadIns.Size = new System.Drawing.Size(177, 22);
mnuPlateAssignLeadIns.Text = "Assign Lead-ins";
mnuPlateAssignLeadIns.Click += PlateAssignLeadIns_Click;
//
// mnuPlatePlaceLeadIn
//
mnuPlatePlaceLeadIn.Name = "mnuPlatePlaceLeadIn";
mnuPlatePlaceLeadIn.Size = new System.Drawing.Size(177, 22);
mnuPlatePlaceLeadIn.Text = "Place Lead-in";
mnuPlatePlaceLeadIn.Click += PlatePlaceLeadIn_Click;
//
// mnuPlateRemoveLeadIns
//
mnuPlateRemoveLeadIns.Name = "mnuPlateRemoveLeadIns";
mnuPlateRemoveLeadIns.Size = new System.Drawing.Size(177, 22);
mnuPlateRemoveLeadIns.Text = "Remove Lead-ins";
mnuPlateRemoveLeadIns.Click += PlateRemoveLeadIns_Click;
//
// mnuWindow
//
mnuWindow.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { mnuWindowCascade, mnuWindowTileVertical, mnuWindowTileHorizontal, toolStripMenuItem10, closeToolStripMenuItem, mnuCloseAll });
@@ -1257,5 +1309,12 @@
private System.Windows.Forms.ToolStripSeparator toolStripSeparator5;
private System.Windows.Forms.ToolStripButton btnCutOff;
private System.Windows.Forms.ToolStripMenuItem mnuPlateCutOff;
private System.Windows.Forms.ToolStripSeparator toolStripMenuItem21;
private System.Windows.Forms.ToolStripMenuItem mnuPlateAssignLeadIns;
private System.Windows.Forms.ToolStripMenuItem mnuPlatePlaceLeadIn;
private System.Windows.Forms.ToolStripMenuItem mnuPlateRemoveLeadIns;
private System.Windows.Forms.ToolStripSeparator toolStripMenuItem22;
private System.Windows.Forms.ToolStripMenuItem mnuNestAssignLeadIns;
private System.Windows.Forms.ToolStripMenuItem mnuNestRemoveLeadIns;
}
}
+30
View File
@@ -1066,6 +1066,18 @@ namespace OpenNest.Forms
activeForm.CalculateNestCutTime();
}
private void NestAssignLeadIns_Click(object sender, EventArgs e)
{
if (activeForm == null) return;
activeForm.AssignLeadInsAllPlates();
}
private void NestRemoveLeadIns_Click(object sender, EventArgs e)
{
if (activeForm == null) return;
activeForm.RemoveLeadInsAllPlates();
}
#endregion Nest Menu Events
#region Plate Menu Events
@@ -1158,6 +1170,24 @@ namespace OpenNest.Forms
activeForm.PlateView.SetAction(typeof(ActionCutOff));
}
private void PlateAssignLeadIns_Click(object sender, EventArgs e)
{
if (activeForm == null) return;
activeForm.AssignLeadIns_Click(sender, e);
}
private void PlatePlaceLeadIn_Click(object sender, EventArgs e)
{
if (activeForm == null) return;
activeForm.PlaceLeadIn_Click(sender, e);
}
private void PlateRemoveLeadIns_Click(object sender, EventArgs e)
{
if (activeForm == null) return;
activeForm.RemoveLeadIns_Click(sender, e);
}
#endregion Plate Menu Events
#region Window Menu Events