From 25faba430ccf880a57357e6f206d903ad85ae906 Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Tue, 7 Apr 2026 15:13:42 -0400 Subject: [PATCH] refactor: extract CutOffHandler from PlateView Move cut-off drag interaction mechanics into a dedicated CutOffHandler class, reducing PlateView complexity and following the same pattern established by SelectionManager extraction in Task 1. Co-Authored-By: Claude Sonnet 4.6 --- OpenNest/Controls/CutOffHandler.cs | 81 ++++++++++++++++++++++++++++++ OpenNest/Controls/PlateView.cs | 59 ++++++---------------- 2 files changed, 95 insertions(+), 45 deletions(-) create mode 100644 OpenNest/Controls/CutOffHandler.cs diff --git a/OpenNest/Controls/CutOffHandler.cs b/OpenNest/Controls/CutOffHandler.cs new file mode 100644 index 0000000..dc5b720 --- /dev/null +++ b/OpenNest/Controls/CutOffHandler.cs @@ -0,0 +1,81 @@ +using OpenNest.CNC; +using OpenNest.Geometry; +using System.Collections.Generic; + +namespace OpenNest.Controls +{ + internal class CutOffHandler + { + private readonly PlateView view; + private Dictionary dragPerimeterCache; + + public CutOffHandler(PlateView view) + { + this.view = view; + } + + public bool IsDragging { get; private set; } + + public CutOff TryStartDrag(Vector point, double tolerance) + { + var hitCutOff = GetCutOffAtPoint(point, tolerance); + if (hitCutOff == null) + return null; + + IsDragging = true; + dragPerimeterCache = Plate.BuildPerimeterCache(view.Plate); + return hitCutOff; + } + + public void UpdateDrag(Vector currentPoint, CutOff cutOff) + { + if (!IsDragging || cutOff == null) + return; + + if (cutOff.Axis == CutOffAxis.Vertical) + cutOff.Position = new Vector(currentPoint.X, cutOff.Position.Y); + else + cutOff.Position = new Vector(cutOff.Position.X, currentPoint.Y); + + cutOff.Regenerate(view.Plate, view.CutOffSettings, dragPerimeterCache); + view.Invalidate(); + } + + public void EndDrag() + { + if (!IsDragging) + return; + + IsDragging = false; + dragPerimeterCache = null; + view.Plate.RegenerateCutOffs(view.CutOffSettings); + view.Invalidate(); + } + + public CutOff GetCutOffAtPoint(Vector point, double tolerance) + { + if (view.Plate?.CutOffs == null) + return null; + + foreach (var cutoff in view.Plate.CutOffs) + { + var program = cutoff.Drawing?.Program; + if (program == null) + 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) + { + var line = new Line(rapid.EndPoint, linear.EndPoint); + if (line.ClosestPointTo(point).DistanceTo(point) <= tolerance) + return cutoff; + } + } + } + + return null; + } + } +} diff --git a/OpenNest/Controls/PlateView.cs b/OpenNest/Controls/PlateView.cs index f07200a..b21747c 100644 --- a/OpenNest/Controls/PlateView.cs +++ b/OpenNest/Controls/PlateView.cs @@ -32,8 +32,7 @@ namespace OpenNest.Controls private Action previousAction; private CutOffSettings cutOffSettings = new CutOffSettings(); private SelectionManager selection; - private bool draggingCutOff; - private Dictionary dragPerimeterCache; + private CutOffHandler cutOffHandler; protected List parts; private List stationaryParts = new List(); private List activeParts = new List(); @@ -70,6 +69,7 @@ namespace OpenNest.Controls public ReadOnlyCollection Parts => new ReadOnlyCollection(parts); internal SelectionManager Selection => selection; + internal CutOffHandler CutOffs => cutOffHandler; public event EventHandler> PartAdded; public event EventHandler> PartRemoved; @@ -93,6 +93,7 @@ namespace OpenNest.Controls origin = new PointF(); parts = new List(); selection = new SelectionManager(this); + cutOffHandler = new CutOffHandler(this); redrawTimer = new Timer() { @@ -273,17 +274,18 @@ namespace OpenNest.Controls if (e.Button == MouseButtons.Left && currentAction is ActionSelect) { - var hitCutOff = GetCutOffAtPoint(CurrentPoint, 5.0 / ViewScale); + var hitCutOff = cutOffHandler.TryStartDrag(CurrentPoint, 5.0 / ViewScale); if (hitCutOff != null) { - SelectedCutOff = hitCutOff; - draggingCutOff = true; - dragPerimeterCache = Plate.BuildPerimeterCache(Plate); + selection.DeselectParts(); + selection.SelectedCutOffs.Clear(); + selection.SelectedCutOffs.Add(hitCutOff); + Invalidate(); return; } else { - SelectedCutOff = null; + selection.DeselectCutOffs(); } } @@ -304,12 +306,9 @@ namespace OpenNest.Controls } } - if (draggingCutOff && SelectedCutOff != null) + if (cutOffHandler.IsDragging && selection.SelectedCutOffs.Count > 0) { - draggingCutOff = false; - dragPerimeterCache = null; - Plate.RegenerateCutOffs(cutOffSettings); - Invalidate(); + cutOffHandler.EndDrag(); return; } @@ -369,15 +368,9 @@ namespace OpenNest.Controls lastPoint = e.Location; - if (draggingCutOff && SelectedCutOff != null) + if (cutOffHandler.IsDragging && selection.SelectedCutOffs.Count > 0) { - if (SelectedCutOff.Axis == CutOffAxis.Vertical) - SelectedCutOff.Position = new Vector(CurrentPoint.X, SelectedCutOff.Position.Y); - else - SelectedCutOff.Position = new Vector(SelectedCutOff.Position.X, CurrentPoint.Y); - - SelectedCutOff.Regenerate(Plate, cutOffSettings, dragPerimeterCache); - Invalidate(); + cutOffHandler.UpdateDrag(CurrentPoint, selection.SelectedCutOffs[0]); return; } @@ -566,31 +559,7 @@ namespace OpenNest.Controls Invalidate(); } - public CutOff GetCutOffAtPoint(Vector point, double tolerance) - { - if (Plate?.CutOffs == null) - return null; - - foreach (var cutoff in Plate.CutOffs) - { - var program = cutoff.Drawing?.Program; - if (program == null) - 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) - { - var line = new Geometry.Line(rapid.EndPoint, linear.EndPoint); - if (line.ClosestPointTo(point).DistanceTo(point) <= tolerance) - return cutoff; - } - } - } - - return null; - } + public CutOff GetCutOffAtPoint(Vector point, double tolerance) => cutOffHandler.GetCutOffAtPoint(point, tolerance); public LayoutPart GetPartAtControlPoint(Point pt) => selection.GetPartAtControlPoint(pt); public LayoutPart GetPartAtGraphPoint(PointF pt) => selection.GetPartAtGraphPoint(pt);