diff --git a/OpenNest.Core/Bending/Bend.cs b/OpenNest.Core/Bending/Bend.cs
index 50c8b582..0c0c7dbc 100644
--- a/OpenNest.Core/Bending/Bend.cs
+++ b/OpenNest.Core/Bending/Bend.cs
@@ -62,8 +62,8 @@ namespace OpenNest.Bending
else
{
var angle = StartPoint.AngleTo(EndPoint);
- var dx = System.Math.Cos(angle) * etchLength;
- var dy = System.Math.Sin(angle) * etchLength;
+ var dx = PortableMath.Cos(angle) * etchLength;
+ var dy = PortableMath.Sin(angle) * etchLength;
result.Add(
CreateEtchLine(StartPoint, new Vector(StartPoint.X + dx, StartPoint.Y + dy))
diff --git a/OpenNest.Core/Breakoffs/BreakoffPath.cs b/OpenNest.Core/Breakoffs/BreakoffPath.cs
index 3ea6a718..ca37e9f1 100644
--- a/OpenNest.Core/Breakoffs/BreakoffPath.cs
+++ b/OpenNest.Core/Breakoffs/BreakoffPath.cs
@@ -153,8 +153,8 @@ namespace OpenNest.Breakoffs
}
var arc = (Arc)e;
var angle = ArcAngleAt(arc, local);
- return new Vector(arc.Center.X + arc.Radius * System.Math.Cos(angle),
- arc.Center.Y + arc.Radius * System.Math.Sin(angle));
+ return new Vector(arc.Center.X + arc.Radius * PortableMath.Cos(angle),
+ arc.Center.Y + arc.Radius * PortableMath.Sin(angle));
}
private static Vector TangentOn(Entity e, double local)
@@ -168,8 +168,8 @@ namespace OpenNest.Breakoffs
var arc = (Arc)e;
var angle = ArcAngleAt(arc, local);
return arc.IsReversed
- ? new Vector(System.Math.Sin(angle), -System.Math.Cos(angle))
- : new Vector(-System.Math.Sin(angle), System.Math.Cos(angle));
+ ? new Vector(PortableMath.Sin(angle), -PortableMath.Cos(angle))
+ : new Vector(-PortableMath.Sin(angle), PortableMath.Cos(angle));
}
private static double ArcAngleAt(Arc arc, double local) =>
diff --git a/OpenNest.Core/CNC/CutSimulation.cs b/OpenNest.Core/CNC/CutSimulation.cs
index 0a230d2d..8824eb3a 100644
--- a/OpenNest.Core/CNC/CutSimulation.cs
+++ b/OpenNest.Core/CNC/CutSimulation.cs
@@ -123,7 +123,7 @@ namespace OpenNest.CNC
{
var angle = StartAngle + Sweep * fraction;
var sign = System.Math.Sign(Sweep);
- return new Vector(-System.Math.Sin(angle) * sign, System.Math.Cos(angle) * sign);
+ return new Vector(-PortableMath.Sin(angle) * sign, PortableMath.Cos(angle) * sign);
}
return Length > 0 ? new Vector((End.X - Start.X) / Length, (End.Y - Start.Y) / Length) : Vector.Zero;
}
@@ -137,8 +137,8 @@ namespace OpenNest.CNC
{
var angle = StartAngle + Sweep * fraction;
return new Vector(
- Center.X + Radius * System.Math.Cos(angle),
- Center.Y + Radius * System.Math.Sin(angle));
+ Center.X + Radius * PortableMath.Cos(angle),
+ Center.Y + Radius * PortableMath.Sin(angle));
}
return new Vector(
diff --git a/OpenNest.Core/CNC/CuttingPlanning/PreparedContours.cs b/OpenNest.Core/CNC/CuttingPlanning/PreparedContours.cs
index cf33876e..38f1bb7b 100644
--- a/OpenNest.Core/CNC/CuttingPlanning/PreparedContours.cs
+++ b/OpenNest.Core/CNC/CuttingPlanning/PreparedContours.cs
@@ -5,6 +5,7 @@ using System.Threading;
using OpenNest.CNC.CuttingStrategy;
using OpenNest.Diagnostics;
using OpenNest.Geometry;
+using OpenNest.Math;
using OpenNest.Support;
namespace OpenNest.CNC.CuttingPlanning;
@@ -511,8 +512,8 @@ public sealed class PreparedContours
if (entity is Circle circle)
{
for (var angle = 0; angle < 8 && entries.Count < maxEntries; angle++)
- Add(i, circle.Center + new Vector(System.Math.Cos(angle * System.Math.PI / 4),
- System.Math.Sin(angle * System.Math.PI / 4)) * circle.Radius);
+ Add(i, circle.Center + new Vector(PortableMath.Cos(angle * System.Math.PI / 4),
+ PortableMath.Sin(angle * System.Math.PI / 4)) * circle.Radius);
}
else
{
@@ -764,8 +765,8 @@ public sealed class PreparedContours
// The legacy eight compass points, same native construction.
for (var angle = 0; angle < 8; angle++)
fallbacks.Add(Fallback(i, circle.Center + new Vector(
- System.Math.Cos(angle * System.Math.PI / 4),
- System.Math.Sin(angle * System.Math.PI / 4)) * circle.Radius,
+ PortableMath.Cos(angle * System.Math.PI / 4),
+ PortableMath.Sin(angle * System.Math.PI / 4)) * circle.Radius,
AutomaticEntryKind.CircleCompass));
break;
}
@@ -977,7 +978,7 @@ public sealed class PreparedContours
if (ContourCuttingStrategy.ClassifyTurn((before.EndPoint - before.StartPoint).Normalize(),
(after.EndPoint - after.StartPoint).Normalize(), winding) != ContourCuttingStrategy.CornerKind.Convex)
return null;
- var reach = arc.Radius * System.Math.Tan(sweep / 2);
+ var reach = arc.Radius * PortableMath.Tan(sweep / 2);
return reach <= System.Math.Min(before.Length, after.Length) ? reach : null;
bool IsSmooth(Vector vertex, Entity entity) =>
diff --git a/OpenNest.Core/CNC/CuttingPlanning/SeamOvercut.cs b/OpenNest.Core/CNC/CuttingPlanning/SeamOvercut.cs
index 687feb6c..db1b1a86 100644
--- a/OpenNest.Core/CNC/CuttingPlanning/SeamOvercut.cs
+++ b/OpenNest.Core/CNC/CuttingPlanning/SeamOvercut.cs
@@ -163,8 +163,8 @@ internal static class SeamOvercut
/// The angle traveled around from one point to another, in [0, 2pi).
private static double Travel(Vector center, Vector from, Vector to, bool clockwise)
{
- var start = System.Math.Atan2(from.Y - center.Y, from.X - center.X);
- var end = System.Math.Atan2(to.Y - center.Y, to.X - center.X);
+ var start = PortableMath.Atan2(from.Y - center.Y, from.X - center.X);
+ var end = PortableMath.Atan2(to.Y - center.Y, to.X - center.X);
var travel = (clockwise ? start - end : end - start) % (2 * System.Math.PI);
return travel < 0 ? travel + 2 * System.Math.PI : travel;
}
diff --git a/OpenNest.Core/CNC/CuttingStrategy/ContourCuttingStrategy.cs b/OpenNest.Core/CNC/CuttingStrategy/ContourCuttingStrategy.cs
index 84d9eaf5..1aec05c0 100644
--- a/OpenNest.Core/CNC/CuttingStrategy/ContourCuttingStrategy.cs
+++ b/OpenNest.Core/CNC/CuttingStrategy/ContourCuttingStrategy.cs
@@ -516,8 +516,8 @@ namespace OpenNest.CNC.CuttingStrategy
var outwardAngle = normal - System.Math.PI;
point = new Vector(
- circle.Center.X + circle.Radius * System.Math.Cos(outwardAngle),
- circle.Center.Y + circle.Radius * System.Math.Sin(outwardAngle)
+ circle.Center.X + circle.Radius * PortableMath.Cos(outwardAngle),
+ circle.Center.Y + circle.Radius * PortableMath.Sin(outwardAngle)
);
}
@@ -974,13 +974,13 @@ namespace OpenNest.CNC.CuttingStrategy
var adjacentNormal = ComputeNormal(point, corner.Incoming, contourType, winding);
// Sum unit normals rather than averaging angles (which fails at 0/2Ï€).
// Winding makes this point into the scrap even at reflex corners.
- var x = System.Math.Cos(normal) + System.Math.Cos(adjacentNormal);
- var y = System.Math.Sin(normal) + System.Math.Sin(adjacentNormal);
+ var x = PortableMath.Cos(normal) + PortableMath.Cos(adjacentNormal);
+ var y = PortableMath.Sin(normal) + PortableMath.Sin(adjacentNormal);
if (!double.IsFinite(x) || !double.IsFinite(y)
|| x * x + y * y <= Tolerance.Epsilon * Tolerance.Epsilon)
return null; // Opposing normals at a cusp have no unique bisector.
- return Angle.NormalizeRad(System.Math.Atan2(y, x));
+ return Angle.NormalizeRad(PortableMath.Atan2(y, x));
}
///
@@ -1009,12 +1009,12 @@ namespace OpenNest.CNC.CuttingStrategy
|| ClassifyTurn(tangentEnd, tangentStart, winding) != CornerKind.Convex)
return null;
- var x = System.Math.Cos(startNormal) + System.Math.Cos(endNormal);
- var y = System.Math.Sin(startNormal) + System.Math.Sin(endNormal);
+ var x = PortableMath.Cos(startNormal) + PortableMath.Cos(endNormal);
+ var y = PortableMath.Sin(startNormal) + PortableMath.Sin(endNormal);
if (x * x + y * y <= Tolerance.Epsilon * Tolerance.Epsilon)
return null;
- var normal = Angle.NormalizeRad(System.Math.Atan2(y, x));
+ var normal = Angle.NormalizeRad(PortableMath.Atan2(y, x));
var pierce = leadIn.GetPiercePoint(start, normal, turn);
var leadOutEnd = ((LinearMove)leadOut.Generate(end, normal, turn)[0]).EndPoint;
return IsClearStraightLead(shape, start, pierce, clearance)
@@ -1248,7 +1248,7 @@ namespace OpenNest.CNC.CuttingStrategy
var length = pierce.DistanceTo(point);
if (length <= Math.Tolerance.Epsilon)
return leadIn;
- var direction = System.Math.Atan2(pierce.Y - point.Y, pierce.X - point.X);
+ var direction = PortableMath.Atan2(pierce.Y - point.Y, pierce.X - point.X);
return new LineLeadIn
{
Length = length,
@@ -1352,7 +1352,7 @@ namespace OpenNest.CNC.CuttingStrategy
private static double Clearance(Shape hole, Vector point) => hole.ClosestPointTo(point, out _).DistanceTo(point);
- private static Vector Inward(double normal) => new(System.Math.Cos(normal), System.Math.Sin(normal));
+ private static Vector Inward(double normal) => new(PortableMath.Cos(normal), PortableMath.Sin(normal));
///
/// A straight lead-in from to , with the
@@ -1360,7 +1360,7 @@ namespace OpenNest.CNC.CuttingStrategy
///
private static LeadIn StraightLeadIn(Vector point, Vector pierce, out double normal)
{
- normal = System.Math.Atan2(pierce.Y - point.Y, pierce.X - point.X);
+ normal = PortableMath.Atan2(pierce.Y - point.Y, pierce.X - point.X);
return new LineLeadIn { Length = pierce.DistanceTo(point), ApproachAngle = 90 };
}
@@ -1397,7 +1397,7 @@ namespace OpenNest.CNC.CuttingStrategy
if (shape.Entities is [Circle round] && round.Radius > tabSize / 2)
{
var entry = round.Center.AngleTo(center);
- var gap = 2 * System.Math.Asin(tabSize / (2 * round.Radius));
+ var gap = 2 * PortableMath.Asin(tabSize / (2 * round.Radius));
var reversed = round.Rotation == RotationType.CW;
var arc = new Arc(round.Center, round.Radius, entry, reversed ? entry + gap : entry - gap, reversed)
{
@@ -1577,7 +1577,7 @@ namespace OpenNest.CNC.CuttingStrategy
path = (Shape)cut.Clone();
path.Reverse();
}
- var normal = Angle.NormalizeRad(System.Math.Atan2(scrap.Y, scrap.X));
+ var normal = Angle.NormalizeRad(PortableMath.Atan2(scrap.Y, scrap.X));
program.Codes.AddRange(leadIn.Generate(entry, normal, fromEnd ? RotationType.CW : RotationType.CCW));
program.Codes.AddRange(ConvertShapeToMoves(path, entry, LayerType.Break));
}
@@ -1782,9 +1782,9 @@ namespace OpenNest.CNC.CuttingStrategy
private static bool OnArc(Vector center, Vector start, Vector end, Vector point, RotationType rotation)
{
- var a0 = System.Math.Atan2(start.Y - center.Y, start.X - center.X);
- var a1 = System.Math.Atan2(end.Y - center.Y, end.X - center.X);
- var ap = System.Math.Atan2(point.Y - center.Y, point.X - center.X);
+ var a0 = PortableMath.Atan2(start.Y - center.Y, start.X - center.X);
+ var a1 = PortableMath.Atan2(end.Y - center.Y, end.X - center.X);
+ var ap = PortableMath.Atan2(point.Y - center.Y, point.X - center.X);
var sign = rotation == RotationType.CCW ? 1 : -1;
var sweep = Angle.NormalizeRad(sign * (a1 - a0));
if (sweep <= Tolerance.Epsilon)
@@ -1841,8 +1841,8 @@ namespace OpenNest.CNC.CuttingStrategy
{
var angle = atEnd ? arc.EndAngle : arc.StartAngle;
return arc.IsReversed
- ? new Vector(System.Math.Sin(angle), -System.Math.Cos(angle))
- : new Vector(-System.Math.Sin(angle), System.Math.Cos(angle));
+ ? new Vector(PortableMath.Sin(angle), -PortableMath.Cos(angle))
+ : new Vector(-PortableMath.Sin(angle), PortableMath.Cos(angle));
}
var line = (Line)entity;
return line.EndPoint - line.StartPoint;
diff --git a/OpenNest.Core/CNC/CuttingStrategy/LeadIns/ArcLeadIn.cs b/OpenNest.Core/CNC/CuttingStrategy/LeadIns/ArcLeadIn.cs
index 63bdc80c..8e8524c2 100644
--- a/OpenNest.Core/CNC/CuttingStrategy/LeadIns/ArcLeadIn.cs
+++ b/OpenNest.Core/CNC/CuttingStrategy/LeadIns/ArcLeadIn.cs
@@ -46,8 +46,8 @@ namespace OpenNest.CNC.CuttingStrategy
var angle = contourNormalAngle + (winding == RotationType.CCW ? shortfall : -shortfall);
var center = ArcCenter(contourStartPoint, contourNormalAngle);
return new Vector(
- center.X + Radius * System.Math.Cos(angle),
- center.Y + Radius * System.Math.Sin(angle)
+ center.X + Radius * PortableMath.Cos(angle),
+ center.Y + Radius * PortableMath.Sin(angle)
);
}
@@ -55,8 +55,8 @@ namespace OpenNest.CNC.CuttingStrategy
new ArcLeadIn { Radius = Radius * factor, SweepAngle = SweepAngle };
private Vector ArcCenter(Vector contourStartPoint, double contourNormalAngle) => new Vector(
- contourStartPoint.X + Radius * System.Math.Cos(contourNormalAngle),
- contourStartPoint.Y + Radius * System.Math.Sin(contourNormalAngle)
+ contourStartPoint.X + Radius * PortableMath.Cos(contourNormalAngle),
+ contourStartPoint.Y + Radius * PortableMath.Sin(contourNormalAngle)
);
}
}
diff --git a/OpenNest.Core/CNC/CuttingStrategy/LeadIns/LineArcApproach.cs b/OpenNest.Core/CNC/CuttingStrategy/LeadIns/LineArcApproach.cs
index 35e683e3..679049b6 100644
--- a/OpenNest.Core/CNC/CuttingStrategy/LeadIns/LineArcApproach.cs
+++ b/OpenNest.Core/CNC/CuttingStrategy/LeadIns/LineArcApproach.cs
@@ -28,22 +28,22 @@ namespace OpenNest.CNC.CuttingStrategy
public static LineArcApproach Create(Vector contourStartPoint, double contourNormalAngle,
double arcRadius, double approachAngle, double lineLength, RotationType winding)
{
- var arcCenterX = contourStartPoint.X + arcRadius * System.Math.Cos(contourNormalAngle);
- var arcCenterY = contourStartPoint.Y + arcRadius * System.Math.Sin(contourNormalAngle);
+ var arcCenterX = contourStartPoint.X + arcRadius * PortableMath.Cos(contourNormalAngle);
+ var arcCenterY = contourStartPoint.Y + arcRadius * PortableMath.Sin(contourNormalAngle);
// The approach angle is measured back from the normal against the turn, so the arc
// runs the short way round to the contour point whichever way it turns.
var approach = Angle.ToRadians(approachAngle);
var lineAngle = contourNormalAngle + (winding == RotationType.CCW ? approach : -approach);
- var arcStartX = arcCenterX + arcRadius * System.Math.Cos(lineAngle);
- var arcStartY = arcCenterY + arcRadius * System.Math.Sin(lineAngle);
+ var arcStartX = arcCenterX + arcRadius * PortableMath.Cos(lineAngle);
+ var arcStartY = arcCenterY + arcRadius * PortableMath.Sin(lineAngle);
return new LineArcApproach(
new Vector(arcCenterX, arcCenterY),
new Vector(arcStartX, arcStartY),
new Vector(
- arcStartX + lineLength * System.Math.Cos(lineAngle),
- arcStartY + lineLength * System.Math.Sin(lineAngle)));
+ arcStartX + lineLength * PortableMath.Cos(lineAngle),
+ arcStartY + lineLength * PortableMath.Sin(lineAngle)));
}
/// Rapid to the pierce point, then the line and arc on the lead-in layer.
diff --git a/OpenNest.Core/CNC/CuttingStrategy/LeadIns/LineLeadIn.cs b/OpenNest.Core/CNC/CuttingStrategy/LeadIns/LineLeadIn.cs
index 2219dd3c..0d0d4560 100644
--- a/OpenNest.Core/CNC/CuttingStrategy/LeadIns/LineLeadIn.cs
+++ b/OpenNest.Core/CNC/CuttingStrategy/LeadIns/LineLeadIn.cs
@@ -29,8 +29,8 @@ namespace OpenNest.CNC.CuttingStrategy
{
var approachAngle = contourNormalAngle - Angle.HalfPI + Angle.ToRadians(ApproachAngle);
return new Vector(
- contourStartPoint.X + Length * System.Math.Cos(approachAngle),
- contourStartPoint.Y + Length * System.Math.Sin(approachAngle)
+ contourStartPoint.X + Length * PortableMath.Cos(approachAngle),
+ contourStartPoint.Y + Length * PortableMath.Sin(approachAngle)
);
}
diff --git a/OpenNest.Core/CNC/CuttingStrategy/LeadIns/LineLineLeadIn.cs b/OpenNest.Core/CNC/CuttingStrategy/LeadIns/LineLineLeadIn.cs
index 0c537dc7..e1aad60e 100644
--- a/OpenNest.Core/CNC/CuttingStrategy/LeadIns/LineLineLeadIn.cs
+++ b/OpenNest.Core/CNC/CuttingStrategy/LeadIns/LineLineLeadIn.cs
@@ -21,8 +21,8 @@ namespace OpenNest.CNC.CuttingStrategy
var secondAngle = contourNormalAngle - Angle.HalfPI + Angle.ToRadians(ApproachAngle1);
var midPoint = new Vector(
- contourStartPoint.X + Length2 * System.Math.Cos(secondAngle),
- contourStartPoint.Y + Length2 * System.Math.Sin(secondAngle)
+ contourStartPoint.X + Length2 * PortableMath.Cos(secondAngle),
+ contourStartPoint.Y + Length2 * PortableMath.Sin(secondAngle)
);
return new List
@@ -37,13 +37,13 @@ namespace OpenNest.CNC.CuttingStrategy
RotationType winding = RotationType.CW)
{
var secondAngle = contourNormalAngle - Angle.HalfPI + Angle.ToRadians(ApproachAngle1);
- var midX = contourStartPoint.X + Length2 * System.Math.Cos(secondAngle);
- var midY = contourStartPoint.Y + Length2 * System.Math.Sin(secondAngle);
+ var midX = contourStartPoint.X + Length2 * PortableMath.Cos(secondAngle);
+ var midY = contourStartPoint.Y + Length2 * PortableMath.Sin(secondAngle);
var firstAngle = secondAngle + Angle.ToRadians(ApproachAngle2);
return new Vector(
- midX + Length1 * System.Math.Cos(firstAngle),
- midY + Length1 * System.Math.Sin(firstAngle)
+ midX + Length1 * PortableMath.Cos(firstAngle),
+ midY + Length1 * PortableMath.Sin(firstAngle)
);
}
diff --git a/OpenNest.Core/CNC/CuttingStrategy/LeadOuts/ArcLeadOut.cs b/OpenNest.Core/CNC/CuttingStrategy/LeadOuts/ArcLeadOut.cs
index 346ee846..1acd1fb7 100644
--- a/OpenNest.Core/CNC/CuttingStrategy/LeadOuts/ArcLeadOut.cs
+++ b/OpenNest.Core/CNC/CuttingStrategy/LeadOuts/ArcLeadOut.cs
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using OpenNest.Geometry;
+using OpenNest.Math;
namespace OpenNest.CNC.CuttingStrategy
{
@@ -17,16 +18,16 @@ namespace OpenNest.CNC.CuttingStrategy
RotationType winding = RotationType.CW
)
{
- var arcCenterX = contourEndPoint.X + Radius * System.Math.Cos(contourNormalAngle);
- var arcCenterY = contourEndPoint.Y + Radius * System.Math.Sin(contourNormalAngle);
+ var arcCenterX = contourEndPoint.X + Radius * PortableMath.Cos(contourNormalAngle);
+ var arcCenterY = contourEndPoint.Y + Radius * PortableMath.Sin(contourNormalAngle);
var arcCenter = new Vector(arcCenterX, arcCenterY);
// The contour point sits opposite the normal from the center; turn a quarter on.
var endAngle = contourNormalAngle
+ (winding == RotationType.CCW ? -System.Math.PI / 2 : System.Math.PI / 2);
var endPoint = new Vector(
- arcCenterX + Radius * System.Math.Cos(endAngle),
- arcCenterY + Radius * System.Math.Sin(endAngle)
+ arcCenterX + Radius * PortableMath.Cos(endAngle),
+ arcCenterY + Radius * PortableMath.Sin(endAngle)
);
return new List
diff --git a/OpenNest.Core/CNC/CuttingStrategy/LeadOuts/LineLeadOut.cs b/OpenNest.Core/CNC/CuttingStrategy/LeadOuts/LineLeadOut.cs
index a7bafa6d..1a738422 100644
--- a/OpenNest.Core/CNC/CuttingStrategy/LeadOuts/LineLeadOut.cs
+++ b/OpenNest.Core/CNC/CuttingStrategy/LeadOuts/LineLeadOut.cs
@@ -17,8 +17,8 @@ namespace OpenNest.CNC.CuttingStrategy
{
var overcutAngle = contourNormalAngle + Angle.HalfPI - Angle.ToRadians(ApproachAngle);
var endPoint = new Vector(
- contourEndPoint.X + Length * System.Math.Cos(overcutAngle),
- contourEndPoint.Y + Length * System.Math.Sin(overcutAngle)
+ contourEndPoint.X + Length * PortableMath.Cos(overcutAngle),
+ contourEndPoint.Y + Length * PortableMath.Sin(overcutAngle)
);
return new List { new LinearMove(endPoint) { Layer = LayerType.Leadout } };
diff --git a/OpenNest.Core/CNC/CuttingStrategy/Tabs/BreakerTab.cs b/OpenNest.Core/CNC/CuttingStrategy/Tabs/BreakerTab.cs
index 3f7b986a..e28c881d 100644
--- a/OpenNest.Core/CNC/CuttingStrategy/Tabs/BreakerTab.cs
+++ b/OpenNest.Core/CNC/CuttingStrategy/Tabs/BreakerTab.cs
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using OpenNest.Geometry;
+using OpenNest.Math;
namespace OpenNest.CNC.CuttingStrategy
{
@@ -23,8 +24,8 @@ namespace OpenNest.CNC.CuttingStrategy
var scoreAngle = contourNormalAngle + System.Math.PI;
var scoreEnd = new Vector(
- tabStartPoint.X + BreakerDepth * System.Math.Cos(scoreAngle),
- tabStartPoint.Y + BreakerDepth * System.Math.Sin(scoreAngle)
+ tabStartPoint.X + BreakerDepth * PortableMath.Cos(scoreAngle),
+ tabStartPoint.Y + BreakerDepth * PortableMath.Sin(scoreAngle)
);
codes.Add(new LinearMove(scoreEnd));
codes.Add(new RapidMove(tabEndPoint));
diff --git a/OpenNest.Core/CNC/Program.cs b/OpenNest.Core/CNC/Program.cs
index 4d36d64e..3bed0436 100644
--- a/OpenNest.Core/CNC/Program.cs
+++ b/OpenNest.Core/CNC/Program.cs
@@ -103,8 +103,8 @@ namespace OpenNest.CNC
if (subpgm.Offset.X != 0 || subpgm.Offset.Y != 0)
{
- var cos = System.Math.Cos(angle);
- var sin = System.Math.Sin(angle);
+ var cos = PortableMath.Cos(angle);
+ var sin = PortableMath.Sin(angle);
var dx = subpgm.Offset.X - origin.X;
var dy = subpgm.Offset.Y - origin.Y;
subpgm.Offset = new Geometry.Vector(
diff --git a/OpenNest.Core/CanonicalAngle.cs b/OpenNest.Core/CanonicalAngle.cs
index 0fa9733a..22fbf895 100644
--- a/OpenNest.Core/CanonicalAngle.cs
+++ b/OpenNest.Core/CanonicalAngle.cs
@@ -1,6 +1,7 @@
using System.Linq;
using OpenNest.Converters;
using OpenNest.Geometry;
+using OpenNest.Math;
namespace OpenNest
{
@@ -123,7 +124,7 @@ namespace OpenNest
// keeps offsets lying exactly on an axis (mirror-symmetric parts) away from the
// interval edge so floating-point noise cannot flip the choice.
var halfPi = System.Math.PI / 2.0;
- var direction = System.Math.Atan2(dy, dx);
+ var direction = PortableMath.Atan2(dy, dx);
for (var turns = 0; turns < 4; turns++)
{
var relative = direction + turns * halfPi - (System.Math.PI - PreferenceMargin);
diff --git a/OpenNest.Core/CommonLineCutting.cs b/OpenNest.Core/CommonLineCutting.cs
index df623d95..42e9b873 100644
--- a/OpenNest.Core/CommonLineCutting.cs
+++ b/OpenNest.Core/CommonLineCutting.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using OpenNest.CNC;
using OpenNest.Geometry;
+using OpenNest.Math;
namespace OpenNest
{
@@ -238,8 +239,8 @@ namespace OpenNest
{
var din = (points[k] - points[k - 1]).Normalize();
var dout = (points[k + 1] - points[k]).Normalize();
- var turn = System.Math.Atan2(System.Math.Abs(Cross(din, dout)), Dot(din, dout));
- snaps[k] = kerf / 2 * System.Math.Tan(turn / 2) + 2 * CommonLineGeometry.GapTolerance;
+ var turn = PortableMath.Atan2(System.Math.Abs(Cross(din, dout)), Dot(din, dout));
+ snaps[k] = kerf / 2 * PortableMath.Tan(turn / 2) + 2 * CommonLineGeometry.GapTolerance;
}
for (var k = 0; k + 1 < points.Length; k++)
yield return new Leg(points[k], points[k + 1], snaps[k], snaps[k + 1]);
diff --git a/OpenNest.Core/CommonLineGeometry.cs b/OpenNest.Core/CommonLineGeometry.cs
index f56add66..72644178 100644
--- a/OpenNest.Core/CommonLineGeometry.cs
+++ b/OpenNest.Core/CommonLineGeometry.cs
@@ -4,6 +4,7 @@ using System.Linq;
using OpenNest.CNC;
using OpenNest.Diagnostics;
using OpenNest.Geometry;
+using OpenNest.Math;
namespace OpenNest
{
@@ -161,7 +162,7 @@ namespace OpenNest
Vector offset, double kerf, double maxGap)
{
var along = chosen.First.Direction;
- var minimum = System.Math.Sin(MinCornerTurn);
+ var minimum = PortableMath.Sin(MinCornerTurn);
var shifted = Shift(moving, offset);
var slides = FacingEdges(anchor, shifted, -maxGap, maxGap)
.Select(facing => (Facing: facing, Across: Dot(along, facing.First.Outward)))
@@ -266,14 +267,14 @@ namespace OpenNest
var din = new Vector((aEnd.X - aOther.X) / aLength, (aEnd.Y - aOther.Y) / aLength);
var dout = new Vector((bOther.X - bEnd.X) / bLength, (bOther.Y - bEnd.Y) / bLength);
var cross = Cross(din, dout);
- var turn = System.Math.Atan2(System.Math.Abs(cross), Dot(din, dout));
+ var turn = PortableMath.Atan2(System.Math.Abs(cross), Dot(din, dout));
if (turn < MinCornerTurn || turn > MaxCornerTurn)
return null;
// aEnd + din·s = bEnd − dout·t: the corner lies s past a's end and t before b's end.
var gap = bEnd - aEnd;
var s = Cross(gap, dout) / cross;
var t = Cross(din, gap) / cross;
- var reach = kerf / 2 * System.Math.Tan(turn / 2) + 2 * GapTolerance;
+ var reach = kerf / 2 * PortableMath.Tan(turn / 2) + 2 * GapTolerance;
if (s < -GapTolerance || t < -GapTolerance || s > reach || t > reach)
return null;
return new Vector(aEnd.X + din.X * s, aEnd.Y + din.Y * s);
diff --git a/OpenNest.Core/Diagnostics/PostVerificationGeometry.cs b/OpenNest.Core/Diagnostics/PostVerificationGeometry.cs
index acde64d9..9f468316 100644
--- a/OpenNest.Core/Diagnostics/PostVerificationGeometry.cs
+++ b/OpenNest.Core/Diagnostics/PostVerificationGeometry.cs
@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Threading;
using OpenNest.Geometry;
+using OpenNest.Math;
namespace OpenNest.Diagnostics;
@@ -243,8 +244,8 @@ internal static class PostVerificationGeometry
System.Math.Max(Start.X, End.X), System.Math.Max(Start.Y, End.Y));
internal Vector Midpoint => Center is { } center
- ? new Vector(center.X + Radius * System.Math.Cos(StartAngle + Sweep / 2),
- center.Y + Radius * System.Math.Sin(StartAngle + Sweep / 2))
+ ? new Vector(center.X + Radius * PortableMath.Cos(StartAngle + Sweep / 2),
+ center.Y + Radius * PortableMath.Sin(StartAngle + Sweep / 2))
: (Start + End) * 0.5;
///
@@ -287,7 +288,7 @@ internal static class PostVerificationGeometry
internal double DistanceAlong(Vector point) => Center is { } center
? point.DistanceTo(Start) <= Epsilon ? 0
- : Travel(System.Math.Atan2(point.Y - center.Y, point.X - center.X)) * Radius
+ : Travel(PortableMath.Atan2(point.Y - center.Y, point.X - center.X)) * Radius
: Dot(point - Start, (End - Start) * (1 / Length));
/// Unit direction of travel at the curve's start or end.
@@ -314,8 +315,8 @@ internal static class PostVerificationGeometry
if (distance <= 0) return Start;
if (distance >= Length) return End;
return Center is { } center
- ? center + new Vector(System.Math.Cos(StartAngle + System.Math.Sign(Sweep) * distance / Radius),
- System.Math.Sin(StartAngle + System.Math.Sign(Sweep) * distance / Radius)) * Radius
+ ? center + new Vector(PortableMath.Cos(StartAngle + System.Math.Sign(Sweep) * distance / Radius),
+ PortableMath.Sin(StartAngle + System.Math.Sign(Sweep) * distance / Radius)) * Radius
: Start + (End - Start) * (distance / Length);
}
@@ -331,7 +332,7 @@ internal static class PostVerificationGeometry
///
internal Curve Until(Vector point) => Center is { } center
? new Curve(Start, point, center, Radius,
- System.Math.Sign(Sweep) * Travel(System.Math.Atan2(point.Y - center.Y, point.X - center.X)))
+ System.Math.Sign(Sweep) * Travel(PortableMath.Atan2(point.Y - center.Y, point.X - center.X)))
: new Curve(Start, point, null, 0, 0);
internal bool Contains(Vector point) => Contains(point, Epsilon);
@@ -524,16 +525,16 @@ internal static class PostVerificationGeometry
if (radius <= Epsilon || !double.IsFinite(radius)
|| System.Math.Abs(radius - end.DistanceTo(c)) > Epsilon * System.Math.Max(1, radius))
throw new ArgumentException("Arc has zero or inconsistent radius.");
- var a = System.Math.Atan2(start.Y - c.Y, start.X - c.X);
- var b = System.Math.Atan2(end.Y - c.Y, end.X - c.X);
+ var a = PortableMath.Atan2(start.Y - c.Y, start.X - c.X);
+ var b = PortableMath.Atan2(end.Y - c.Y, end.X - c.X);
var sweep = start.DistanceTo(end) <= Epsilon ? TwoPi
: Normalize(clockwise ? a - b : b - a);
return new Curve(start, end, c, radius, clockwise ? -sweep : sweep);
}
- private double StartAngle => System.Math.Atan2(Start.Y - Center.Value.Y, Start.X - Center.Value.X);
+ private double StartAngle => PortableMath.Atan2(Start.Y - Center.Value.Y, Start.X - Center.Value.X);
private double Travel(double angle) => Normalize(Sweep < 0 ? StartAngle - angle : angle - StartAngle);
- private bool OnArc(Vector point) => Travel(System.Math.Atan2(point.Y - Center.Value.Y,
+ private bool OnArc(Vector point) => Travel(PortableMath.Atan2(point.Y - Center.Value.Y,
point.X - Center.Value.X)) <= System.Math.Abs(Sweep) + Epsilon / Radius
|| point.DistanceTo(Start) <= Epsilon || point.DistanceTo(End) <= Epsilon;
@@ -637,13 +638,13 @@ internal static class PostVerificationGeometry
{
var a = StartAngle + System.Math.Sign(Sweep) * breaks[i - 1];
var b = StartAngle + System.Math.Sign(Sweep) * breaks[i];
- var ya = i == 1 ? Start.Y : center.Y + Radius * System.Math.Sin(a);
- var yb = i == breaks.Count - 1 ? End.Y : center.Y + Radius * System.Math.Sin(b);
+ var ya = i == 1 ? Start.Y : center.Y + Radius * PortableMath.Sin(a);
+ var yb = i == breaks.Count - 1 ? End.Y : center.Y + Radius * PortableMath.Sin(b);
if ((ya > rayY) == (yb > rayY))
continue;
var dy = point.Y - center.Y;
var dx = System.Math.Sqrt(System.Math.Max(0, Radius * Radius - dy * dy));
- var x = center.X + (System.Math.Cos((a + b) / 2) >= 0 ? dx : -dx);
+ var x = center.X + (PortableMath.Cos((a + b) / 2) >= 0 ? dx : -dx);
if (x > point.X)
inside = !inside;
}
diff --git a/OpenNest.Core/Dimensions/DimensionBuilder.cs b/OpenNest.Core/Dimensions/DimensionBuilder.cs
index a379b36f..fab53d49 100644
--- a/OpenNest.Core/Dimensions/DimensionBuilder.cs
+++ b/OpenNest.Core/Dimensions/DimensionBuilder.cs
@@ -189,7 +189,7 @@ namespace OpenNest.Dimensions
var angle = arc != null && kind == DimensionKind.Radius
? ClampToSweep(arc, dimension.Angle)
: dimension.Angle;
- var direction = new Vector(System.Math.Cos(angle), System.Math.Sin(angle));
+ var direction = new Vector(PortableMath.Cos(angle), PortableMath.Sin(angle));
var arrow = center + direction * radius;
var far = center - direction * radius;
return new DimensionLayout
diff --git a/OpenNest.Core/Dimensions/DimensionLayout.cs b/OpenNest.Core/Dimensions/DimensionLayout.cs
index 809c2b74..e0efaa3d 100644
--- a/OpenNest.Core/Dimensions/DimensionLayout.cs
+++ b/OpenNest.Core/Dimensions/DimensionLayout.cs
@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using OpenNest.Geometry;
+using OpenNest.Math;
namespace OpenNest.Dimensions
{
@@ -80,7 +81,7 @@ namespace OpenNest.Dimensions
var dx = point.X - Center.X;
var dy = point.Y - Center.Y;
var distance = System.Math.Sqrt(dx * dx + dy * dy);
- var angle = distance > 0 ? System.Math.Atan2(dy, dx) : Angle;
+ var angle = distance > 0 ? PortableMath.Atan2(dy, dx) : Angle;
if (Arc != null && Kind == DimensionKind.Radius)
angle = DimensionBuilder.ClampToSweep(Arc, angle);
diff --git a/OpenNest.Core/Geometry/Arc.cs b/OpenNest.Core/Geometry/Arc.cs
index 5a3c4a7a..7849b37b 100644
--- a/OpenNest.Core/Geometry/Arc.cs
+++ b/OpenNest.Core/Geometry/Arc.cs
@@ -141,8 +141,8 @@ namespace OpenNest.Geometry
public Vector StartPoint()
{
return new Vector(
- Center.X + Radius * System.Math.Cos(StartAngle),
- Center.Y + Radius * System.Math.Sin(StartAngle)
+ Center.X + Radius * PortableMath.Cos(StartAngle),
+ Center.Y + Radius * PortableMath.Sin(StartAngle)
);
}
@@ -153,8 +153,8 @@ namespace OpenNest.Geometry
public Vector EndPoint()
{
return new Vector(
- Center.X + Radius * System.Math.Cos(EndAngle),
- Center.Y + Radius * System.Math.Sin(EndAngle)
+ Center.X + Radius * PortableMath.Cos(EndAngle),
+ Center.Y + Radius * PortableMath.Sin(EndAngle)
);
}
@@ -165,8 +165,8 @@ namespace OpenNest.Geometry
{
var midAngle = StartAngle + (IsReversed ? -SweepAngle() / 2 : SweepAngle() / 2);
return new Vector(
- Center.X + Radius * System.Math.Cos(midAngle),
- Center.Y + Radius * System.Math.Sin(midAngle)
+ Center.X + Radius * PortableMath.Cos(midAngle),
+ Center.Y + Radius * PortableMath.Sin(midAngle)
);
}
@@ -231,7 +231,7 @@ namespace OpenNest.Geometry
if (tolerance >= Radius)
return 1;
- var maxAngle = 2.0 * System.Math.Acos(1.0 - tolerance / Radius);
+ var maxAngle = 2.0 * PortableMath.Acos(1.0 - tolerance / Radius);
return System.Math.Max(
1,
(int)System.Math.Ceiling(System.Math.Abs(SweepAngle()) / maxAngle)
@@ -250,7 +250,7 @@ namespace OpenNest.Geometry
var r =
circumscribe && segments > 0
- ? Radius / System.Math.Cos(System.Math.Abs(stepAngle) / 2.0)
+ ? Radius / PortableMath.Cos(System.Math.Abs(stepAngle) / 2.0)
: Radius;
for (int i = 0; i <= segments; ++i)
@@ -259,8 +259,8 @@ namespace OpenNest.Geometry
points.Add(
new Vector(
- System.Math.Cos(angle) * r + Center.X,
- System.Math.Sin(angle) * r + Center.Y
+ PortableMath.Cos(angle) * r + Center.X,
+ PortableMath.Sin(angle) * r + Center.Y
)
);
}
@@ -479,8 +479,8 @@ namespace OpenNest.Geometry
if (Angle.IsBetweenRad(angle, StartAngle, EndAngle, IsReversed))
{
return new Vector(
- System.Math.Cos(angle) * Radius + Center.X,
- System.Math.Sin(angle) * Radius + Center.Y
+ PortableMath.Cos(angle) * Radius + Center.X,
+ PortableMath.Sin(angle) * Radius + Center.Y
);
}
else
diff --git a/OpenNest.Core/Geometry/ArcFit.cs b/OpenNest.Core/Geometry/ArcFit.cs
index 1773cb05..b929e10d 100644
--- a/OpenNest.Core/Geometry/ArcFit.cs
+++ b/OpenNest.Core/Geometry/ArcFit.cs
@@ -102,8 +102,8 @@ namespace OpenNest.Geometry
return (Vector.Invalid, 0, double.MaxValue);
var halfChord = chordLen / 2;
- var radius = halfChord / System.Math.Abs(System.Math.Sin(theta));
- var d = -halfChord / System.Math.Tan(theta);
+ var radius = halfChord / System.Math.Abs(PortableMath.Sin(theta));
+ var d = -halfChord / PortableMath.Tan(theta);
var cx = (p1.X + pn.X) / 2 + d * -uy;
var cy = (p1.Y + pn.Y) / 2 + d * ux;
@@ -116,7 +116,7 @@ namespace OpenNest.Geometry
var len = System.Math.Sqrt(to.X * to.X + to.Y * to.Y);
if (len < 1e-10)
return 0;
- return System.Math.Atan2(ux * to.Y - uy * to.X, ux * to.X + uy * to.Y);
+ return PortableMath.Atan2(ux * to.Y - uy * to.X, ux * to.X + uy * to.Y);
}
///
@@ -128,8 +128,8 @@ namespace OpenNest.Geometry
var total = 0.0;
for (var i = 0; i < points.Count - 1; i++)
{
- var a1 = System.Math.Atan2(points[i].Y - center.Y, points[i].X - center.X);
- var a2 = System.Math.Atan2(points[i + 1].Y - center.Y, points[i + 1].X - center.X);
+ var a1 = PortableMath.Atan2(points[i].Y - center.Y, points[i].X - center.X);
+ var a2 = PortableMath.Atan2(points[i + 1].Y - center.Y, points[i + 1].X - center.X);
var da = a2 - a1;
while (da > System.Math.PI)
da -= Angle.TwoPI;
diff --git a/OpenNest.Core/Geometry/Circle.cs b/OpenNest.Core/Geometry/Circle.cs
index 7c890f06..cf1c8e3b 100644
--- a/OpenNest.Core/Geometry/Circle.cs
+++ b/OpenNest.Core/Geometry/Circle.cs
@@ -126,7 +126,7 @@ namespace OpenNest.Geometry
if (tolerance >= Radius)
return 3;
- var maxAngle = 2.0 * System.Math.Acos(1.0 - tolerance / Radius);
+ var maxAngle = 2.0 * PortableMath.Acos(1.0 - tolerance / Radius);
return System.Math.Max(3, (int)System.Math.Ceiling(Angle.TwoPI / maxAngle));
}
@@ -137,7 +137,7 @@ namespace OpenNest.Geometry
Rotation == RotationType.CW ? -Angle.TwoPI / segments : Angle.TwoPI / segments;
var r =
- circumscribe && segments > 0 ? Radius / System.Math.Cos(stepAngle / 2.0) : Radius;
+ circumscribe && segments > 0 ? Radius / PortableMath.Cos(stepAngle / 2.0) : Radius;
for (int i = 0; i <= segments; ++i)
{
@@ -145,8 +145,8 @@ namespace OpenNest.Geometry
points.Add(
new Vector(
- System.Math.Cos(angle) * r + Center.X,
- System.Math.Sin(angle) * r + Center.Y
+ PortableMath.Cos(angle) * r + Center.X,
+ PortableMath.Sin(angle) * r + Center.Y
)
);
}
@@ -312,8 +312,8 @@ namespace OpenNest.Geometry
var angle = Center.AngleTo(pt);
return new Vector(
- System.Math.Cos(angle) * Radius + Center.X,
- System.Math.Sin(angle) * Radius + Center.Y
+ PortableMath.Cos(angle) * Radius + Center.X,
+ PortableMath.Sin(angle) * Radius + Center.Y
);
}
diff --git a/OpenNest.Core/Geometry/ClipperBridge.cs b/OpenNest.Core/Geometry/ClipperBridge.cs
index 137f9cbe..fd772513 100644
--- a/OpenNest.Core/Geometry/ClipperBridge.cs
+++ b/OpenNest.Core/Geometry/ClipperBridge.cs
@@ -299,7 +299,7 @@ namespace OpenNest.Geometry
var sweep = arc.SweepAngle();
var segments = CircumscribedSegments(arc.Radius, sweep, tolerance);
var step = (arc.IsReversed ? -sweep : sweep) / segments;
- var r = arc.Radius / System.Math.Cos(System.Math.Abs(step) / 2);
+ var r = arc.Radius / PortableMath.Cos(System.Math.Abs(step) / 2);
points.Add(arc.StartPoint());
@@ -308,8 +308,8 @@ namespace OpenNest.Geometry
var angle = arc.StartAngle + step * (i + 0.5);
points.Add(
new Vector(
- arc.Center.X + r * System.Math.Cos(angle),
- arc.Center.Y + r * System.Math.Sin(angle)
+ arc.Center.X + r * PortableMath.Cos(angle),
+ arc.Center.Y + r * PortableMath.Sin(angle)
)
);
}
@@ -332,14 +332,14 @@ namespace OpenNest.Geometry
var segments = CircumscribedSegments(circle.Radius, Angle.TwoPI, tolerance);
var step = Angle.TwoPI / segments;
- var r = circle.Radius / System.Math.Cos(step / 2);
+ var r = circle.Radius / PortableMath.Cos(step / 2);
for (var i = 0; i < segments; i++)
{
points.Add(
new Vector(
- circle.Center.X + r * System.Math.Cos(step * i),
- circle.Center.Y + r * System.Math.Sin(step * i)
+ circle.Center.X + r * PortableMath.Cos(step * i),
+ circle.Center.Y + r * PortableMath.Sin(step * i)
)
);
}
@@ -352,7 +352,7 @@ namespace OpenNest.Geometry
///
private static int CircumscribedSegments(double radius, double sweep, double tolerance)
{
- var maxHalfStep = System.Math.Acos(radius / (radius + tolerance));
+ var maxHalfStep = PortableMath.Acos(radius / (radius + tolerance));
var segments = (int)System.Math.Ceiling(System.Math.Abs(sweep) / (2 * maxHalfStep));
var quarters = (int)System.Math.Ceiling(System.Math.Abs(sweep) / Angle.HalfPI);
diff --git a/OpenNest.Core/Geometry/ContourSampler.cs b/OpenNest.Core/Geometry/ContourSampler.cs
index a3acce1f..54cda5f2 100644
--- a/OpenNest.Core/Geometry/ContourSampler.cs
+++ b/OpenNest.Core/Geometry/ContourSampler.cs
@@ -80,7 +80,7 @@ namespace OpenNest.Geometry
var dirX = dx / length;
var dirY = dy / length;
- var tangent = System.Math.Atan2(dirY, dirX);
+ var tangent = PortableMath.Atan2(dirY, dirX);
var count = System.Math.Max(1, (int)(length / spacing));
var step = length / (count + 1);
@@ -114,8 +114,8 @@ namespace OpenNest.Geometry
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);
+ var startAngle = PortableMath.Atan2(start.Y - center.Y, start.X - center.X);
+ var endAngle = PortableMath.Atan2(end.Y - center.Y, end.X - center.X);
double sweep;
if (rotation == RotationType.CCW)
@@ -147,8 +147,8 @@ namespace OpenNest.Geometry
angle = startAngle - stepAngle * i;
var pt = new Vector(
- center.X + radius * System.Math.Cos(angle),
- center.Y + radius * System.Math.Sin(angle)
+ center.X + radius * PortableMath.Cos(angle),
+ center.Y + radius * PortableMath.Sin(angle)
);
double tangent;
@@ -157,7 +157,7 @@ namespace OpenNest.Geometry
else
tangent = angle - System.Math.PI / 2;
- var dir = new Vector(System.Math.Cos(tangent), System.Math.Sin(tangent));
+ var dir = new Vector(PortableMath.Cos(tangent), PortableMath.Sin(tangent));
var at = radius * System.Math.Abs(stepAngle * i);
output.Add(new ContourSample(pt, dir, tangent, at));
}
@@ -325,7 +325,7 @@ namespace OpenNest.Geometry
var local = segLen > 0 ? (s - segStart) / segLen : 0.0;
var dir = SegmentDirection(a, b);
var pt = new Vector(a.X + (b.X - a.X) * local, a.Y + (b.Y - a.Y) * local);
- output.Add(new ContourSample(pt, dir, System.Math.Atan2(dir.Y, dir.X), s));
+ output.Add(new ContourSample(pt, dir, PortableMath.Atan2(dir.Y, dir.X), s));
}
}
@@ -357,8 +357,8 @@ namespace OpenNest.Geometry
// Same sweep convention as ArcMoves: always in (0, 2*PI], so a full
// circle counts its whole circumference toward the walk's arclength.
- 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);
+ var startAngle = PortableMath.Atan2(start.Y - center.Y, start.X - center.X);
+ var endAngle = PortableMath.Atan2(end.Y - center.Y, end.X - center.X);
var sweep =
rotation == RotationType.CCW ? endAngle - startAngle : startAngle - endAngle;
if (sweep <= 0)
diff --git a/OpenNest.Core/Geometry/DrawingAligner.cs b/OpenNest.Core/Geometry/DrawingAligner.cs
index f17750a3..7ad15a52 100644
--- a/OpenNest.Core/Geometry/DrawingAligner.cs
+++ b/OpenNest.Core/Geometry/DrawingAligner.cs
@@ -786,7 +786,7 @@ namespace OpenNest.Geometry
if (!(covXX > 0) || double.IsNaN(covXY) || double.IsNaN(covXX))
return false;
- dTheta = System.Math.Atan2(covXY, covXX);
+ dTheta = PortableMath.Atan2(covXY, covXX);
if (double.IsNaN(dTheta) || double.IsInfinity(dTheta))
return false;
diff --git a/OpenNest.Core/Geometry/EllipseConverter.cs b/OpenNest.Core/Geometry/EllipseConverter.cs
index 6d621060..eb256bcc 100644
--- a/OpenNest.Core/Geometry/EllipseConverter.cs
+++ b/OpenNest.Core/Geometry/EllipseConverter.cs
@@ -17,11 +17,11 @@ namespace OpenNest.Geometry
double t
)
{
- var x = semiMajor * System.Math.Cos(t);
- var y = semiMinor * System.Math.Sin(t);
+ var x = semiMajor * PortableMath.Cos(t);
+ var y = semiMinor * PortableMath.Sin(t);
- var cos = System.Math.Cos(rotation);
- var sin = System.Math.Sin(rotation);
+ var cos = PortableMath.Cos(rotation);
+ var sin = PortableMath.Sin(rotation);
return new Vector(center.X + x * cos - y * sin, center.Y + x * sin + y * cos);
}
@@ -33,11 +33,11 @@ namespace OpenNest.Geometry
double t
)
{
- var tx = -semiMajor * System.Math.Sin(t);
- var ty = semiMinor * System.Math.Cos(t);
+ var tx = -semiMajor * PortableMath.Sin(t);
+ var ty = semiMinor * PortableMath.Cos(t);
- var cos = System.Math.Cos(rotation);
- var sin = System.Math.Sin(rotation);
+ var cos = PortableMath.Cos(rotation);
+ var sin = PortableMath.Sin(rotation);
return new Vector(tx * cos - ty * sin, tx * sin + ty * cos);
}
@@ -51,11 +51,11 @@ namespace OpenNest.Geometry
{
// Inward normal: perpendicular to tangent, pointing toward center of curvature.
// In local coords: N(t) = (-b*cos(t), -a*sin(t))
- var nx = -semiMinor * System.Math.Cos(t);
- var ny = -semiMajor * System.Math.Sin(t);
+ var nx = -semiMinor * PortableMath.Cos(t);
+ var ny = -semiMajor * PortableMath.Sin(t);
- var cos = System.Math.Cos(rotation);
- var sin = System.Math.Sin(rotation);
+ var cos = PortableMath.Cos(rotation);
+ var sin = PortableMath.Sin(rotation);
return new Vector(nx * cos - ny * sin, nx * sin + ny * cos);
}
@@ -326,8 +326,8 @@ namespace OpenNest.Geometry
radius = p0.DistanceTo(cc);
}
- var startAngle = System.Math.Atan2(p0.Y - arcCenter.Y, p0.X - arcCenter.X);
- var endAngle = System.Math.Atan2(p1.Y - arcCenter.Y, p1.X - arcCenter.X);
+ var startAngle = PortableMath.Atan2(p0.Y - arcCenter.Y, p0.X - arcCenter.X);
+ var endAngle = PortableMath.Atan2(p1.Y - arcCenter.Y, p1.X - arcCenter.X);
var points = new List { p0, pMid, p1 };
var isReversed = ArcFit.SumSignedAngles(arcCenter, points) < 0;
diff --git a/OpenNest.Core/Geometry/GeometryOptimizer.cs b/OpenNest.Core/Geometry/GeometryOptimizer.cs
index e90953c2..8e685ebe 100644
--- a/OpenNest.Core/Geometry/GeometryOptimizer.cs
+++ b/OpenNest.Core/Geometry/GeometryOptimizer.cs
@@ -148,7 +148,7 @@ namespace OpenNest.Geometry
System.Math.Abs(point.DistanceTo(center) - circle.Radius) <= Tolerance.ChainTolerance;
bool OnOverlap(Vector point)
{
- var angle = System.Math.Atan2(point.Y - center.Y, point.X - center.X);
+ var angle = PortableMath.Atan2(point.Y - center.Y, point.X - center.X);
return spans.Count(s =>
{
var offset = Angle.NormalizeRad(angle - s.Start);
diff --git a/OpenNest.Core/Geometry/GeometrySimplifier.cs b/OpenNest.Core/Geometry/GeometrySimplifier.cs
index 2e8b8fd4..3ed03be0 100644
--- a/OpenNest.Core/Geometry/GeometrySimplifier.cs
+++ b/OpenNest.Core/Geometry/GeometrySimplifier.cs
@@ -333,10 +333,10 @@ public class GeometrySimplifier
// Mirroring reverses winding — swap start/end to preserve arc direction
var mirrorStart = NormalizeAngle(
- System.Math.Atan2(mirrorEp.Y - mirrorCenter.Y, mirrorEp.X - mirrorCenter.X)
+ PortableMath.Atan2(mirrorEp.Y - mirrorCenter.Y, mirrorEp.X - mirrorCenter.X)
);
var mirrorEnd = NormalizeAngle(
- System.Math.Atan2(mirrorSp.Y - mirrorCenter.Y, mirrorSp.X - mirrorCenter.X)
+ PortableMath.Atan2(mirrorSp.Y - mirrorCenter.Y, mirrorSp.X - mirrorCenter.X)
);
var result = new Arc(mirrorCenter, arc.Radius, mirrorStart, mirrorEnd, arc.IsReversed);
@@ -653,8 +653,8 @@ public class GeometrySimplifier
/// The direction of travel along an arc at .
private static Vector ArcTangent(Arc arc, double angle) =>
arc.IsReversed
- ? new Vector(System.Math.Sin(angle), -System.Math.Cos(angle))
- : new Vector(-System.Math.Sin(angle), System.Math.Cos(angle));
+ ? new Vector(PortableMath.Sin(angle), -PortableMath.Cos(angle))
+ : new Vector(-PortableMath.Sin(angle), PortableMath.Cos(angle));
///
/// Computes the tangent direction at the last point of a fitted arc,
@@ -779,10 +779,10 @@ public class GeometrySimplifier
var lastPoint = points[^1];
var startAngle = NormalizeAngle(
- System.Math.Atan2(firstPoint.Y - center.Y, firstPoint.X - center.X)
+ PortableMath.Atan2(firstPoint.Y - center.Y, firstPoint.X - center.X)
);
var endAngle = NormalizeAngle(
- System.Math.Atan2(lastPoint.Y - center.Y, lastPoint.X - center.X)
+ PortableMath.Atan2(lastPoint.Y - center.Y, lastPoint.X - center.X)
);
var isReversed = ArcFit.SumSignedAngles(center, points) < 0;
@@ -815,8 +815,8 @@ public class GeometrySimplifier
bool isReversed
)
{
- var startAngle = System.Math.Atan2(points[0].Y - center.Y, points[0].X - center.X);
- var endAngle = System.Math.Atan2(points[^1].Y - center.Y, points[^1].X - center.X);
+ var startAngle = PortableMath.Atan2(points[0].Y - center.Y, points[0].X - center.X);
+ var endAngle = PortableMath.Atan2(points[^1].Y - center.Y, points[^1].X - center.X);
var sweep = endAngle - startAngle;
if (isReversed)
@@ -838,8 +838,8 @@ public class GeometrySimplifier
{
var t = (double)i / sampleCount;
var angle = startAngle + sweep * t;
- var px = center.X + radius * System.Math.Cos(angle);
- var py = center.Y + radius * System.Math.Sin(angle);
+ var px = center.X + radius * PortableMath.Cos(angle);
+ var py = center.Y + radius * PortableMath.Sin(angle);
var arcPt = new Vector(px, py);
var minDist = double.MaxValue;
diff --git a/OpenNest.Core/Geometry/Line.cs b/OpenNest.Core/Geometry/Line.cs
index 1fb4ef82..69372099 100644
--- a/OpenNest.Core/Geometry/Line.cs
+++ b/OpenNest.Core/Geometry/Line.cs
@@ -214,7 +214,7 @@ namespace OpenNest.Geometry
{
var m1 = Slope();
var m2 = line.Slope();
- return System.Math.Atan(System.Math.Abs((m2 - m1) / (1 + m2 * m1)));
+ return PortableMath.Atan(System.Math.Abs((m2 - m1) / (1 + m2 * m1)));
}
///
@@ -395,8 +395,8 @@ namespace OpenNest.Geometry
{
var angle = OpenNest.Math.Angle.NormalizeRad(Angle() + OpenNest.Math.Angle.HalfPI);
- var x = System.Math.Cos(angle) * distance;
- var y = System.Math.Sin(angle) * distance;
+ var x = PortableMath.Cos(angle) * distance;
+ var y = PortableMath.Sin(angle) * distance;
var pt = side == OffsetSide.Left ? new Vector(x, y) : new Vector(-x, -y);
diff --git a/OpenNest.Core/Geometry/NoFitPolygon.cs b/OpenNest.Core/Geometry/NoFitPolygon.cs
index 680aa23c..af580f1f 100644
--- a/OpenNest.Core/Geometry/NoFitPolygon.cs
+++ b/OpenNest.Core/Geometry/NoFitPolygon.cs
@@ -146,11 +146,11 @@ namespace OpenNest.Geometry
}
else
{
- var angleA = System.Math.Atan2(orderedA[ia].Y, orderedA[ia].X);
+ var angleA = PortableMath.Atan2(orderedA[ia].Y, orderedA[ia].X);
if (angleA < 0)
angleA += Angle.TwoPI;
- var angleB = System.Math.Atan2(orderedB[ib].Y, orderedB[ib].X);
+ var angleB = PortableMath.Atan2(orderedB[ib].Y, orderedB[ib].X);
if (angleB < 0)
angleB += Angle.TwoPI;
diff --git a/OpenNest.Core/Geometry/RotatingCalipers.cs b/OpenNest.Core/Geometry/RotatingCalipers.cs
index 550748ef..04e70861 100644
--- a/OpenNest.Core/Geometry/RotatingCalipers.cs
+++ b/OpenNest.Core/Geometry/RotatingCalipers.cs
@@ -42,7 +42,7 @@ namespace OpenNest.Geometry
{
var dx = vertices[1].X - vertices[0].X;
var dy = vertices[1].Y - vertices[0].Y;
- var angle = System.Math.Atan2(dy, dx);
+ var angle = PortableMath.Atan2(dy, dx);
var length = System.Math.Sqrt(dx * dx + dy * dy);
return new BoundingRectangleResult(angle, length, 0);
}
@@ -100,7 +100,7 @@ namespace OpenNest.Geometry
if (best == null || area < best.Area - margin)
{
- var edgeAngle = System.Math.Atan2(edgeY, edgeX);
+ var edgeAngle = PortableMath.Atan2(edgeY, edgeX);
best = new BoundingRectangleResult(edgeAngle, width, height);
}
}
@@ -137,7 +137,7 @@ namespace OpenNest.Geometry
if (edgeLen.IsEqualTo(0))
continue;
- var edgeAngle = Angle.NormalizeRad(System.Math.Atan2(edgeY, edgeX));
+ var edgeAngle = Angle.NormalizeRad(PortableMath.Atan2(edgeY, edgeX));
// Each edge produces 4 candidate angles (0, 90, 180, 270 relative to edge)
for (int k = 0; k < 4; k++)
@@ -172,8 +172,8 @@ namespace OpenNest.Geometry
double angle
)
{
- var cos = System.Math.Cos(angle);
- var sin = System.Math.Sin(angle);
+ var cos = PortableMath.Cos(angle);
+ var sin = PortableMath.Sin(angle);
double minU = double.MaxValue,
maxU = double.MinValue;
diff --git a/OpenNest.Core/Geometry/Shape.cs b/OpenNest.Core/Geometry/Shape.cs
index 52201344..2f028f8d 100644
--- a/OpenNest.Core/Geometry/Shape.cs
+++ b/OpenNest.Core/Geometry/Shape.cs
@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
+using OpenNest.Math;
namespace OpenNest.Geometry
{
@@ -661,8 +662,8 @@ namespace OpenNest.Geometry
private static Vector ArcTangent(Arc arc, double angle)
{
- var sin = System.Math.Sin(angle);
- var cos = System.Math.Cos(angle);
+ var sin = PortableMath.Sin(angle);
+ var cos = PortableMath.Cos(angle);
return arc.IsReversed ? new Vector(sin, -cos) : new Vector(-sin, cos);
}
diff --git a/OpenNest.Core/Geometry/ShapeBuilder.cs b/OpenNest.Core/Geometry/ShapeBuilder.cs
index 06647e83..4be66d83 100644
--- a/OpenNest.Core/Geometry/ShapeBuilder.cs
+++ b/OpenNest.Core/Geometry/ShapeBuilder.cs
@@ -176,7 +176,7 @@ namespace OpenNest.Geometry
var arc = (Arc)entity;
var deltaX = weldedPoint.X - arc.Center.X;
var deltaY = weldedPoint.Y - arc.Center.Y;
- var angle = System.Math.Atan2(deltaY, deltaX);
+ var angle = PortableMath.Atan2(deltaY, deltaX);
if (isStart)
arc.StartAngle = angle;
diff --git a/OpenNest.Core/Geometry/SlideContact.cs b/OpenNest.Core/Geometry/SlideContact.cs
index 0007dfad..0aed4ccd 100644
--- a/OpenNest.Core/Geometry/SlideContact.cs
+++ b/OpenNest.Core/Geometry/SlideContact.cs
@@ -237,8 +237,8 @@ namespace OpenNest.Geometry
if (IsZero(inTangent) || IsZero(outTangent))
return ContactSide.Ambiguous;
- var outAngle = System.Math.Atan2(outTangent.Y, outTangent.X);
- var inAngle = System.Math.Atan2(-inTangent.Y, -inTangent.X);
+ var outAngle = PortableMath.Atan2(outTangent.Y, outTangent.X);
+ var inAngle = PortableMath.Atan2(-inTangent.Y, -inTangent.X);
var left = materialLeft[loopOf[best]];
start = left ? outAngle : inAngle;
@@ -326,7 +326,7 @@ namespace OpenNest.Geometry
private static Vector ArcTangent(double angle, bool clockwise)
{
var sign = clockwise ? -1.0 : 1.0;
- return new Vector(-System.Math.Sin(angle) * sign, System.Math.Cos(angle) * sign);
+ return new Vector(-PortableMath.Sin(angle) * sign, PortableMath.Cos(angle) * sign);
}
private static Vector Direction(Vector from, Vector to)
@@ -380,7 +380,7 @@ namespace OpenNest.Geometry
var sweep = arc.IsReversed ? -arc.SweepAngle() : arc.SweepAngle();
var r = arc.Radius;
area += Cross(arc.StartPoint(), arc.EndPoint()) / 2;
- area += r * r / 2 * (sweep - System.Math.Sin(sweep));
+ area += r * r / 2 * (sweep - PortableMath.Sin(sweep));
break;
}
}
@@ -432,13 +432,13 @@ namespace OpenNest.Geometry
{
var a = arc.StartAngle + sign * cuts[k - 1];
var b = arc.StartAngle + sign * cuts[k];
- var y1 = arc.Center.Y + arc.Radius * System.Math.Sin(a);
- var y2 = arc.Center.Y + arc.Radius * System.Math.Sin(b);
+ var y1 = arc.Center.Y + arc.Radius * PortableMath.Sin(a);
+ var y2 = arc.Center.Y + arc.Radius * PortableMath.Sin(b);
if ((y1 > point.Y) == (y2 > point.Y))
continue;
var dy = point.Y - arc.Center.Y;
var dx = System.Math.Sqrt(System.Math.Max(0, arc.Radius * arc.Radius - dy * dy));
- var x = arc.Center.X + (System.Math.Cos((a + b) / 2) >= 0 ? dx : -dx);
+ var x = arc.Center.X + (PortableMath.Cos((a + b) / 2) >= 0 ? dx : -dx);
if (point.X < x)
inside = !inside;
}
@@ -752,7 +752,7 @@ namespace OpenNest.Geometry
if (stationarySide == ContactSide.Ambiguous || movingSide == ContactSide.Ambiguous)
return true;
- var direction = System.Math.Atan2(dirY, dirX);
+ var direction = PortableMath.Atan2(dirY, dirX);
var stationaryPieces = Split(stationaryStart, stationaryWidth);
var movingPieces = Split(movingStart + System.Math.PI, movingWidth);
var onBoundary = false;
diff --git a/OpenNest.Core/Geometry/SlideCurvePrimitives.cs b/OpenNest.Core/Geometry/SlideCurvePrimitives.cs
index c8749a99..a3212d97 100644
--- a/OpenNest.Core/Geometry/SlideCurvePrimitives.cs
+++ b/OpenNest.Core/Geometry/SlideCurvePrimitives.cs
@@ -10,7 +10,7 @@ namespace OpenNest.Geometry
// A zero-radius curve is a point: its angular range has no geometric meaning.
if (arc == null || radius == 0)
return true;
- var angle = Angle.NormalizeRad(System.Math.Atan2(y, x));
+ var angle = Angle.NormalizeRad(PortableMath.Atan2(y, x));
return Angle.IsBetweenRad(angle, arc.StartAngle, arc.EndAngle, arc.IsReversed);
}
diff --git a/OpenNest.Core/Geometry/SlideEvents.cs b/OpenNest.Core/Geometry/SlideEvents.cs
index 4b3890ea..1f1f6fc1 100644
--- a/OpenNest.Core/Geometry/SlideEvents.cs
+++ b/OpenNest.Core/Geometry/SlideEvents.cs
@@ -63,7 +63,7 @@ namespace OpenNest.Geometry
continue;
var hitAngle = Angle.NormalizeRad(
- System.Math.Atan2(vy + t * rayY - cy, vx + t * rayX - cx)
+ PortableMath.Atan2(vy + t * rayY - cy, vx + t * rayX - cx)
);
if (!Angle.IsBetweenRad(hitAngle, arc.StartAngle, arc.EndAngle, arc.IsReversed))
continue;
@@ -281,7 +281,7 @@ namespace OpenNest.Geometry
// The directional distance from an arc point at angle θ to the
// line is t(θ) = [A + r·(ey·cosθ − ex·sinθ)] / det.
// dt/dθ = 0 at θ = atan2(−ex, ey) and θ + π.
- var theta1 = Angle.NormalizeRad(System.Math.Atan2(-ex, ey));
+ var theta1 = Angle.NormalizeRad(PortableMath.Atan2(-ex, ey));
var theta2 = Angle.NormalizeRad(theta1 + System.Math.PI);
for (var k = 0; k < 2; k++)
@@ -291,8 +291,8 @@ namespace OpenNest.Geometry
if (arc != null && !Angle.IsBetweenRad(theta, arc.StartAngle, arc.EndAngle, arc.IsReversed))
continue;
- var qx = cx + r * System.Math.Cos(theta);
- var qy = cy + r * System.Math.Sin(theta);
+ var qx = cx + r * PortableMath.Cos(theta);
+ var qy = cy + r * PortableMath.Sin(theta);
RayLine(ref sink, qx, qy, p1x, p1y, p2x, p2y, rayX, rayY, arcMoves);
if (sink.IsDone)
diff --git a/OpenNest.Core/Geometry/SpatialQuery.cs b/OpenNest.Core/Geometry/SpatialQuery.cs
index 5c1e1d81..81b937b0 100644
--- a/OpenNest.Core/Geometry/SpatialQuery.cs
+++ b/OpenNest.Core/Geometry/SpatialQuery.cs
@@ -179,7 +179,7 @@ namespace OpenNest.Geometry
if (t1 > -Tolerance.Epsilon)
{
var hitAngle = Angle.NormalizeRad(
- System.Math.Atan2(vy + t1 * dirY - cy, vx + t1 * dirX - cx)
+ PortableMath.Atan2(vy + t1 * dirY - cy, vx + t1 * dirX - cx)
);
if (Angle.IsBetweenRad(hitAngle, startAngle, endAngle, reversed))
best = t1 > Tolerance.Epsilon ? t1 : 0;
@@ -188,7 +188,7 @@ namespace OpenNest.Geometry
if (t2 > -Tolerance.Epsilon && t2 < best)
{
var hitAngle = Angle.NormalizeRad(
- System.Math.Atan2(vy + t2 * dirY - cy, vx + t2 * dirX - cx)
+ PortableMath.Atan2(vy + t2 * dirY - cy, vx + t2 * dirX - cx)
);
if (Angle.IsBetweenRad(hitAngle, startAngle, endAngle, reversed))
best = t2 > Tolerance.Epsilon ? t2 : 0;
diff --git a/OpenNest.Core/Geometry/SplineConverter.cs b/OpenNest.Core/Geometry/SplineConverter.cs
index fc282173..073240a0 100644
--- a/OpenNest.Core/Geometry/SplineConverter.cs
+++ b/OpenNest.Core/Geometry/SplineConverter.cs
@@ -167,8 +167,8 @@ namespace OpenNest.Geometry
var firstPoint = points[0];
var lastPoint = points[^1];
- var startAngle = System.Math.Atan2(firstPoint.Y - center.Y, firstPoint.X - center.X);
- var endAngle = System.Math.Atan2(lastPoint.Y - center.Y, lastPoint.X - center.X);
+ var startAngle = PortableMath.Atan2(firstPoint.Y - center.Y, firstPoint.X - center.X);
+ var endAngle = PortableMath.Atan2(lastPoint.Y - center.Y, lastPoint.X - center.X);
var isReversed = ArcFit.SumSignedAngles(center, points) < 0;
if (startAngle < 0)
diff --git a/OpenNest.Core/Geometry/Vector.cs b/OpenNest.Core/Geometry/Vector.cs
index 285a7fc3..4b55f596 100644
--- a/OpenNest.Core/Geometry/Vector.cs
+++ b/OpenNest.Core/Geometry/Vector.cs
@@ -63,7 +63,7 @@ namespace OpenNest.Geometry
public double Angle()
{
- return OpenNest.Math.Angle.NormalizeRad(System.Math.Atan2(Y, X));
+ return OpenNest.Math.Angle.NormalizeRad(PortableMath.Atan2(Y, X));
}
///
@@ -99,7 +99,7 @@ namespace OpenNest.Geometry
var dot = v1.X * v2.X + v1.Y + v2.Y;
var det = v1.X * v2.X - v1.Y + v2.Y;
- return System.Math.Atan2(det, dot);
+ return PortableMath.Atan2(det, dot);
}
public static Vector operator +(Vector pt1, Vector pt2)
@@ -161,8 +161,8 @@ namespace OpenNest.Geometry
{
var v = new Vector();
- var cos = System.Math.Cos(angle);
- var sin = System.Math.Sin(angle);
+ var cos = PortableMath.Cos(angle);
+ var sin = PortableMath.Sin(angle);
v.X = X * cos - Y * sin;
v.Y = X * sin + Y * cos;
@@ -175,8 +175,8 @@ namespace OpenNest.Geometry
var v = new Vector();
var pt = this - origin;
- var cos = System.Math.Cos(angle);
- var sin = System.Math.Sin(angle);
+ var cos = PortableMath.Cos(angle);
+ var sin = PortableMath.Sin(angle);
v.X = pt.X * cos - pt.Y * sin + origin.X;
v.Y = pt.X * sin + pt.Y * cos + origin.Y;
diff --git a/OpenNest.Core/Shapes/NgonShape.cs b/OpenNest.Core/Shapes/NgonShape.cs
index 326cb47d..a659cedd 100644
--- a/OpenNest.Core/Shapes/NgonShape.cs
+++ b/OpenNest.Core/Shapes/NgonShape.cs
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using OpenNest.Geometry;
+using OpenNest.Math;
namespace OpenNest.Shapes
{
@@ -27,7 +28,7 @@ namespace OpenNest.Shapes
{
var n = Sides < 3 ? 3 : Sides;
var center = Width / 2.0;
- var circumRadius = Width / (2.0 * System.Math.Cos(System.Math.PI / n));
+ var circumRadius = Width / (2.0 * PortableMath.Cos(System.Math.PI / n));
var step = 2.0 * System.Math.PI / n;
var start = System.Math.PI / n;
@@ -36,8 +37,8 @@ namespace OpenNest.Shapes
{
var angle = start + i * step;
vertices[i] = new Vector(
- center + circumRadius * System.Math.Cos(angle),
- center + circumRadius * System.Math.Sin(angle)
+ center + circumRadius * PortableMath.Cos(angle),
+ center + circumRadius * PortableMath.Sin(angle)
);
}
diff --git a/OpenNest.Core/Shapes/PipeFlangeShape.cs b/OpenNest.Core/Shapes/PipeFlangeShape.cs
index 9b3a6651..c2ead366 100644
--- a/OpenNest.Core/Shapes/PipeFlangeShape.cs
+++ b/OpenNest.Core/Shapes/PipeFlangeShape.cs
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using OpenNest.Geometry;
+using OpenNest.Math;
namespace OpenNest.Shapes
{
@@ -46,7 +47,7 @@ namespace OpenNest.Shapes
{
ShapeDimension.Linear(nameof(OD), new Vector(-OD / 2, 0), new Vector(OD / 2, 0), ShapeDimension.Up, "Ø"),
ShapeDimension.CircleLeader(nameof(HolePatternDiameter), Vector.Zero, HolePatternDiameter / 2,
- new Vector(System.Math.Cos(boltAngle), System.Math.Sin(boltAngle)), "Ø"),
+ new Vector(PortableMath.Cos(boltAngle), PortableMath.Sin(boltAngle)), "Ø"),
};
// The leader crosses the hole nearest 45 degrees through its center, out that way.
@@ -55,7 +56,7 @@ namespace OpenNest.Shapes
var step = 2.0 * System.Math.PI / HoleCount;
var angle = System.Math.Round(System.Math.PI / 4 / step) * step;
var out45 = new Vector(System.Math.Sqrt(0.5), System.Math.Sqrt(0.5));
- var center = new Vector(HolePatternDiameter / 2 * System.Math.Cos(angle), HolePatternDiameter / 2 * System.Math.Sin(angle));
+ var center = new Vector(HolePatternDiameter / 2 * PortableMath.Cos(angle), HolePatternDiameter / 2 * PortableMath.Sin(angle));
dimensions.Add(ShapeDimension.HoleLeader(nameof(HoleDiameter), center, HoleDiameter / 2, out45, "Ø")
.WithCount(nameof(HoleCount), "holes"));
}
@@ -76,8 +77,8 @@ namespace OpenNest.Shapes
for (var i = 0; i < HoleCount; i++)
{
var angle = i * angleStep;
- var cx = boltCircleRadius * System.Math.Cos(angle);
- var cy = boltCircleRadius * System.Math.Sin(angle);
+ var cx = boltCircleRadius * PortableMath.Cos(angle);
+ var cy = boltCircleRadius * PortableMath.Sin(angle);
entities.Add(new Circle(cx, cy, holeRadius));
}
diff --git a/OpenNest.Core/Splitting/DrawingSplitter.cs b/OpenNest.Core/Splitting/DrawingSplitter.cs
index d6eccd45..e2545101 100644
--- a/OpenNest.Core/Splitting/DrawingSplitter.cs
+++ b/OpenNest.Core/Splitting/DrawingSplitter.cs
@@ -2,6 +2,7 @@ using System.Collections.Generic;
using System.Linq;
using OpenNest.Converters;
using OpenNest.Geometry;
+using OpenNest.Math;
namespace OpenNest;
@@ -470,8 +471,8 @@ public static class DrawingSplitter
{
var midAngle = (arc.StartAngle + arc.EndAngle) / 2;
return new Vector(
- arc.Center.X + arc.Radius * System.Math.Cos(midAngle),
- arc.Center.Y + arc.Radius * System.Math.Sin(midAngle)
+ arc.Center.X + arc.Radius * PortableMath.Cos(midAngle),
+ arc.Center.Y + arc.Radius * PortableMath.Sin(midAngle)
);
}
diff --git a/OpenNest.Core/Splitting/SpikeGrooveSplit.cs b/OpenNest.Core/Splitting/SpikeGrooveSplit.cs
index 10895816..0996bec6 100644
--- a/OpenNest.Core/Splitting/SpikeGrooveSplit.cs
+++ b/OpenNest.Core/Splitting/SpikeGrooveSplit.cs
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using OpenNest.Geometry;
+using OpenNest.Math;
namespace OpenNest;
@@ -25,8 +26,8 @@ public class SpikeGrooveSplit : ISplitFeature
var spikeDepth = parameters.SpikeDepth;
var grooveDepth = parameters.GrooveDepth;
var angleRad = OpenNest.Math.Angle.ToRadians(parameters.SpikeAngle / 2);
- var spikeHalfWidth = spikeDepth * System.Math.Tan(angleRad);
- var grooveHalfWidth = grooveDepth * System.Math.Tan(angleRad);
+ var spikeHalfWidth = spikeDepth * PortableMath.Tan(angleRad);
+ var grooveHalfWidth = grooveDepth * PortableMath.Tan(angleRad);
var isVertical = line.Axis == CutOffAxis.Vertical;
var pos = line.Position;
diff --git a/OpenNest.Core/Support/SupportAnalyzer.cs b/OpenNest.Core/Support/SupportAnalyzer.cs
index 1a320cb2..25d063ed 100644
--- a/OpenNest.Core/Support/SupportAnalyzer.cs
+++ b/OpenNest.Core/Support/SupportAnalyzer.cs
@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using OpenNest.Geometry;
+using OpenNest.Math;
namespace OpenNest.Support
{
@@ -177,7 +178,7 @@ namespace OpenNest.Support
{
var box = BoundingRectangle(outline);
var angle = box.Width >= box.Height ? box.Angle : box.Angle + Math.Angle.HalfPI;
- return new Vector(System.Math.Cos(angle), System.Math.Sin(angle));
+ return new Vector(PortableMath.Cos(angle), PortableMath.Sin(angle));
}
// The minimum-area rectangle over every hull edge. Shapes such as a right isosceles
@@ -211,7 +212,7 @@ namespace OpenNest.Support
minV = System.Math.Min(minV, pv);
maxV = System.Math.Max(maxV, pv);
}
- candidates.Add(new BoundingRectangleResult(System.Math.Atan2(edge.Y, edge.X), maxU - minU, maxV - minV));
+ candidates.Add(new BoundingRectangleResult(PortableMath.Atan2(edge.Y, edge.X), maxU - minU, maxV - minV));
}
if (candidates.Count == 0)
return RotatingCalipers.MinimumBoundingRectangle(outline.ToList());
@@ -262,7 +263,7 @@ namespace OpenNest.Support
var outOf = outline[(i + 1) % count] - outline[i];
var cross = into.X * outOf.Y - into.Y * outOf.X;
var dot = into.X * outOf.X + into.Y * outOf.Y;
- turns[i] = System.Math.Atan2(cross, dot) * System.Math.Sign(area);
+ turns[i] = PortableMath.Atan2(cross, dot) * System.Math.Sign(area);
}
// Start after a vertex that does not turn inward so no run is split by the wrap.
diff --git a/OpenNest.Engine.Tests/Jobs/FillStackCharacterizationTests.cs b/OpenNest.Engine.Tests/Jobs/FillStackCharacterizationTests.cs
index fb7093e8..5df21f85 100644
--- a/OpenNest.Engine.Tests/Jobs/FillStackCharacterizationTests.cs
+++ b/OpenNest.Engine.Tests/Jobs/FillStackCharacterizationTests.cs
@@ -40,6 +40,10 @@ namespace OpenNest.Engine.Tests.Jobs;
/// Re-captured when the pairs phase began tiling herringbone pairs (HerringbonePatterns):
/// the mixed Strip nest keeps 35 laid out differently, and herringbone pairs won the chevron of
/// the learned-angle nests whatever angles were learned, so a cross replaced it.
+/// The learned-angle nests were re-captured when geometry moved to PortableMath: their
+/// herringbone-era pins came from Windows, whose runtime trig differs from Linux in the last
+/// bit, and the cross nested differently on each (Strip 23 there, 28 here). Both platforms
+/// now give these layouts, which pass the overlap and work-area checks.
/// Not pinned: richer mixes that pair parallelogram, chevron or tee parts at oblique angles
/// with other irregular items. Their Fill and Vertical Remnant nests vary between runs, most
/// likely because best-fit pair candidates are gathered in a ConcurrentBag and equal
@@ -47,16 +51,6 @@ namespace OpenNest.Engine.Tests.Jobs;
///
public class FillStackCharacterizationTests
{
- /// A theory that runs only on Windows.
- private sealed class WindowsOnlyTheoryAttribute : TheoryAttribute
- {
- public WindowsOnlyTheoryAttribute()
- {
- if (!OperatingSystem.IsWindows())
- Skip = "Layout differs off Windows (Project Memory 518f3b58)";
- }
- }
-
/// 6x4 block with a 2x2 notch cut into the middle of its top edge.
private static Program Notched()
{
@@ -309,17 +303,11 @@ public class FillStackCharacterizationTests
AssertPinned(layout, expectedCount, expectedDigest);
}
- ///
- /// Windows-only: these pins were captured on Windows, and Linux lays this nest out
- /// differently (Strip places 28, not 23) because the two platforms' trig differs in the last
- /// bit and the fill stack turns that into different tie and threshold decisions (Project
- /// Memory 518f3b58). Unskip on every platform when that is fixed.
- ///
- [WindowsOnlyTheory]
- [InlineData("Fill", 28, "BE363E3A47E9B036")]
- [InlineData("Strip", 23, "E4AA82327508D538")]
- [InlineData("Vertical Remnant", 28, "DB2409DB39642666")]
- [InlineData("Horizontal Remnant", 24, "883BD209AE87E2C5")]
+ [Theory]
+ [InlineData("Fill", 28, "58E544673B69F3CB")]
+ [InlineData("Strip", 28, "BB448C553CFECBD8")]
+ [InlineData("Vertical Remnant", 28, "E146E6F08C26C375")]
+ [InlineData("Horizontal Remnant", 24, "5D5B0413007B0B07")]
public void Nest_LearnedAngleItems_PinnedLayout(
string strategy,
int expectedCount,
diff --git a/OpenNest.Engine/BestFit/BestFitFinder.cs b/OpenNest.Engine/BestFit/BestFitFinder.cs
index 29da0a74..b4b189bc 100644
--- a/OpenNest.Engine/BestFit/BestFitFinder.cs
+++ b/OpenNest.Engine/BestFit/BestFitFinder.cs
@@ -213,7 +213,7 @@ namespace OpenNest.Engine.BestFit
if (dx * dx + dy * dy < Tolerance.Epsilon)
continue;
- var angle = Angle.NormalizeRad(System.Math.Atan2(dy, dx));
+ var angle = Angle.NormalizeRad(PortableMath.Atan2(dy, dx));
AddUniqueAngle(hullAngles, angle);
}
diff --git a/OpenNest.Engine/BestFit/PolygonHelper.cs b/OpenNest.Engine/BestFit/PolygonHelper.cs
index bcdfb957..79d934aa 100644
--- a/OpenNest.Engine/BestFit/PolygonHelper.cs
+++ b/OpenNest.Engine/BestFit/PolygonHelper.cs
@@ -54,8 +54,8 @@ namespace OpenNest.Engine.BestFit
return polygon;
var result = new Polygon();
- var cos = System.Math.Cos(angle);
- var sin = System.Math.Sin(angle);
+ var cos = PortableMath.Cos(angle);
+ var sin = PortableMath.Sin(angle);
foreach (var v in polygon.Vertices)
{
diff --git a/OpenNest.Engine/Fill/Compactor.cs b/OpenNest.Engine/Fill/Compactor.cs
index 6fe4f8ef..eafa83aa 100644
--- a/OpenNest.Engine/Fill/Compactor.cs
+++ b/OpenNest.Engine/Fill/Compactor.cs
@@ -29,7 +29,7 @@ namespace OpenNest.Engine.Fill
/// Pushes movingParts along an arbitrary angle (radians, 0 = right, π/2 = up).
///
public static double Push(List movingParts, Plate plate, double angle) =>
- Push(movingParts, plate, new Vector(System.Math.Cos(angle), System.Math.Sin(angle)));
+ Push(movingParts, plate, new Vector(PortableMath.Cos(angle), PortableMath.Sin(angle)));
private static double Push(List movingParts, Plate plate, Vector direction)
{
diff --git a/OpenNest.Engine/Fill/RotationAnalysis.cs b/OpenNest.Engine/Fill/RotationAnalysis.cs
index cab26610..f638d0d3 100644
--- a/OpenNest.Engine/Fill/RotationAnalysis.cs
+++ b/OpenNest.Engine/Fill/RotationAnalysis.cs
@@ -143,7 +143,7 @@ namespace OpenNest.Engine.Fill
if (lengthSq < Tolerance.Epsilon)
continue;
- var angle = -System.Math.Atan2(dy, dx);
+ var angle = -PortableMath.Atan2(dy, dx);
if (!edges.Any(e => e.angle.IsEqualTo(angle)))
edges.Add((angle, lengthSq));
diff --git a/OpenNest.Engine/Fill/TiltedRemnantFiller.cs b/OpenNest.Engine/Fill/TiltedRemnantFiller.cs
index 36c6f595..6a617b96 100644
--- a/OpenNest.Engine/Fill/TiltedRemnantFiller.cs
+++ b/OpenNest.Engine/Fill/TiltedRemnantFiller.cs
@@ -260,8 +260,8 @@ namespace OpenNest.Engine.Fill
private static (double Along, double Cross) Extents(List hull, double angle, bool alongX)
{
- var cos = System.Math.Cos(angle);
- var sin = System.Math.Sin(angle);
+ var cos = PortableMath.Cos(angle);
+ var sin = PortableMath.Sin(angle);
double minX = double.MaxValue, maxX = double.MinValue;
double minY = double.MaxValue, maxY = double.MinValue;
diff --git a/OpenNest.Engine/Jobs/CommonLines/CommonLinePipelinePrepass.cs b/OpenNest.Engine/Jobs/CommonLines/CommonLinePipelinePrepass.cs
index b3c7e0be..82d0a804 100644
--- a/OpenNest.Engine/Jobs/CommonLines/CommonLinePipelinePrepass.cs
+++ b/OpenNest.Engine/Jobs/CommonLines/CommonLinePipelinePrepass.cs
@@ -3,6 +3,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
+using OpenNest.Math;
namespace OpenNest.Engine.Jobs.CommonLines;
@@ -138,8 +139,8 @@ internal sealed class CommonLinePipelinePrepass : IPipelinePrepass
poses.Add((pose.PartId, pose.X, pose.Y, pose.Rotation));
continue;
}
- var sine = System.Math.Sin(pose.Rotation);
- var cosine = System.Math.Cos(pose.Rotation);
+ var sine = PortableMath.Sin(pose.Rotation);
+ var cosine = PortableMath.Cos(pose.Rotation);
var pair = proxy.Pair;
groups.Add(new NestJobCommonLine(kerf, new[] { poses.Count, poses.Count + 1 }));
poses.Add((proxy.PartId, pose.X, pose.Y, pose.Rotation));
diff --git a/OpenNest.Engine/Jobs/Cutouts/CutoutCirclePacker.cs b/OpenNest.Engine/Jobs/Cutouts/CutoutCirclePacker.cs
index e3ee077e..5ada3821 100644
--- a/OpenNest.Engine/Jobs/Cutouts/CutoutCirclePacker.cs
+++ b/OpenNest.Engine/Jobs/Cutouts/CutoutCirclePacker.cs
@@ -247,7 +247,7 @@ internal static class CutoutCirclePacker
{
if (p.DistanceTo(0, 0) < Epsilon)
return -1;
- var angle = System.Math.Atan2(p.X, -p.Y);
+ var angle = PortableMath.Atan2(p.X, -p.Y);
return angle < 0 ? angle + 2 * System.Math.PI : angle;
}
}
diff --git a/OpenNest.Engine/Jobs/Cutouts/CutoutPipelinePrepass.cs b/OpenNest.Engine/Jobs/Cutouts/CutoutPipelinePrepass.cs
index 18370aaf..a564b0d9 100644
--- a/OpenNest.Engine/Jobs/Cutouts/CutoutPipelinePrepass.cs
+++ b/OpenNest.Engine/Jobs/Cutouts/CutoutPipelinePrepass.cs
@@ -5,6 +5,7 @@ using System.Linq;
using System.Threading;
using OpenNest.Engine.CirclePacking;
using OpenNest.Engine.Jobs.Adapters;
+using OpenNest.Math;
namespace OpenNest.Engine.Jobs.Cutouts;
@@ -317,8 +318,8 @@ internal sealed class CutoutPipelinePrepass : IPipelinePrepass
private void Expand(Bundle bundle, double x, double y, double rotation,
List<(string PartId, double X, double Y, double Rotation)> poses)
{
- var sine = System.Math.Sin(rotation);
- var cosine = System.Math.Cos(rotation);
+ var sine = PortableMath.Sin(rotation);
+ var cosine = PortableMath.Cos(rotation);
foreach (var local in bundle.LocalInserts)
{
var insertX = x + local.X * cosine - local.Y * sine;
diff --git a/OpenNest.Engine/Jobs/JobPartGeometry.cs b/OpenNest.Engine/Jobs/JobPartGeometry.cs
index af1e18db..b590d70e 100644
--- a/OpenNest.Engine/Jobs/JobPartGeometry.cs
+++ b/OpenNest.Engine/Jobs/JobPartGeometry.cs
@@ -148,7 +148,7 @@ public sealed class JobPartGeometry
var arc = (Arc)entity;
var angle = arc.StartAngle + (arc.IsReversed ? -1 : 1) * arc.SweepAngle() * value;
return arc.Center
- + new Vector(System.Math.Cos(angle), System.Math.Sin(angle)) * arc.Radius;
+ + new Vector(PortableMath.Cos(angle), PortableMath.Sin(angle)) * arc.Radius;
}
void Check(Vector point)
{
diff --git a/OpenNest.Engine/Jobs/Placement/Fillers/PlateFillPolicy.cs b/OpenNest.Engine/Jobs/Placement/Fillers/PlateFillPolicy.cs
index 2d874574..da95c520 100644
--- a/OpenNest.Engine/Jobs/Placement/Fillers/PlateFillPolicy.cs
+++ b/OpenNest.Engine/Jobs/Placement/Fillers/PlateFillPolicy.cs
@@ -83,16 +83,16 @@ internal sealed class PlateFillPolicy
private static double RotatedWidth(NestItem item, double angle)
{
var boundingBox = item.Drawing.Program.BoundingBox();
- var cos = System.Math.Abs(System.Math.Cos(angle));
- var sin = System.Math.Abs(System.Math.Sin(angle));
+ var cos = System.Math.Abs(PortableMath.Cos(angle));
+ var sin = System.Math.Abs(PortableMath.Sin(angle));
return boundingBox.Length * cos + boundingBox.Width * sin;
}
private static double RotatedHeight(NestItem item, double angle)
{
var boundingBox = item.Drawing.Program.BoundingBox();
- var cos = System.Math.Abs(System.Math.Cos(angle));
- var sin = System.Math.Abs(System.Math.Sin(angle));
+ var cos = System.Math.Abs(PortableMath.Cos(angle));
+ var sin = System.Math.Abs(PortableMath.Sin(angle));
return boundingBox.Width * cos + boundingBox.Length * sin;
}
}
diff --git a/OpenNest.Engine/Jobs/Placement/PlateFillService.CommonLines.cs b/OpenNest.Engine/Jobs/Placement/PlateFillService.CommonLines.cs
index f5bd6c3c..7d3d5f94 100644
--- a/OpenNest.Engine/Jobs/Placement/PlateFillService.CommonLines.cs
+++ b/OpenNest.Engine/Jobs/Placement/PlateFillService.CommonLines.cs
@@ -5,6 +5,7 @@ using System.Threading;
using OpenNest.Engine.Jobs.Adapters;
using OpenNest.Engine.Jobs.CommonLines;
using OpenNest.Geometry;
+using OpenNest.Math;
namespace OpenNest.Engine.Jobs.Placement;
@@ -83,8 +84,8 @@ public static partial class PlateFillService
{
var rotation = placed.Rotation;
var first = Place(item.Drawing, rotation, placed.Location);
- var sine = System.Math.Sin(rotation);
- var cosine = System.Math.Cos(rotation);
+ var sine = PortableMath.Sin(rotation);
+ var cosine = PortableMath.Cos(rotation);
var second = Place(item.Drawing, rotation + CommonLinePair.SecondRotation,
new Vector(placed.Location.X + pair.SecondX * cosine - pair.SecondY * sine,
placed.Location.Y + pair.SecondX * sine + pair.SecondY * cosine));
diff --git a/OpenNest.Engine/NestingEngines/Irregular/ContactFit.cs b/OpenNest.Engine/NestingEngines/Irregular/ContactFit.cs
index 1c83bf75..81718eb7 100644
--- a/OpenNest.Engine/NestingEngines/Irregular/ContactFit.cs
+++ b/OpenNest.Engine/NestingEngines/Irregular/ContactFit.cs
@@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Linq;
using Clipper2Lib;
using OpenNest.Geometry;
+using OpenNest.Math;
namespace OpenNest.Engine.NestingEngines.Irregular;
@@ -83,8 +84,8 @@ internal static class ContactFit
double theta = 0, x = centerX, y = centerY;
Pose At() => new(start.Rotation + theta,
- x - (System.Math.Cos(theta) * pivotX - System.Math.Sin(theta) * pivotY),
- y - (System.Math.Sin(theta) * pivotX + System.Math.Cos(theta) * pivotY));
+ x - (PortableMath.Cos(theta) * pivotX - PortableMath.Sin(theta) * pivotY),
+ y - (PortableMath.Sin(theta) * pivotX + PortableMath.Cos(theta) * pivotY));
Pose? best = null;
var bestWorst = double.PositiveInfinity;
var cap = MaxStep;
@@ -205,8 +206,8 @@ internal static class ContactFit
private static PathD Place(PathD local, double theta, double x, double y)
{
- var cos = System.Math.Cos(theta);
- var sin = System.Math.Sin(theta);
+ var cos = PortableMath.Cos(theta);
+ var sin = PortableMath.Sin(theta);
var result = new PathD(local.Count);
foreach (var p in local)
result.Add(new PointD(cos * p.x - sin * p.y + x, sin * p.x + cos * p.y + y));
diff --git a/OpenNest.Engine/NestingEngines/Irregular/NoFitCache.cs b/OpenNest.Engine/NestingEngines/Irregular/NoFitCache.cs
index 6afc291f..bc0785fe 100644
--- a/OpenNest.Engine/NestingEngines/Irregular/NoFitCache.cs
+++ b/OpenNest.Engine/NestingEngines/Irregular/NoFitCache.cs
@@ -6,6 +6,7 @@ using System.Linq;
using System.Threading;
using Clipper2Lib;
using OpenNest.Engine.Jobs;
+using OpenNest.Math;
namespace OpenNest.Engine.NestingEngines.Irregular;
@@ -202,7 +203,7 @@ internal sealed class NoFitCache
private static double EdgeAngle(PointD edge)
{
- var angle = System.Math.Atan2(edge.y, edge.x);
+ var angle = PortableMath.Atan2(edge.y, edge.x);
return angle < 0 ? angle + System.Math.PI * 2 : angle;
}
diff --git a/OpenNest.IO/ChrFont.cs b/OpenNest.IO/ChrFont.cs
index 3fa8bfdd..2f12f1ca 100644
--- a/OpenNest.IO/ChrFont.cs
+++ b/OpenNest.IO/ChrFont.cs
@@ -2,6 +2,7 @@ using System.Collections.Generic;
using System.IO;
using System.Text;
using OpenNest.Geometry;
+using OpenNest.Math;
namespace OpenNest.IO
{
@@ -383,9 +384,9 @@ namespace OpenNest.IO
) / d;
var radius = System.Math.Sqrt((ax - ux) * (ax - ux) + (ay - uy) * (ay - uy));
- var a0 = System.Math.Atan2(ay - uy, ax - ux);
- var am = System.Math.Atan2(by - uy, bx - ux);
- var a1 = System.Math.Atan2(cy - uy, cx - ux);
+ var a0 = PortableMath.Atan2(ay - uy, ax - ux);
+ var am = PortableMath.Atan2(by - uy, bx - ux);
+ var a1 = PortableMath.Atan2(cy - uy, cx - ux);
var ccwSweep = a1 - a0;
while (ccwSweep <= 0)
@@ -403,8 +404,8 @@ namespace OpenNest.IO
var angle = a0 + sweep * t;
output.Add(
new Vector(
- ux + radius * System.Math.Cos(angle),
- uy + radius * System.Math.Sin(angle)
+ ux + radius * PortableMath.Cos(angle),
+ uy + radius * PortableMath.Sin(angle)
)
);
}
diff --git a/OpenNest.IO/Dxf.cs b/OpenNest.IO/Dxf.cs
index 844c8dc5..b454956f 100644
--- a/OpenNest.IO/Dxf.cs
+++ b/OpenNest.IO/Dxf.cs
@@ -398,9 +398,9 @@ namespace OpenNest.IO
center = new XYZ(center.X + CurPos.X, center.Y + CurPos.Y, 0);
}
- var startAngle = System.Math.Atan2(CurPos.Y - center.Y, CurPos.X - center.X);
+ var startAngle = PortableMath.Atan2(CurPos.Y - center.Y, CurPos.X - center.X);
- var endAngle = System.Math.Atan2(endpt.Y - center.Y, endpt.X - center.X);
+ var endAngle = PortableMath.Atan2(endpt.Y - center.Y, endpt.X - center.X);
if (arc.Rotation == RotationType.CW)
Generic.Swap(ref startAngle, ref endAngle);
diff --git a/OpenNest.IO/Extensions.cs b/OpenNest.IO/Extensions.cs
index c4773ebf..1b8f8e2b 100644
--- a/OpenNest.IO/Extensions.cs
+++ b/OpenNest.IO/Extensions.cs
@@ -4,6 +4,7 @@ using System.Drawing;
using ACadSharp.Entities;
using CSMath;
using OpenNest.Geometry;
+using OpenNest.Math;
namespace OpenNest.IO
{
@@ -210,7 +211,7 @@ namespace OpenNest.IO
var semiMajor = System.Math.Sqrt(majorAxis.X * majorAxis.X + majorAxis.Y * majorAxis.Y);
var semiMinor = semiMajor * ellipse.RadiusRatio;
- var rotation = System.Math.Atan2(majorAxis.Y, majorAxis.X);
+ var rotation = PortableMath.Atan2(majorAxis.Y, majorAxis.X);
var layer = ellipse.Layer.ToOpenNest();
var color = ellipse.ResolveColor();
diff --git a/OpenNest.IO/SplitDxfWriter.cs b/OpenNest.IO/SplitDxfWriter.cs
index 842df35e..78ebf58d 100644
--- a/OpenNest.IO/SplitDxfWriter.cs
+++ b/OpenNest.IO/SplitDxfWriter.cs
@@ -7,6 +7,7 @@ using ACadSharp.Tables;
using CSMath;
using OpenNest.Bending;
using OpenNest.Converters;
+using OpenNest.Math;
// Disambiguate Entity — both ACadSharp.Entities and OpenNest.Geometry define it
using GeoEntity = OpenNest.Geometry.Entity;
@@ -174,8 +175,8 @@ namespace OpenNest.IO
else
{
var angle = start.AngleTo(end);
- var dx = System.Math.Cos(angle) * EtchLength;
- var dy = System.Math.Sin(angle) * EtchLength;
+ var dx = PortableMath.Cos(angle) * EtchLength;
+ var dy = PortableMath.Sin(angle) * EtchLength;
doc.Entities.Add(
new ACadSharp.Entities.Line
diff --git a/OpenNest.IO/SvgReader.cs b/OpenNest.IO/SvgReader.cs
index 2f2c6512..fc88d65d 100644
--- a/OpenNest.IO/SvgReader.cs
+++ b/OpenNest.IO/SvgReader.cs
@@ -703,8 +703,8 @@ namespace OpenNest.IO
return;
}
- var cos = System.Math.Cos(rotation);
- var sin = System.Math.Sin(rotation);
+ var cos = PortableMath.Cos(rotation);
+ var sin = PortableMath.Sin(rotation);
var halfX = (from.X - to.X) / 2;
var halfY = (from.Y - to.Y) / 2;
var x = cos * halfX + sin * halfY;
@@ -731,8 +731,8 @@ namespace OpenNest.IO
sin * centerX + cos * centerY + (from.Y + to.Y) / 2
);
- var startAngle = System.Math.Atan2((y - centerY) / ry, (x - centerX) / rx);
- var endAngle = System.Math.Atan2((-y - centerY) / ry, (-x - centerX) / rx);
+ var startAngle = PortableMath.Atan2((y - centerY) / ry, (x - centerX) / rx);
+ var endAngle = PortableMath.Atan2((-y - centerY) / ry, (-x - centerX) / rx);
var delta = endAngle - startAngle;
if (sweep && delta < 0)
delta += Angle.TwoPI;
@@ -758,17 +758,17 @@ namespace OpenNest.IO
Layer layer
)
{
- var cos = System.Math.Cos(rotation);
- var sin = System.Math.Sin(rotation);
+ var cos = PortableMath.Cos(rotation);
+ var sin = PortableMath.Sin(rotation);
var axisX = t.ApplyLinear(new Vector(rx * cos, rx * sin));
var axisY = t.ApplyLinear(new Vector(-ry * sin, ry * cos));
- var offset = 0.5 * System.Math.Atan2(
+ var offset = 0.5 * PortableMath.Atan2(
2 * (axisX.X * axisY.X + axisX.Y * axisY.Y),
axisX.X * axisX.X + axisX.Y * axisX.Y - axisY.X * axisY.X - axisY.Y * axisY.Y
);
- var major = axisX * System.Math.Cos(offset) + axisY * System.Math.Sin(offset);
- var minor = axisY * System.Math.Cos(offset) - axisX * System.Math.Sin(offset);
+ var major = axisX * PortableMath.Cos(offset) + axisY * PortableMath.Sin(offset);
+ var minor = axisY * PortableMath.Cos(offset) - axisX * PortableMath.Sin(offset);
var semiMajor = Length2(major);
var semiMinor = Length2(minor);
@@ -784,7 +784,7 @@ namespace OpenNest.IO
t.Apply(center),
semiMajor,
semiMinor,
- System.Math.Atan2(major.Y, major.X),
+ PortableMath.Atan2(major.Y, major.X),
System.Math.Min(from, to),
System.Math.Max(from, to),
tolerance
diff --git a/OpenNest.IO/SvgTransform.cs b/OpenNest.IO/SvgTransform.cs
index c8d21c1d..944b5eb7 100644
--- a/OpenNest.IO/SvgTransform.cs
+++ b/OpenNest.IO/SvgTransform.cs
@@ -4,6 +4,7 @@ using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using OpenNest.Geometry;
+using OpenNest.Math;
namespace OpenNest.IO
{
@@ -126,12 +127,12 @@ namespace OpenNest.IO
private static SvgTransform Rotation(double degrees)
{
var radians = degrees * System.Math.PI / 180;
- var cos = System.Math.Cos(radians);
- var sin = System.Math.Sin(radians);
+ var cos = PortableMath.Cos(radians);
+ var sin = PortableMath.Sin(radians);
return new SvgTransform(cos, sin, -sin, cos, 0, 0);
}
private static double Tan(double degrees) =>
- System.Math.Tan(degrees * System.Math.PI / 180);
+ PortableMath.Tan(degrees * System.Math.PI / 180);
}
}
diff --git a/OpenNest.Tests/Geometry/BoundingBoxHelperTests.cs b/OpenNest.Tests/Geometry/BoundingBoxHelperTests.cs
index f618459b..619e9685 100644
--- a/OpenNest.Tests/Geometry/BoundingBoxHelperTests.cs
+++ b/OpenNest.Tests/Geometry/BoundingBoxHelperTests.cs
@@ -112,11 +112,12 @@ public class BoundingBoxHelperTests
return new Box(l, bo, r - l, t - bo);
}
- // Frozen from DiscPacker.Rotate / CutoutCirclePacker.Rotate / DrawingAligner.Rotate.
+ // Frozen from DiscPacker.Rotate / CutoutCirclePacker.Rotate / DrawingAligner.Rotate,
+ // trig spelled PortableMath as production now is.
private static Vector FrozenRotate(Vector v, double angle)
{
- var sine = System.Math.Sin(angle);
- var cosine = System.Math.Cos(angle);
+ var sine = OpenNest.Math.PortableMath.Sin(angle);
+ var cosine = OpenNest.Math.PortableMath.Cos(angle);
return new Vector(v.X * cosine - v.Y * sine, v.X * sine + v.Y * cosine);
}
}
diff --git a/OpenNest.Tests/Geometry/LegacyEntitySlideEvents.cs b/OpenNest.Tests/Geometry/LegacyEntitySlideEvents.cs
index 62744b00..5236f225 100644
--- a/OpenNest.Tests/Geometry/LegacyEntitySlideEvents.cs
+++ b/OpenNest.Tests/Geometry/LegacyEntitySlideEvents.cs
@@ -2,7 +2,8 @@
// commit ad5f2a9041ee14d0a85aab00e0c8a9f5082d5b6b, before the axis band filter, used only as
// the differential oracle in SlideBandTests. Mechanical edits only: renamed types, internal
// visibility, calls to unchanged SlideEvents members qualified, the private Eps constant
-// spelled Tolerance.Epsilon, and the four-space indentation of the original kept.
+// spelled Tolerance.Epsilon, trig spelled PortableMath as production now is, and the
+// four-space indentation of the original kept.
#nullable disable
using System.Collections.Generic;
using OpenNest.Geometry;
@@ -54,7 +55,7 @@ namespace OpenNest.Tests.Geometry
// The directional distance from an arc point at angle θ to the
// line is t(θ) = [A + r·(ey·cosθ − ex·sinθ)] / det.
// dt/dθ = 0 at θ = atan2(−ex, ey) and θ + π.
- var theta1 = Angle.NormalizeRad(System.Math.Atan2(-ex, ey));
+ var theta1 = Angle.NormalizeRad(PortableMath.Atan2(-ex, ey));
var theta2 = Angle.NormalizeRad(theta1 + System.Math.PI);
for (var k = 0; k < 2; k++)
@@ -64,8 +65,8 @@ namespace OpenNest.Tests.Geometry
if (arc != null && !Angle.IsBetweenRad(theta, arc.StartAngle, arc.EndAngle, arc.IsReversed))
continue;
- var qx = cx + r * System.Math.Cos(theta);
- var qy = cy + r * System.Math.Sin(theta);
+ var qx = cx + r * PortableMath.Cos(theta);
+ var qy = cy + r * PortableMath.Sin(theta);
SlideEvents.RayLine(ref sink, qx, qy, p1x, p1y, p2x, p2y, rayX, rayY, arcMoves);
if (sink.IsDone)
diff --git a/OpenNest.Tests/Math/PortableMathTests.cs b/OpenNest.Tests/Math/PortableMathTests.cs
index 57ad8611..b337db8e 100644
--- a/OpenNest.Tests/Math/PortableMathTests.cs
+++ b/OpenNest.Tests/Math/PortableMathTests.cs
@@ -87,6 +87,47 @@ public class PortableMathTests
Assert.True(double.IsNaN(PortableMath.Atan2(double.NaN, 1)));
}
+ ///
+ /// Geometry and nesting code must not call the runtime's trig: one call is enough for the
+ /// same job to nest differently on Windows and Linux.
+ ///
+ [Fact]
+ public void LayoutProjects_UsePortableTrigOnly()
+ {
+ var root = CuttingPlanning.PlanningCharacterizationTests.RepositoryRoot();
+ var runtimeTrig = new System.Text.RegularExpressions.Regex(
+ @"\bMath\.(Sin|Cos|Tan|Atan2|Atan|Asin|Acos|SinCos)\("
+ );
+ var offenders = new List();
+ var projects = new[] { "OpenNest.Core", "OpenNest.Engine", "OpenNest.IO", "OpenNest.Data" };
+ foreach (var project in projects)
+ {
+ var files = Directory.EnumerateFiles(
+ Path.Combine(root, project),
+ "*.cs",
+ SearchOption.AllDirectories
+ );
+ foreach (var file in files)
+ {
+ var parts = Path.GetRelativePath(root, file).Split(Path.DirectorySeparatorChar);
+ var generated = parts.Contains("bin") || parts.Contains("obj");
+ if (generated || parts[^1] == "PortableMath.cs")
+ continue;
+ var lines = File.ReadAllLines(file);
+ for (var i = 0; i < lines.Length; i++)
+ {
+ if (runtimeTrig.IsMatch(lines[i]))
+ offenders.Add($"{string.Join('/', parts)}:{i + 1}");
+ }
+ }
+ }
+
+ Assert.True(
+ offenders.Count == 0,
+ "use PortableMath instead:\n" + string.Join("\n", offenders)
+ );
+ }
+
///
/// The point of PortableMath: the same bits on every platform. The digest was captured on
/// Linux and must also pass on Windows (Project Memory 518f3b58).