Compare commits
18 Commits
f3ca021fad
...
5bd4c89999
| Author | SHA1 | Date | |
|---|---|---|---|
| 5bd4c89999 | |||
| dd93c230dd | |||
| d6ffd8efc9 | |||
| 68c3a904e8 | |||
| d57e2ca54b | |||
| 904eeb38c2 | |||
| e1bb723169 | |||
| aa156fff57 | |||
| d3a439181c | |||
| bb70ae26d3 | |||
| 35dc954017 | |||
| 0cae9e88e7 | |||
| 5d824a1aff | |||
| 8a293bcc9d | |||
| 24b89689c5 | |||
| 3da5d1c70c | |||
| d3ec4eb3e2 | |||
| cb446e1057 |
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Generated
+1
-37
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
Generated
+63
-4
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user