feat: add direction arrows and reverse direction to program editor
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -140,4 +140,18 @@ public class ContourClassificationTests
|
|||||||
Assert.Equal(ContourClassification.Perimeter, contours[0].Type);
|
Assert.Equal(ContourClassification.Perimeter, contours[0].Type);
|
||||||
Assert.Equal("Perimeter", contours[0].Label);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ namespace OpenNest.Controls
|
|||||||
contourList.DrawItem += OnDrawContourItem;
|
contourList.DrawItem += OnDrawContourItem;
|
||||||
contourList.MeasureItem += OnMeasureContourItem;
|
contourList.MeasureItem += OnMeasureContourItem;
|
||||||
contourList.SelectedIndexChanged += OnContourSelectionChanged;
|
contourList.SelectedIndexChanged += OnContourSelectionChanged;
|
||||||
|
reverseButton.Click += OnReverseClicked;
|
||||||
|
menuReverse.Click += OnReverseClicked;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Program Program { get; private set; }
|
public Program Program { get; private set; }
|
||||||
@@ -101,6 +103,9 @@ namespace OpenNest.Controls
|
|||||||
entity.Color = color;
|
entity.Color = color;
|
||||||
preview.Entities.Add(entity);
|
preview.Entities.Add(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (selected)
|
||||||
|
AddDirectionArrows(contour.Shape, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
preview.ZoomToFit();
|
preview.ZoomToFit();
|
||||||
@@ -178,5 +183,137 @@ namespace OpenNest.Controls
|
|||||||
{
|
{
|
||||||
RefreshPreview();
|
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 AddDirectionArrows(Shape shape, Color color)
|
||||||
|
{
|
||||||
|
var entities = shape.Entities;
|
||||||
|
if (entities.Count == 0) return;
|
||||||
|
|
||||||
|
var totalLength = shape.Length;
|
||||||
|
if (totalLength < 0.001) return;
|
||||||
|
|
||||||
|
var arrowSize = totalLength * 0.02;
|
||||||
|
if (arrowSize < 0.5) arrowSize = 0.5;
|
||||||
|
if (arrowSize > 5) arrowSize = 5;
|
||||||
|
|
||||||
|
foreach (var fraction in new[] { 0.25, 0.75 })
|
||||||
|
{
|
||||||
|
var targetDist = totalLength * fraction;
|
||||||
|
var accumulated = 0.0;
|
||||||
|
var found = false;
|
||||||
|
|
||||||
|
foreach (var entity in entities)
|
||||||
|
{
|
||||||
|
var entityLen = entity.Length;
|
||||||
|
if (accumulated + entityLen >= targetDist)
|
||||||
|
{
|
||||||
|
var localFraction = (targetDist - accumulated) / entityLen;
|
||||||
|
var (point, angle) = GetPointAndAngle(entity, localFraction);
|
||||||
|
AddArrowHead(point, angle, arrowSize, color);
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
accumulated += entityLen;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found && entities.Count > 0)
|
||||||
|
{
|
||||||
|
var last = entities[^1];
|
||||||
|
var (point, angle) = GetPointAndAngle(last, 0.5);
|
||||||
|
AddArrowHead(point, angle, arrowSize, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static (Vector point, double angle) GetPointAndAngle(Entity entity, double fraction)
|
||||||
|
{
|
||||||
|
switch (entity)
|
||||||
|
{
|
||||||
|
case Line line:
|
||||||
|
{
|
||||||
|
var dx = line.EndPoint.X - line.StartPoint.X;
|
||||||
|
var dy = line.EndPoint.Y - line.StartPoint.Y;
|
||||||
|
var pt = new Vector(
|
||||||
|
line.StartPoint.X + dx * fraction,
|
||||||
|
line.StartPoint.Y + dy * fraction);
|
||||||
|
var angle = System.Math.Atan2(dy, dx);
|
||||||
|
return (pt, angle);
|
||||||
|
}
|
||||||
|
case Arc arc:
|
||||||
|
{
|
||||||
|
var startAngle = arc.StartAngle;
|
||||||
|
var endAngle = arc.EndAngle;
|
||||||
|
if (arc.IsReversed)
|
||||||
|
{
|
||||||
|
var span = startAngle - endAngle;
|
||||||
|
if (span < 0) span += 2 * System.Math.PI;
|
||||||
|
var a = startAngle - span * fraction;
|
||||||
|
var pt = new Vector(
|
||||||
|
arc.Center.X + arc.Radius * System.Math.Cos(a),
|
||||||
|
arc.Center.Y + arc.Radius * System.Math.Sin(a));
|
||||||
|
var tangent = a - System.Math.PI / 2;
|
||||||
|
return (pt, tangent);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var span = endAngle - startAngle;
|
||||||
|
if (span < 0) span += 2 * System.Math.PI;
|
||||||
|
var a = startAngle + span * fraction;
|
||||||
|
var pt = new Vector(
|
||||||
|
arc.Center.X + arc.Radius * System.Math.Cos(a),
|
||||||
|
arc.Center.Y + arc.Radius * System.Math.Sin(a));
|
||||||
|
var tangent = a + System.Math.PI / 2;
|
||||||
|
return (pt, tangent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case Circle circle:
|
||||||
|
{
|
||||||
|
var a = 2 * System.Math.PI * fraction;
|
||||||
|
var pt = new Vector(
|
||||||
|
circle.Center.X + circle.Radius * System.Math.Cos(a),
|
||||||
|
circle.Center.Y + circle.Radius * System.Math.Sin(a));
|
||||||
|
var tangent = a + System.Math.PI / 2;
|
||||||
|
return (pt, tangent);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return (new Vector(), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddArrowHead(Vector tip, double angle, double size, Color color)
|
||||||
|
{
|
||||||
|
var leftAngle = angle + System.Math.PI + 0.4;
|
||||||
|
var rightAngle = angle + System.Math.PI - 0.4;
|
||||||
|
|
||||||
|
var left = new Vector(
|
||||||
|
tip.X + size * System.Math.Cos(leftAngle),
|
||||||
|
tip.Y + size * System.Math.Sin(leftAngle));
|
||||||
|
var right = new Vector(
|
||||||
|
tip.X + size * System.Math.Cos(rightAngle),
|
||||||
|
tip.Y + size * System.Math.Sin(rightAngle));
|
||||||
|
|
||||||
|
var arrowColor = Color.FromArgb(255, 140, 50);
|
||||||
|
preview.Entities.Add(new Line(left, tip) { Color = arrowColor });
|
||||||
|
preview.Entities.Add(new Line(right, tip) { Color = arrowColor });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user