fix: Cincinnati post processor arc feedrate, G89 spacing, pallet exchange, and preamble

- Add radius-based arc feedrate calculation (Variables/Percentages modes)
  with configurable radius ranges (#123/#124/#125 or inline expressions)
- Fix arc distance in SpeedClassifier using actual arc length instead of
  chord length (full circles previously computed as zero)
- Fix G89 P spacing: P now adjacent to filename per CL-707 manual syntax
- Add lead-out feedrate support (#129) and arc lead-in feedrate (#127)
- Fix pallet exchange: StartAndEnd emits M50 in preamble + last sheet only
- Add G121 Smart Rapids emission when UseSmartRapids is enabled
- Add G90 absolute mode to main program preamble alongside G20/G21

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-30 09:33:50 -04:00
parent 722f758e94
commit 3d4204db7b
10 changed files with 515 additions and 22 deletions
+39 -1
View File
@@ -1,6 +1,7 @@
using System.Collections.Generic;
using OpenNest.CNC;
using OpenNest.Geometry;
using OpenNest.Math;
namespace OpenNest.Posts.Cincinnati;
@@ -105,11 +106,48 @@ public static class FeatureUtils
}
else if (code is ArcMove arc)
{
distance += currentPos.DistanceTo(arc.EndPoint);
distance += ComputeArcLength(currentPos, arc);
currentPos = arc.EndPoint;
}
}
return distance;
}
/// <summary>
/// Computes the arc length from the current position through an arc move.
/// Uses radius * sweep angle instead of chord length.
/// </summary>
public static double ComputeArcLength(Vector startPos, ArcMove arc)
{
var radius = startPos.DistanceTo(arc.CenterPoint);
if (radius < Tolerance.Epsilon)
return 0.0;
// Full circle: start ≈ end
if (Tolerance.IsEqualTo(startPos.X, arc.EndPoint.X)
&& Tolerance.IsEqualTo(startPos.Y, arc.EndPoint.Y))
return 2.0 * System.Math.PI * radius;
var startAngle = System.Math.Atan2(
startPos.Y - arc.CenterPoint.Y,
startPos.X - arc.CenterPoint.X);
var endAngle = System.Math.Atan2(
arc.EndPoint.Y - arc.CenterPoint.Y,
arc.EndPoint.X - arc.CenterPoint.X);
double sweep;
if (arc.Rotation == RotationType.CW)
{
sweep = startAngle - endAngle;
if (sweep <= 0) sweep += 2.0 * System.Math.PI;
}
else
{
sweep = endAngle - startAngle;
if (sweep <= 0) sweep += 2.0 * System.Math.PI;
}
return radius * sweep;
}
}