feat: fit-to-plate splits use full plate work area with preview line
FitToPlate now places split lines at usable-width intervals so each piece (except the last) fills the entire plate work area. Also adds a live yellow preview line that follows the cursor during manual split line placement, and piece dimension labels in the preview regions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -19,19 +19,11 @@ public static class AutoSplitCalculator
|
|||||||
if (verticalSplits < 0) verticalSplits = 0;
|
if (verticalSplits < 0) verticalSplits = 0;
|
||||||
if (horizontalSplits < 0) horizontalSplits = 0;
|
if (horizontalSplits < 0) horizontalSplits = 0;
|
||||||
|
|
||||||
if (verticalSplits > 0)
|
for (var i = 1; i <= verticalSplits; i++)
|
||||||
{
|
lines.Add(new SplitLine(partBounds.X + usableWidth * i, CutOffAxis.Vertical));
|
||||||
var spacing = partBounds.Width / (verticalSplits + 1);
|
|
||||||
for (var i = 1; i <= verticalSplits; i++)
|
|
||||||
lines.Add(new SplitLine(partBounds.X + spacing * i, CutOffAxis.Vertical));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (horizontalSplits > 0)
|
for (var i = 1; i <= horizontalSplits; i++)
|
||||||
{
|
lines.Add(new SplitLine(partBounds.Y + usableHeight * i, CutOffAxis.Horizontal));
|
||||||
var spacing = partBounds.Length / (horizontalSplits + 1);
|
|
||||||
for (var i = 1; i <= horizontalSplits; i++)
|
|
||||||
lines.Add(new SplitLine(partBounds.Y + spacing * i, CutOffAxis.Horizontal));
|
|
||||||
}
|
|
||||||
|
|
||||||
return lines;
|
return lines;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ public class AutoSplitCalculatorTests
|
|||||||
|
|
||||||
Assert.Single(lines);
|
Assert.Single(lines);
|
||||||
Assert.Equal(CutOffAxis.Vertical, lines[0].Axis);
|
Assert.Equal(CutOffAxis.Vertical, lines[0].Axis);
|
||||||
Assert.Equal(50.0, lines[0].Position, 1);
|
Assert.Equal(58.0, lines[0].Position, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ public partial class SplitDrawingForm : Form
|
|||||||
private readonly List<SplitLine> _splitLines = new();
|
private readonly List<SplitLine> _splitLines = new();
|
||||||
private CutOffAxis _currentAxis = CutOffAxis.Vertical;
|
private CutOffAxis _currentAxis = CutOffAxis.Vertical;
|
||||||
private bool _placingLine;
|
private bool _placingLine;
|
||||||
|
private Vector _placingCursor;
|
||||||
|
|
||||||
// Feature handle drag state
|
// Feature handle drag state
|
||||||
private int _dragLineIndex = -1;
|
private int _dragLineIndex = -1;
|
||||||
@@ -78,12 +79,8 @@ public partial class SplitDrawingForm : Form
|
|||||||
if (usable > 0)
|
if (usable > 0)
|
||||||
{
|
{
|
||||||
var splits = (int)System.Math.Ceiling(_drawingBounds.Width / usable) - 1;
|
var splits = (int)System.Math.Ceiling(_drawingBounds.Width / usable) - 1;
|
||||||
if (splits > 0)
|
for (var i = 1; i <= splits; i++)
|
||||||
{
|
_splitLines.Add(new SplitLine(_drawingBounds.X + usable * i, CutOffAxis.Vertical));
|
||||||
var step = _drawingBounds.Width / (splits + 1);
|
|
||||||
for (var i = 1; i <= splits; i++)
|
|
||||||
_splitLines.Add(new SplitLine(_drawingBounds.X + step * i, CutOffAxis.Vertical));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (axisIndex == 2)
|
else if (axisIndex == 2)
|
||||||
@@ -92,12 +89,8 @@ public partial class SplitDrawingForm : Form
|
|||||||
if (usable > 0)
|
if (usable > 0)
|
||||||
{
|
{
|
||||||
var splits = (int)System.Math.Ceiling(_drawingBounds.Length / usable) - 1;
|
var splits = (int)System.Math.Ceiling(_drawingBounds.Length / usable) - 1;
|
||||||
if (splits > 0)
|
for (var i = 1; i <= splits; i++)
|
||||||
{
|
_splitLines.Add(new SplitLine(_drawingBounds.Y + usable * i, CutOffAxis.Horizontal));
|
||||||
var step = _drawingBounds.Length / (splits + 1);
|
|
||||||
for (var i = 1; i <= splits; i++)
|
|
||||||
_splitLines.Add(new SplitLine(_drawingBounds.Y + step * i, CutOffAxis.Horizontal));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -293,6 +286,12 @@ public partial class SplitDrawingForm : Form
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_placingLine)
|
||||||
|
{
|
||||||
|
_placingCursor = SnapToMidpoint(worldPt);
|
||||||
|
pnlPreview.Invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
lblCursor.Text = $"Cursor: {worldPt.X:F2}, {worldPt.Y:F2}";
|
lblCursor.Text = $"Cursor: {worldPt.X:F2}, {worldPt.Y:F2}";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -339,6 +338,7 @@ public partial class SplitDrawingForm : Form
|
|||||||
if (keyData == Keys.Space)
|
if (keyData == Keys.Space)
|
||||||
{
|
{
|
||||||
_currentAxis = _currentAxis == CutOffAxis.Vertical ? CutOffAxis.Horizontal : CutOffAxis.Vertical;
|
_currentAxis = _currentAxis == CutOffAxis.Vertical ? CutOffAxis.Horizontal : CutOffAxis.Vertical;
|
||||||
|
pnlPreview.Invalidate();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (keyData == Keys.Escape)
|
if (keyData == Keys.Escape)
|
||||||
@@ -381,10 +381,11 @@ public partial class SplitDrawingForm : Form
|
|||||||
System.Math.Abs(br.X - tl.X), System.Math.Abs(br.Y - tl.Y));
|
System.Math.Abs(br.X - tl.X), System.Math.Abs(br.Y - tl.Y));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Piece number labels at center of each region
|
// Piece number and dimension labels at center of each region
|
||||||
if (regions.Count > 1)
|
if (regions.Count > 1)
|
||||||
{
|
{
|
||||||
using var labelFont = new Font("Segoe UI", 14f, FontStyle.Bold, GraphicsUnit.Pixel);
|
using var labelFont = new Font("Segoe UI", 14f, FontStyle.Bold, GraphicsUnit.Pixel);
|
||||||
|
using var dimFont = new Font("Segoe UI", 11f, FontStyle.Regular, GraphicsUnit.Pixel);
|
||||||
using var labelBrush = new SolidBrush(Color.FromArgb(200, 255, 255, 255));
|
using var labelBrush = new SolidBrush(Color.FromArgb(200, 255, 255, 255));
|
||||||
using var shadowBrush = new SolidBrush(Color.FromArgb(160, 0, 0, 0));
|
using var shadowBrush = new SolidBrush(Color.FromArgb(160, 0, 0, 0));
|
||||||
var sf = new StringFormat { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center };
|
var sf = new StringFormat { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center };
|
||||||
@@ -394,10 +395,13 @@ public partial class SplitDrawingForm : Form
|
|||||||
var r = regions[i];
|
var r = regions[i];
|
||||||
var center = pnlPreview.PointWorldToGraph(r.Center.X, r.Center.Y);
|
var center = pnlPreview.PointWorldToGraph(r.Center.X, r.Center.Y);
|
||||||
var label = (i + 1).ToString();
|
var label = (i + 1).ToString();
|
||||||
|
var dim = $"{r.Width:F2} x {r.Length:F2}";
|
||||||
|
|
||||||
// Shadow offset for readability
|
// Shadow offset for readability
|
||||||
g.DrawString(label, labelFont, shadowBrush, center.X + 1, center.Y + 1, sf);
|
g.DrawString(label, labelFont, shadowBrush, center.X + 1, center.Y - 7, sf);
|
||||||
g.DrawString(label, labelFont, labelBrush, center.X, center.Y, sf);
|
g.DrawString(label, labelFont, labelBrush, center.X, center.Y - 8, sf);
|
||||||
|
g.DrawString(dim, dimFont, shadowBrush, center.X + 1, center.Y + 9, sf);
|
||||||
|
g.DrawString(dim, dimFont, labelBrush, center.X, center.Y + 8, sf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -469,6 +473,33 @@ public partial class SplitDrawingForm : Form
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Placement preview line
|
||||||
|
if (_placingLine && _placingCursor != null)
|
||||||
|
{
|
||||||
|
var isVert = _currentAxis == CutOffAxis.Vertical;
|
||||||
|
var snapped = _placingCursor;
|
||||||
|
var pos = isVert ? snapped.X : snapped.Y;
|
||||||
|
var margin = 10.0;
|
||||||
|
|
||||||
|
PointF pp1, pp2;
|
||||||
|
if (isVert)
|
||||||
|
{
|
||||||
|
pp1 = pnlPreview.PointWorldToGraph(pos, _drawingBounds.Bottom - margin);
|
||||||
|
pp2 = pnlPreview.PointWorldToGraph(pos, _drawingBounds.Top + margin);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pp1 = pnlPreview.PointWorldToGraph(_drawingBounds.Left - margin, pos);
|
||||||
|
pp2 = pnlPreview.PointWorldToGraph(_drawingBounds.Right + margin, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
using var previewPen = new Pen(Color.FromArgb(180, 255, 213, 79), 1.5f);
|
||||||
|
previewPen.DashStyle = DashStyle.DashDot;
|
||||||
|
g.DrawLine(previewPen, pp1, pp2);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// Feature position handles
|
// Feature position handles
|
||||||
if (!radStraight.Checked)
|
if (!radStraight.Checked)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user