feat: interactive cut-off selection and drag via line hit-testing
Select cut-offs by clicking their lines instead of a grip point. Drag is axis-constrained with live regeneration during movement. Selected cut-off highlighted with bright blue 3.5px line. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -33,6 +33,7 @@ namespace OpenNest.Controls
|
|||||||
private CutOffSettings cutOffSettings = new CutOffSettings();
|
private CutOffSettings cutOffSettings = new CutOffSettings();
|
||||||
private CutOff selectedCutOff;
|
private CutOff selectedCutOff;
|
||||||
private bool draggingCutOff;
|
private bool draggingCutOff;
|
||||||
|
private Dictionary<Part, Geometry.Entity> dragPerimeterCache;
|
||||||
protected List<LayoutPart> parts;
|
protected List<LayoutPart> parts;
|
||||||
private List<LayoutPart> stationaryParts = new List<LayoutPart>();
|
private List<LayoutPart> stationaryParts = new List<LayoutPart>();
|
||||||
private List<LayoutPart> activeParts = new List<LayoutPart>();
|
private List<LayoutPart> activeParts = new List<LayoutPart>();
|
||||||
@@ -242,6 +243,7 @@ namespace OpenNest.Controls
|
|||||||
{
|
{
|
||||||
SelectedCutOff = hitCutOff;
|
SelectedCutOff = hitCutOff;
|
||||||
draggingCutOff = true;
|
draggingCutOff = true;
|
||||||
|
dragPerimeterCache = Plate.BuildPerimeterCache(Plate);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -270,6 +272,7 @@ namespace OpenNest.Controls
|
|||||||
if (draggingCutOff && selectedCutOff != null)
|
if (draggingCutOff && selectedCutOff != null)
|
||||||
{
|
{
|
||||||
draggingCutOff = false;
|
draggingCutOff = false;
|
||||||
|
dragPerimeterCache = null;
|
||||||
Plate.RegenerateCutOffs(cutOffSettings);
|
Plate.RegenerateCutOffs(cutOffSettings);
|
||||||
Invalidate();
|
Invalidate();
|
||||||
return;
|
return;
|
||||||
@@ -333,7 +336,12 @@ namespace OpenNest.Controls
|
|||||||
|
|
||||||
if (draggingCutOff && selectedCutOff != null)
|
if (draggingCutOff && selectedCutOff != null)
|
||||||
{
|
{
|
||||||
selectedCutOff.Position = CurrentPoint;
|
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();
|
Invalidate();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -455,7 +463,6 @@ namespace OpenNest.Controls
|
|||||||
DrawPlate(e.Graphics);
|
DrawPlate(e.Graphics);
|
||||||
DrawParts(e.Graphics);
|
DrawParts(e.Graphics);
|
||||||
DrawCutOffs(e.Graphics);
|
DrawCutOffs(e.Graphics);
|
||||||
DrawCutOffGrip(e.Graphics);
|
|
||||||
DrawActiveWorkArea(e.Graphics);
|
DrawActiveWorkArea(e.Graphics);
|
||||||
DrawDebugRemnants(e.Graphics);
|
DrawDebugRemnants(e.Graphics);
|
||||||
|
|
||||||
@@ -612,7 +619,8 @@ namespace OpenNest.Controls
|
|||||||
if (Plate?.CutOffs == null || Plate.CutOffs.Count == 0)
|
if (Plate?.CutOffs == null || Plate.CutOffs.Count == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
using var pen = new Pen(Color.FromArgb(64, 64, 64), 1.5f / ViewScale);
|
using var pen = new Pen(Color.FromArgb(64, 64, 64), 1.5f);
|
||||||
|
using var selectedPen = new Pen(Color.FromArgb(0, 120, 255), 3.5f);
|
||||||
|
|
||||||
foreach (var cutoff in Plate.CutOffs)
|
foreach (var cutoff in Plate.CutOffs)
|
||||||
{
|
{
|
||||||
@@ -620,35 +628,19 @@ namespace OpenNest.Controls
|
|||||||
if (program == null || program.Codes.Count == 0)
|
if (program == null || program.Codes.Count == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
var activePen = cutoff == selectedCutOff ? selectedPen : pen;
|
||||||
|
|
||||||
for (var i = 0; i < program.Codes.Count - 1; i += 2)
|
for (var i = 0; i < program.Codes.Count - 1; i += 2)
|
||||||
{
|
{
|
||||||
if (program.Codes[i] is RapidMove rapid &&
|
if (program.Codes[i] is RapidMove rapid &&
|
||||||
program.Codes[i + 1] is LinearMove linear)
|
program.Codes[i + 1] is LinearMove linear)
|
||||||
{
|
{
|
||||||
DrawLine(g, rapid.EndPoint, linear.EndPoint, pen);
|
DrawLine(g, rapid.EndPoint, linear.EndPoint, activePen);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
public CutOff GetCutOffAtPoint(Vector point, double tolerance)
|
||||||
{
|
{
|
||||||
if (Plate?.CutOffs == null)
|
if (Plate?.CutOffs == null)
|
||||||
@@ -656,9 +648,20 @@ namespace OpenNest.Controls
|
|||||||
|
|
||||||
foreach (var cutoff in Plate.CutOffs)
|
foreach (var cutoff in Plate.CutOffs)
|
||||||
{
|
{
|
||||||
var dist = cutoff.Position.DistanceTo(point);
|
var program = cutoff.Drawing?.Program;
|
||||||
if (dist <= tolerance)
|
if (program == null)
|
||||||
return cutoff;
|
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;
|
return null;
|
||||||
|
|||||||
Reference in New Issue
Block a user