refactor: extract CutDirectionArrows and reuse in program editor preview
This commit is contained in:
@@ -0,0 +1,146 @@
|
|||||||
|
using OpenNest.CNC;
|
||||||
|
using OpenNest.Geometry;
|
||||||
|
using OpenNest.Math;
|
||||||
|
using System;
|
||||||
|
using System.Drawing;
|
||||||
|
|
||||||
|
namespace OpenNest.Controls
|
||||||
|
{
|
||||||
|
internal static class CutDirectionArrows
|
||||||
|
{
|
||||||
|
public static void DrawProgram(Graphics g, DrawControl view, Program pgm, ref Vector pos,
|
||||||
|
Pen pen, double spacing, float arrowSize)
|
||||||
|
{
|
||||||
|
for (var i = 0; i < pgm.Length; ++i)
|
||||||
|
{
|
||||||
|
var code = pgm[i];
|
||||||
|
|
||||||
|
if (code.Type == CodeType.SubProgramCall)
|
||||||
|
{
|
||||||
|
var subpgm = (SubProgramCall)code;
|
||||||
|
if (subpgm.Program != null)
|
||||||
|
DrawProgram(g, view, subpgm.Program, ref pos, pen, spacing, arrowSize);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (code is not Motion motion) continue;
|
||||||
|
|
||||||
|
var endpt = pgm.Mode == Mode.Incremental
|
||||||
|
? motion.EndPoint + pos
|
||||||
|
: motion.EndPoint;
|
||||||
|
|
||||||
|
if (code.Type == CodeType.LinearMove)
|
||||||
|
{
|
||||||
|
var line = (LinearMove)code;
|
||||||
|
if (!line.Suppressed)
|
||||||
|
DrawLineArrows(g, view, pos, endpt, pen, spacing, arrowSize);
|
||||||
|
}
|
||||||
|
else if (code.Type == CodeType.ArcMove)
|
||||||
|
{
|
||||||
|
var arc = (ArcMove)code;
|
||||||
|
if (!arc.Suppressed)
|
||||||
|
{
|
||||||
|
var center = pgm.Mode == Mode.Incremental
|
||||||
|
? arc.CenterPoint + pos
|
||||||
|
: arc.CenterPoint;
|
||||||
|
DrawArcArrows(g, view, pos, endpt, center, arc.Rotation, pen, spacing, arrowSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pos = endpt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DrawLineArrows(Graphics g, DrawControl view, Vector start, Vector end,
|
||||||
|
Pen pen, double spacing, float arrowSize)
|
||||||
|
{
|
||||||
|
var dx = end.X - start.X;
|
||||||
|
var dy = end.Y - start.Y;
|
||||||
|
var length = System.Math.Sqrt(dx * dx + dy * dy);
|
||||||
|
if (length < spacing * 0.5) return;
|
||||||
|
|
||||||
|
var dirX = dx / length;
|
||||||
|
var dirY = dy / length;
|
||||||
|
|
||||||
|
var count = System.Math.Max(1, (int)(length / spacing));
|
||||||
|
var step = length / (count + 1);
|
||||||
|
|
||||||
|
for (var i = 1; i <= count; i++)
|
||||||
|
{
|
||||||
|
var t = step * i;
|
||||||
|
var pt = new Vector(start.X + dirX * t, start.Y + dirY * t);
|
||||||
|
var screenPt = view.PointWorldToGraph(pt);
|
||||||
|
var angle = System.Math.Atan2(-dirY, dirX);
|
||||||
|
DrawArrowHead(g, pen, screenPt, angle, arrowSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DrawArcArrows(Graphics g, DrawControl view, Vector start, Vector end, Vector center,
|
||||||
|
RotationType rotation, Pen pen, double spacing, float arrowSize)
|
||||||
|
{
|
||||||
|
var radius = center.DistanceTo(start);
|
||||||
|
if (radius < Tolerance.Epsilon) return;
|
||||||
|
|
||||||
|
var startAngle = System.Math.Atan2(start.Y - center.Y, start.X - center.X);
|
||||||
|
var endAngle = System.Math.Atan2(end.Y - center.Y, end.X - center.X);
|
||||||
|
|
||||||
|
double sweep;
|
||||||
|
if (rotation == RotationType.CCW)
|
||||||
|
{
|
||||||
|
sweep = endAngle - startAngle;
|
||||||
|
if (sweep <= 0) sweep += 2 * System.Math.PI;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sweep = startAngle - endAngle;
|
||||||
|
if (sweep <= 0) sweep += 2 * System.Math.PI;
|
||||||
|
}
|
||||||
|
|
||||||
|
var arcLength = radius * System.Math.Abs(sweep);
|
||||||
|
if (arcLength < spacing * 0.5) return;
|
||||||
|
|
||||||
|
var count = System.Math.Max(1, (int)(arcLength / spacing));
|
||||||
|
var stepAngle = sweep / (count + 1);
|
||||||
|
|
||||||
|
for (var i = 1; i <= count; i++)
|
||||||
|
{
|
||||||
|
double angle;
|
||||||
|
if (rotation == RotationType.CCW)
|
||||||
|
angle = startAngle + stepAngle * i;
|
||||||
|
else
|
||||||
|
angle = startAngle - stepAngle * i;
|
||||||
|
|
||||||
|
var pt = new Vector(
|
||||||
|
center.X + radius * System.Math.Cos(angle),
|
||||||
|
center.Y + radius * System.Math.Sin(angle));
|
||||||
|
var screenPt = view.PointWorldToGraph(pt);
|
||||||
|
|
||||||
|
double tangent;
|
||||||
|
if (rotation == RotationType.CCW)
|
||||||
|
tangent = angle + System.Math.PI / 2;
|
||||||
|
else
|
||||||
|
tangent = angle - System.Math.PI / 2;
|
||||||
|
|
||||||
|
var screenAngle = System.Math.Atan2(-System.Math.Sin(tangent), System.Math.Cos(tangent));
|
||||||
|
DrawArrowHead(g, pen, screenPt, screenAngle, arrowSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DrawArrowHead(Graphics g, Pen pen, PointF tip, double angle, float size)
|
||||||
|
{
|
||||||
|
var sin = (float)System.Math.Sin(angle);
|
||||||
|
var cos = (float)System.Math.Cos(angle);
|
||||||
|
|
||||||
|
var backX = -size * cos;
|
||||||
|
var backY = -size * sin;
|
||||||
|
var wingX = size * 0.5f * sin;
|
||||||
|
var wingY = -size * 0.5f * cos;
|
||||||
|
|
||||||
|
var wing1 = new PointF(tip.X + backX + wingX, tip.Y + backY + wingY);
|
||||||
|
var wing2 = new PointF(tip.X + backX - wingX, tip.Y + backY - wingY);
|
||||||
|
|
||||||
|
g.DrawLine(pen, wing1, tip);
|
||||||
|
g.DrawLine(pen, wing2, tip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -525,145 +525,10 @@ namespace OpenNest.Controls
|
|||||||
var part = view.Plate.Parts[i];
|
var part = view.Plate.Parts[i];
|
||||||
var pgm = part.Program;
|
var pgm = part.Program;
|
||||||
var pos = part.Location;
|
var pos = part.Location;
|
||||||
DrawProgramCutDirectionArrows(g, pgm, ref pos, pen, arrowSpacingWorld, arrowSize);
|
CutDirectionArrows.DrawProgram(g, view, pgm, ref pos, pen, arrowSpacingWorld, arrowSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawProgramCutDirectionArrows(Graphics g, Program pgm, ref Vector pos,
|
|
||||||
Pen pen, double spacing, float arrowSize)
|
|
||||||
{
|
|
||||||
for (var i = 0; i < pgm.Length; ++i)
|
|
||||||
{
|
|
||||||
var code = pgm[i];
|
|
||||||
|
|
||||||
if (code.Type == CodeType.SubProgramCall)
|
|
||||||
{
|
|
||||||
var subpgm = (SubProgramCall)code;
|
|
||||||
if (subpgm.Program != null)
|
|
||||||
DrawProgramCutDirectionArrows(g, subpgm.Program, ref pos, pen, spacing, arrowSize);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (code is not Motion motion) continue;
|
|
||||||
|
|
||||||
var endpt = pgm.Mode == Mode.Incremental
|
|
||||||
? motion.EndPoint + pos
|
|
||||||
: motion.EndPoint;
|
|
||||||
|
|
||||||
if (code.Type == CodeType.LinearMove)
|
|
||||||
{
|
|
||||||
var line = (LinearMove)code;
|
|
||||||
if (!line.Suppressed)
|
|
||||||
DrawLineDirectionArrows(g, pos, endpt, pen, spacing, arrowSize);
|
|
||||||
}
|
|
||||||
else if (code.Type == CodeType.ArcMove)
|
|
||||||
{
|
|
||||||
var arc = (ArcMove)code;
|
|
||||||
if (!arc.Suppressed)
|
|
||||||
{
|
|
||||||
var center = pgm.Mode == Mode.Incremental
|
|
||||||
? arc.CenterPoint + pos
|
|
||||||
: arc.CenterPoint;
|
|
||||||
DrawArcDirectionArrows(g, pos, endpt, center, arc.Rotation, pen, spacing, arrowSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pos = endpt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawLineDirectionArrows(Graphics g, Vector start, Vector end,
|
|
||||||
Pen pen, double spacing, float arrowSize)
|
|
||||||
{
|
|
||||||
var dx = end.X - start.X;
|
|
||||||
var dy = end.Y - start.Y;
|
|
||||||
var length = System.Math.Sqrt(dx * dx + dy * dy);
|
|
||||||
if (length < spacing * 0.5) return;
|
|
||||||
|
|
||||||
var dirX = dx / length;
|
|
||||||
var dirY = dy / length;
|
|
||||||
|
|
||||||
var count = System.Math.Max(1, (int)(length / spacing));
|
|
||||||
var step = length / (count + 1);
|
|
||||||
|
|
||||||
for (var i = 1; i <= count; i++)
|
|
||||||
{
|
|
||||||
var t = step * i;
|
|
||||||
var pt = new Vector(start.X + dirX * t, start.Y + dirY * t);
|
|
||||||
var screenPt = view.PointWorldToGraph(pt);
|
|
||||||
var angle = System.Math.Atan2(-dirY, dirX);
|
|
||||||
DrawArrowHead(g, pen, screenPt, angle, arrowSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawArcDirectionArrows(Graphics g, Vector start, Vector end, Vector center,
|
|
||||||
RotationType rotation, Pen pen, double spacing, float arrowSize)
|
|
||||||
{
|
|
||||||
var radius = center.DistanceTo(start);
|
|
||||||
if (radius < Tolerance.Epsilon) return;
|
|
||||||
|
|
||||||
var startAngle = System.Math.Atan2(start.Y - center.Y, start.X - center.X);
|
|
||||||
var endAngle = System.Math.Atan2(end.Y - center.Y, end.X - center.X);
|
|
||||||
|
|
||||||
double sweep;
|
|
||||||
if (rotation == RotationType.CCW)
|
|
||||||
{
|
|
||||||
sweep = endAngle - startAngle;
|
|
||||||
if (sweep <= 0) sweep += 2 * System.Math.PI;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sweep = startAngle - endAngle;
|
|
||||||
if (sweep <= 0) sweep += 2 * System.Math.PI;
|
|
||||||
}
|
|
||||||
|
|
||||||
var arcLength = radius * System.Math.Abs(sweep);
|
|
||||||
if (arcLength < spacing * 0.5) return;
|
|
||||||
|
|
||||||
var count = System.Math.Max(1, (int)(arcLength / spacing));
|
|
||||||
var stepAngle = sweep / (count + 1);
|
|
||||||
|
|
||||||
for (var i = 1; i <= count; i++)
|
|
||||||
{
|
|
||||||
double angle;
|
|
||||||
if (rotation == RotationType.CCW)
|
|
||||||
angle = startAngle + stepAngle * i;
|
|
||||||
else
|
|
||||||
angle = startAngle - stepAngle * i;
|
|
||||||
|
|
||||||
var pt = new Vector(
|
|
||||||
center.X + radius * System.Math.Cos(angle),
|
|
||||||
center.Y + radius * System.Math.Sin(angle));
|
|
||||||
var screenPt = view.PointWorldToGraph(pt);
|
|
||||||
|
|
||||||
double tangent;
|
|
||||||
if (rotation == RotationType.CCW)
|
|
||||||
tangent = angle + System.Math.PI / 2;
|
|
||||||
else
|
|
||||||
tangent = angle - System.Math.PI / 2;
|
|
||||||
|
|
||||||
var screenAngle = System.Math.Atan2(-System.Math.Sin(tangent), System.Math.Cos(tangent));
|
|
||||||
DrawArrowHead(g, pen, screenPt, screenAngle, arrowSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void DrawArrowHead(Graphics g, Pen pen, PointF tip, double angle, float size)
|
|
||||||
{
|
|
||||||
var sin = (float)System.Math.Sin(angle);
|
|
||||||
var cos = (float)System.Math.Cos(angle);
|
|
||||||
|
|
||||||
var backX = -size * cos;
|
|
||||||
var backY = -size * sin;
|
|
||||||
var wingX = size * 0.5f * sin;
|
|
||||||
var wingY = -size * 0.5f * cos;
|
|
||||||
|
|
||||||
var wing1 = new PointF(tip.X + backX + wingX, tip.Y + backY + wingY);
|
|
||||||
var wing2 = new PointF(tip.X + backX - wingX, tip.Y + backY - wingY);
|
|
||||||
|
|
||||||
g.DrawLine(pen, wing1, tip);
|
|
||||||
g.DrawLine(pen, wing2, tip);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawLine(Graphics g, Vector pt1, Vector pt2, Pen pen)
|
private void DrawLine(Graphics g, Vector pt1, Vector pt2, Pen pen)
|
||||||
{
|
{
|
||||||
var point1 = view.PointWorldToGraph(pt1);
|
var point1 = view.PointWorldToGraph(pt1);
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ namespace OpenNest.Controls
|
|||||||
reverseButton.Click += OnReverseClicked;
|
reverseButton.Click += OnReverseClicked;
|
||||||
menuReverse.Click += OnReverseClicked;
|
menuReverse.Click += OnReverseClicked;
|
||||||
applyButton.Click += OnApplyClicked;
|
applyButton.Click += OnApplyClicked;
|
||||||
|
preview.Paint += OnPreviewPaint;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Program Program { get; private set; }
|
public Program Program { get; private set; }
|
||||||
@@ -157,7 +158,6 @@ namespace OpenNest.Controls
|
|||||||
var selColor = GetContourColor(contour.Type, true);
|
var selColor = GetContourColor(contour.Type, true);
|
||||||
foreach (var entity in contour.Shape.Entities)
|
foreach (var entity in contour.Shape.Entities)
|
||||||
entity.Color = selColor;
|
entity.Color = selColor;
|
||||||
AddDirectionArrows(contour.Shape, selColor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
preview.Entities.AddRange(contour.Shape.Entities);
|
preview.Entities.AddRange(contour.Shape.Entities);
|
||||||
@@ -258,119 +258,27 @@ namespace OpenNest.Controls
|
|||||||
ProgramChanged?.Invoke(this, EventArgs.Empty);
|
ProgramChanged?.Invoke(this, EventArgs.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddDirectionArrows(Shape shape, Color color)
|
private void OnPreviewPaint(object sender, PaintEventArgs e)
|
||||||
{
|
{
|
||||||
var entities = shape.Entities;
|
if (contours.Count == 0) return;
|
||||||
if (entities.Count == 0) return;
|
|
||||||
|
|
||||||
var totalLength = shape.Length;
|
var spacing = preview.LengthGuiToWorld(60f);
|
||||||
if (totalLength < 0.001) return;
|
var arrowSize = 5f;
|
||||||
|
|
||||||
var arrowSize = totalLength * 0.02;
|
for (var i = 0; i < contours.Count; i++)
|
||||||
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;
|
if (!contourList.SelectedIndices.Contains(i)) continue;
|
||||||
var accumulated = 0.0;
|
|
||||||
var found = false;
|
|
||||||
|
|
||||||
foreach (var entity in entities)
|
var contour = contours[i];
|
||||||
{
|
var pgm = ConvertGeometry.ToProgram(contour.Shape);
|
||||||
var entityLen = entity.Length;
|
if (pgm == null) continue;
|
||||||
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)
|
using var pen = new Pen(Color.FromArgb(220, Color.Black), 1.5f);
|
||||||
{
|
var pos = new Vector();
|
||||||
var last = entities[^1];
|
CutDirectionArrows.DrawProgram(e.Graphics, preview, pgm, ref pos, pen, spacing, arrowSize);
|
||||||
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 });
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnApplyClicked(object sender, EventArgs e)
|
private void OnApplyClicked(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
var text = gcodeEditor.Text;
|
var text = gcodeEditor.Text;
|
||||||
|
|||||||
Reference in New Issue
Block a user