From 57bd0447e912c18fd210c4d658fa2ac46534b666 Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Sun, 22 Mar 2026 19:46:49 -0400 Subject: [PATCH] feat: add PlateView cut-off rendering with grip handle Add DrawCutOffs and DrawCutOffGrip methods to PlateView for rendering cut-off lines and selection grip handles. Includes CutOffSettings and SelectedCutOff properties, plus GetCutOffAtPoint for hit-testing. Co-Authored-By: Claude Opus 4.6 (1M context) --- OpenNest/Controls/PlateView.cs | 82 ++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/OpenNest/Controls/PlateView.cs b/OpenNest/Controls/PlateView.cs index 806f5e4..501dbe8 100644 --- a/OpenNest/Controls/PlateView.cs +++ b/OpenNest/Controls/PlateView.cs @@ -30,6 +30,8 @@ namespace OpenNest.Controls private Plate plate; private Action currentAction; private Action previousAction; + private CutOffSettings cutOffSettings = new CutOffSettings(); + private CutOff selectedCutOff; protected List parts; private List stationaryParts = new List(); private List activeParts = new List(); @@ -134,6 +136,27 @@ namespace OpenNest.Controls public bool FillParts { get; set; } + public CutOffSettings CutOffSettings + { + get => cutOffSettings; + set + { + cutOffSettings = value; + Plate?.RegenerateCutOffs(value); + Invalidate(); + } + } + + public CutOff SelectedCutOff + { + get => selectedCutOff; + set + { + selectedCutOff = value; + Invalidate(); + } + } + public double RotateIncrementAngle { get; set; } public double OffsetIncrementDistance { get; set; } @@ -390,6 +413,8 @@ namespace OpenNest.Controls DrawPlate(e.Graphics); DrawParts(e.Graphics); + DrawCutOffs(e.Graphics); + DrawCutOffGrip(e.Graphics); DrawActiveWorkArea(e.Graphics); DrawDebugRemnants(e.Graphics); @@ -541,6 +566,63 @@ namespace OpenNest.Controls DrawRapids(g); } + private void DrawCutOffs(Graphics g) + { + if (Plate?.CutOffs == null || Plate.CutOffs.Count == 0) + return; + + using var pen = new Pen(Color.FromArgb(64, 64, 64), 1.5f / ViewScale); + + foreach (var cutoff in Plate.CutOffs) + { + var program = cutoff.Drawing?.Program; + if (program == null || program.Codes.Count == 0) + continue; + + for (var i = 0; i < program.Codes.Count - 1; i += 2) + { + if (program.Codes[i] is RapidMove rapid && + program.Codes[i + 1] is LinearMove linear) + { + DrawLine(g, rapid.EndPoint, linear.EndPoint, pen); + } + } + } + } + + private void DrawCutOffGrip(Graphics g) + { + if (selectedCutOff == null) + return; + + var radius = 4f / ViewScale; + var pos = selectedCutOff.Position; + var graphPt = PointWorldToGraph(pos); + var scaledRadius = LengthWorldToGui(radius); + + g.FillEllipse(Brushes.DarkGray, + graphPt.X - scaledRadius, graphPt.Y - scaledRadius, + scaledRadius * 2, scaledRadius * 2); + g.DrawEllipse(Pens.Black, + graphPt.X - scaledRadius, graphPt.Y - scaledRadius, + scaledRadius * 2, scaledRadius * 2); + } + + public CutOff GetCutOffAtPoint(Vector point, double tolerance) + { + if (Plate?.CutOffs == null) + return null; + + foreach (var cutoff in Plate.CutOffs) + { + var dist = cutoff.Position.DistanceTo(point); + if (dist <= tolerance) + return cutoff; + } + + return null; + } + private void DrawOffsetGeometry(Graphics g) { using (var offsetPen = new Pen(Color.FromArgb(120, 255, 100, 100)))