mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-11-13 02:21:09 -05:00
fix(geometry): nest the same on Windows and Linux
Geometry, nesting and import code now call PortableMath instead of System.Math for Sin, Cos, Tan, Atan, Atan2, Asin and Acos (Core, Engine, IO, Data; 269 calls in 64 files). A test fails if those projects call the runtime's trig again. With the runtime's trig, Windows and Linux disagreed in the last bit at non-quarter angles and the same job could nest differently: the learned-angle nests (a cross fixture) placed 23 parts in Strip on Windows and 28 on Linux. Now every Engine.Tests pin gives the same layout on both (3600 passing on Linux and on DesktopPC), Linux layouts are unchanged, and the learned-angle rows are re-pinned to the shared layouts, which pass the overlap and work-area checks, and run on every platform again. Fixes Project Memory 518f3b58.
This commit is contained in:
1 parent
921d100aa0
commit
49603eddf9
68 files changed
+327
-273
No files matched your search
@@ -62,8 +62,8 @@ namespace OpenNest.Bending
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
var angle = StartPoint.AngleTo(EndPoint);
|
var angle = StartPoint.AngleTo(EndPoint);
|
||||||
var dx = System.Math.Cos(angle) * etchLength;
|
var dx = PortableMath.Cos(angle) * etchLength;
|
||||||
var dy = System.Math.Sin(angle) * etchLength;
|
var dy = PortableMath.Sin(angle) * etchLength;
|
||||||
|
|
||||||
result.Add(
|
result.Add(
|
||||||
CreateEtchLine(StartPoint, new Vector(StartPoint.X + dx, StartPoint.Y + dy))
|
CreateEtchLine(StartPoint, new Vector(StartPoint.X + dx, StartPoint.Y + dy))
|
||||||
|
|||||||
@@ -153,8 +153,8 @@ namespace OpenNest.Breakoffs
|
|||||||
}
|
}
|
||||||
var arc = (Arc)e;
|
var arc = (Arc)e;
|
||||||
var angle = ArcAngleAt(arc, local);
|
var angle = ArcAngleAt(arc, local);
|
||||||
return new Vector(arc.Center.X + arc.Radius * System.Math.Cos(angle),
|
return new Vector(arc.Center.X + arc.Radius * PortableMath.Cos(angle),
|
||||||
arc.Center.Y + arc.Radius * System.Math.Sin(angle));
|
arc.Center.Y + arc.Radius * PortableMath.Sin(angle));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Vector TangentOn(Entity e, double local)
|
private static Vector TangentOn(Entity e, double local)
|
||||||
@@ -168,8 +168,8 @@ namespace OpenNest.Breakoffs
|
|||||||
var arc = (Arc)e;
|
var arc = (Arc)e;
|
||||||
var angle = ArcAngleAt(arc, local);
|
var angle = ArcAngleAt(arc, local);
|
||||||
return arc.IsReversed
|
return arc.IsReversed
|
||||||
? new Vector(System.Math.Sin(angle), -System.Math.Cos(angle))
|
? new Vector(PortableMath.Sin(angle), -PortableMath.Cos(angle))
|
||||||
: new Vector(-System.Math.Sin(angle), System.Math.Cos(angle));
|
: new Vector(-PortableMath.Sin(angle), PortableMath.Cos(angle));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static double ArcAngleAt(Arc arc, double local) =>
|
private static double ArcAngleAt(Arc arc, double local) =>
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ namespace OpenNest.CNC
|
|||||||
{
|
{
|
||||||
var angle = StartAngle + Sweep * fraction;
|
var angle = StartAngle + Sweep * fraction;
|
||||||
var sign = System.Math.Sign(Sweep);
|
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;
|
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;
|
var angle = StartAngle + Sweep * fraction;
|
||||||
return new Vector(
|
return new Vector(
|
||||||
Center.X + Radius * System.Math.Cos(angle),
|
Center.X + Radius * PortableMath.Cos(angle),
|
||||||
Center.Y + Radius * System.Math.Sin(angle));
|
Center.Y + Radius * PortableMath.Sin(angle));
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Vector(
|
return new Vector(
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using System.Threading;
|
|||||||
using OpenNest.CNC.CuttingStrategy;
|
using OpenNest.CNC.CuttingStrategy;
|
||||||
using OpenNest.Diagnostics;
|
using OpenNest.Diagnostics;
|
||||||
using OpenNest.Geometry;
|
using OpenNest.Geometry;
|
||||||
|
using OpenNest.Math;
|
||||||
using OpenNest.Support;
|
using OpenNest.Support;
|
||||||
|
|
||||||
namespace OpenNest.CNC.CuttingPlanning;
|
namespace OpenNest.CNC.CuttingPlanning;
|
||||||
@@ -511,8 +512,8 @@ public sealed class PreparedContours
|
|||||||
if (entity is Circle circle)
|
if (entity is Circle circle)
|
||||||
{
|
{
|
||||||
for (var angle = 0; angle < 8 && entries.Count < maxEntries; angle++)
|
for (var angle = 0; angle < 8 && entries.Count < maxEntries; angle++)
|
||||||
Add(i, circle.Center + new Vector(System.Math.Cos(angle * System.Math.PI / 4),
|
Add(i, circle.Center + new Vector(PortableMath.Cos(angle * System.Math.PI / 4),
|
||||||
System.Math.Sin(angle * System.Math.PI / 4)) * circle.Radius);
|
PortableMath.Sin(angle * System.Math.PI / 4)) * circle.Radius);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -764,8 +765,8 @@ public sealed class PreparedContours
|
|||||||
// The legacy eight compass points, same native construction.
|
// The legacy eight compass points, same native construction.
|
||||||
for (var angle = 0; angle < 8; angle++)
|
for (var angle = 0; angle < 8; angle++)
|
||||||
fallbacks.Add(Fallback(i, circle.Center + new Vector(
|
fallbacks.Add(Fallback(i, circle.Center + new Vector(
|
||||||
System.Math.Cos(angle * System.Math.PI / 4),
|
PortableMath.Cos(angle * System.Math.PI / 4),
|
||||||
System.Math.Sin(angle * System.Math.PI / 4)) * circle.Radius,
|
PortableMath.Sin(angle * System.Math.PI / 4)) * circle.Radius,
|
||||||
AutomaticEntryKind.CircleCompass));
|
AutomaticEntryKind.CircleCompass));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -977,7 +978,7 @@ public sealed class PreparedContours
|
|||||||
if (ContourCuttingStrategy.ClassifyTurn((before.EndPoint - before.StartPoint).Normalize(),
|
if (ContourCuttingStrategy.ClassifyTurn((before.EndPoint - before.StartPoint).Normalize(),
|
||||||
(after.EndPoint - after.StartPoint).Normalize(), winding) != ContourCuttingStrategy.CornerKind.Convex)
|
(after.EndPoint - after.StartPoint).Normalize(), winding) != ContourCuttingStrategy.CornerKind.Convex)
|
||||||
return null;
|
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;
|
return reach <= System.Math.Min(before.Length, after.Length) ? reach : null;
|
||||||
|
|
||||||
bool IsSmooth(Vector vertex, Entity entity) =>
|
bool IsSmooth(Vector vertex, Entity entity) =>
|
||||||
|
|||||||
@@ -163,8 +163,8 @@ internal static class SeamOvercut
|
|||||||
/// <summary>The angle traveled around <paramref name="center"/> from one point to another, in [0, 2pi).</summary>
|
/// <summary>The angle traveled around <paramref name="center"/> from one point to another, in [0, 2pi).</summary>
|
||||||
private static double Travel(Vector center, Vector from, Vector to, bool clockwise)
|
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 start = PortableMath.Atan2(from.Y - center.Y, from.X - center.X);
|
||||||
var end = System.Math.Atan2(to.Y - center.Y, to.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);
|
var travel = (clockwise ? start - end : end - start) % (2 * System.Math.PI);
|
||||||
return travel < 0 ? travel + 2 * System.Math.PI : travel;
|
return travel < 0 ? travel + 2 * System.Math.PI : travel;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -516,8 +516,8 @@ namespace OpenNest.CNC.CuttingStrategy
|
|||||||
|
|
||||||
var outwardAngle = normal - System.Math.PI;
|
var outwardAngle = normal - System.Math.PI;
|
||||||
point = new Vector(
|
point = new Vector(
|
||||||
circle.Center.X + circle.Radius * System.Math.Cos(outwardAngle),
|
circle.Center.X + circle.Radius * PortableMath.Cos(outwardAngle),
|
||||||
circle.Center.Y + circle.Radius * System.Math.Sin(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);
|
var adjacentNormal = ComputeNormal(point, corner.Incoming, contourType, winding);
|
||||||
// Sum unit normals rather than averaging angles (which fails at 0/2Ï€).
|
// Sum unit normals rather than averaging angles (which fails at 0/2Ï€).
|
||||||
// Winding makes this point into the scrap even at reflex corners.
|
// Winding makes this point into the scrap even at reflex corners.
|
||||||
var x = System.Math.Cos(normal) + System.Math.Cos(adjacentNormal);
|
var x = PortableMath.Cos(normal) + PortableMath.Cos(adjacentNormal);
|
||||||
var y = System.Math.Sin(normal) + System.Math.Sin(adjacentNormal);
|
var y = PortableMath.Sin(normal) + PortableMath.Sin(adjacentNormal);
|
||||||
if (!double.IsFinite(x) || !double.IsFinite(y)
|
if (!double.IsFinite(x) || !double.IsFinite(y)
|
||||||
|| x * x + y * y <= Tolerance.Epsilon * Tolerance.Epsilon)
|
|| x * x + y * y <= Tolerance.Epsilon * Tolerance.Epsilon)
|
||||||
return null; // Opposing normals at a cusp have no unique bisector.
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -1009,12 +1009,12 @@ namespace OpenNest.CNC.CuttingStrategy
|
|||||||
|| ClassifyTurn(tangentEnd, tangentStart, winding) != CornerKind.Convex)
|
|| ClassifyTurn(tangentEnd, tangentStart, winding) != CornerKind.Convex)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var x = System.Math.Cos(startNormal) + System.Math.Cos(endNormal);
|
var x = PortableMath.Cos(startNormal) + PortableMath.Cos(endNormal);
|
||||||
var y = System.Math.Sin(startNormal) + System.Math.Sin(endNormal);
|
var y = PortableMath.Sin(startNormal) + PortableMath.Sin(endNormal);
|
||||||
if (x * x + y * y <= Tolerance.Epsilon * Tolerance.Epsilon)
|
if (x * x + y * y <= Tolerance.Epsilon * Tolerance.Epsilon)
|
||||||
return null;
|
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 pierce = leadIn.GetPiercePoint(start, normal, turn);
|
||||||
var leadOutEnd = ((LinearMove)leadOut.Generate(end, normal, turn)[0]).EndPoint;
|
var leadOutEnd = ((LinearMove)leadOut.Generate(end, normal, turn)[0]).EndPoint;
|
||||||
return IsClearStraightLead(shape, start, pierce, clearance)
|
return IsClearStraightLead(shape, start, pierce, clearance)
|
||||||
@@ -1248,7 +1248,7 @@ namespace OpenNest.CNC.CuttingStrategy
|
|||||||
var length = pierce.DistanceTo(point);
|
var length = pierce.DistanceTo(point);
|
||||||
if (length <= Math.Tolerance.Epsilon)
|
if (length <= Math.Tolerance.Epsilon)
|
||||||
return leadIn;
|
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
|
return new LineLeadIn
|
||||||
{
|
{
|
||||||
Length = length,
|
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 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));
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A straight lead-in from <paramref name="pierce"/> to <paramref name="point"/>, with the
|
/// A straight lead-in from <paramref name="pierce"/> to <paramref name="point"/>, with the
|
||||||
@@ -1360,7 +1360,7 @@ namespace OpenNest.CNC.CuttingStrategy
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private static LeadIn StraightLeadIn(Vector point, Vector pierce, out double normal)
|
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 };
|
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)
|
if (shape.Entities is [Circle round] && round.Radius > tabSize / 2)
|
||||||
{
|
{
|
||||||
var entry = round.Center.AngleTo(center);
|
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 reversed = round.Rotation == RotationType.CW;
|
||||||
var arc = new Arc(round.Center, round.Radius, entry, reversed ? entry + gap : entry - gap, reversed)
|
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 = (Shape)cut.Clone();
|
||||||
path.Reverse();
|
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(leadIn.Generate(entry, normal, fromEnd ? RotationType.CW : RotationType.CCW));
|
||||||
program.Codes.AddRange(ConvertShapeToMoves(path, entry, LayerType.Break));
|
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)
|
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 a0 = PortableMath.Atan2(start.Y - center.Y, start.X - center.X);
|
||||||
var a1 = System.Math.Atan2(end.Y - center.Y, end.X - center.X);
|
var a1 = PortableMath.Atan2(end.Y - center.Y, end.X - center.X);
|
||||||
var ap = System.Math.Atan2(point.Y - center.Y, point.X - center.X);
|
var ap = PortableMath.Atan2(point.Y - center.Y, point.X - center.X);
|
||||||
var sign = rotation == RotationType.CCW ? 1 : -1;
|
var sign = rotation == RotationType.CCW ? 1 : -1;
|
||||||
var sweep = Angle.NormalizeRad(sign * (a1 - a0));
|
var sweep = Angle.NormalizeRad(sign * (a1 - a0));
|
||||||
if (sweep <= Tolerance.Epsilon)
|
if (sweep <= Tolerance.Epsilon)
|
||||||
@@ -1841,8 +1841,8 @@ namespace OpenNest.CNC.CuttingStrategy
|
|||||||
{
|
{
|
||||||
var angle = atEnd ? arc.EndAngle : arc.StartAngle;
|
var angle = atEnd ? arc.EndAngle : arc.StartAngle;
|
||||||
return arc.IsReversed
|
return arc.IsReversed
|
||||||
? new Vector(System.Math.Sin(angle), -System.Math.Cos(angle))
|
? new Vector(PortableMath.Sin(angle), -PortableMath.Cos(angle))
|
||||||
: new Vector(-System.Math.Sin(angle), System.Math.Cos(angle));
|
: new Vector(-PortableMath.Sin(angle), PortableMath.Cos(angle));
|
||||||
}
|
}
|
||||||
var line = (Line)entity;
|
var line = (Line)entity;
|
||||||
return line.EndPoint - line.StartPoint;
|
return line.EndPoint - line.StartPoint;
|
||||||
|
|||||||
@@ -46,8 +46,8 @@ namespace OpenNest.CNC.CuttingStrategy
|
|||||||
var angle = contourNormalAngle + (winding == RotationType.CCW ? shortfall : -shortfall);
|
var angle = contourNormalAngle + (winding == RotationType.CCW ? shortfall : -shortfall);
|
||||||
var center = ArcCenter(contourStartPoint, contourNormalAngle);
|
var center = ArcCenter(contourStartPoint, contourNormalAngle);
|
||||||
return new Vector(
|
return new Vector(
|
||||||
center.X + Radius * System.Math.Cos(angle),
|
center.X + Radius * PortableMath.Cos(angle),
|
||||||
center.Y + Radius * System.Math.Sin(angle)
|
center.Y + Radius * PortableMath.Sin(angle)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,8 +55,8 @@ namespace OpenNest.CNC.CuttingStrategy
|
|||||||
new ArcLeadIn { Radius = Radius * factor, SweepAngle = SweepAngle };
|
new ArcLeadIn { Radius = Radius * factor, SweepAngle = SweepAngle };
|
||||||
|
|
||||||
private Vector ArcCenter(Vector contourStartPoint, double contourNormalAngle) => new Vector(
|
private Vector ArcCenter(Vector contourStartPoint, double contourNormalAngle) => new Vector(
|
||||||
contourStartPoint.X + Radius * System.Math.Cos(contourNormalAngle),
|
contourStartPoint.X + Radius * PortableMath.Cos(contourNormalAngle),
|
||||||
contourStartPoint.Y + Radius * System.Math.Sin(contourNormalAngle)
|
contourStartPoint.Y + Radius * PortableMath.Sin(contourNormalAngle)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -28,22 +28,22 @@ namespace OpenNest.CNC.CuttingStrategy
|
|||||||
public static LineArcApproach Create(Vector contourStartPoint, double contourNormalAngle,
|
public static LineArcApproach Create(Vector contourStartPoint, double contourNormalAngle,
|
||||||
double arcRadius, double approachAngle, double lineLength, RotationType winding)
|
double arcRadius, double approachAngle, double lineLength, RotationType winding)
|
||||||
{
|
{
|
||||||
var arcCenterX = contourStartPoint.X + arcRadius * System.Math.Cos(contourNormalAngle);
|
var arcCenterX = contourStartPoint.X + arcRadius * PortableMath.Cos(contourNormalAngle);
|
||||||
var arcCenterY = contourStartPoint.Y + arcRadius * System.Math.Sin(contourNormalAngle);
|
var arcCenterY = contourStartPoint.Y + arcRadius * PortableMath.Sin(contourNormalAngle);
|
||||||
|
|
||||||
// The approach angle is measured back from the normal against the turn, so the arc
|
// 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.
|
// runs the short way round to the contour point whichever way it turns.
|
||||||
var approach = Angle.ToRadians(approachAngle);
|
var approach = Angle.ToRadians(approachAngle);
|
||||||
var lineAngle = contourNormalAngle + (winding == RotationType.CCW ? approach : -approach);
|
var lineAngle = contourNormalAngle + (winding == RotationType.CCW ? approach : -approach);
|
||||||
var arcStartX = arcCenterX + arcRadius * System.Math.Cos(lineAngle);
|
var arcStartX = arcCenterX + arcRadius * PortableMath.Cos(lineAngle);
|
||||||
var arcStartY = arcCenterY + arcRadius * System.Math.Sin(lineAngle);
|
var arcStartY = arcCenterY + arcRadius * PortableMath.Sin(lineAngle);
|
||||||
|
|
||||||
return new LineArcApproach(
|
return new LineArcApproach(
|
||||||
new Vector(arcCenterX, arcCenterY),
|
new Vector(arcCenterX, arcCenterY),
|
||||||
new Vector(arcStartX, arcStartY),
|
new Vector(arcStartX, arcStartY),
|
||||||
new Vector(
|
new Vector(
|
||||||
arcStartX + lineLength * System.Math.Cos(lineAngle),
|
arcStartX + lineLength * PortableMath.Cos(lineAngle),
|
||||||
arcStartY + lineLength * System.Math.Sin(lineAngle)));
|
arcStartY + lineLength * PortableMath.Sin(lineAngle)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Rapid to the pierce point, then the line and arc on the lead-in layer.</summary>
|
/// <summary>Rapid to the pierce point, then the line and arc on the lead-in layer.</summary>
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ namespace OpenNest.CNC.CuttingStrategy
|
|||||||
{
|
{
|
||||||
var approachAngle = contourNormalAngle - Angle.HalfPI + Angle.ToRadians(ApproachAngle);
|
var approachAngle = contourNormalAngle - Angle.HalfPI + Angle.ToRadians(ApproachAngle);
|
||||||
return new Vector(
|
return new Vector(
|
||||||
contourStartPoint.X + Length * System.Math.Cos(approachAngle),
|
contourStartPoint.X + Length * PortableMath.Cos(approachAngle),
|
||||||
contourStartPoint.Y + Length * System.Math.Sin(approachAngle)
|
contourStartPoint.Y + Length * PortableMath.Sin(approachAngle)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ namespace OpenNest.CNC.CuttingStrategy
|
|||||||
|
|
||||||
var secondAngle = contourNormalAngle - Angle.HalfPI + Angle.ToRadians(ApproachAngle1);
|
var secondAngle = contourNormalAngle - Angle.HalfPI + Angle.ToRadians(ApproachAngle1);
|
||||||
var midPoint = new Vector(
|
var midPoint = new Vector(
|
||||||
contourStartPoint.X + Length2 * System.Math.Cos(secondAngle),
|
contourStartPoint.X + Length2 * PortableMath.Cos(secondAngle),
|
||||||
contourStartPoint.Y + Length2 * System.Math.Sin(secondAngle)
|
contourStartPoint.Y + Length2 * PortableMath.Sin(secondAngle)
|
||||||
);
|
);
|
||||||
|
|
||||||
return new List<ICode>
|
return new List<ICode>
|
||||||
@@ -37,13 +37,13 @@ namespace OpenNest.CNC.CuttingStrategy
|
|||||||
RotationType winding = RotationType.CW)
|
RotationType winding = RotationType.CW)
|
||||||
{
|
{
|
||||||
var secondAngle = contourNormalAngle - Angle.HalfPI + Angle.ToRadians(ApproachAngle1);
|
var secondAngle = contourNormalAngle - Angle.HalfPI + Angle.ToRadians(ApproachAngle1);
|
||||||
var midX = contourStartPoint.X + Length2 * System.Math.Cos(secondAngle);
|
var midX = contourStartPoint.X + Length2 * PortableMath.Cos(secondAngle);
|
||||||
var midY = contourStartPoint.Y + Length2 * System.Math.Sin(secondAngle);
|
var midY = contourStartPoint.Y + Length2 * PortableMath.Sin(secondAngle);
|
||||||
|
|
||||||
var firstAngle = secondAngle + Angle.ToRadians(ApproachAngle2);
|
var firstAngle = secondAngle + Angle.ToRadians(ApproachAngle2);
|
||||||
return new Vector(
|
return new Vector(
|
||||||
midX + Length1 * System.Math.Cos(firstAngle),
|
midX + Length1 * PortableMath.Cos(firstAngle),
|
||||||
midY + Length1 * System.Math.Sin(firstAngle)
|
midY + Length1 * PortableMath.Sin(firstAngle)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using OpenNest.Geometry;
|
using OpenNest.Geometry;
|
||||||
|
using OpenNest.Math;
|
||||||
|
|
||||||
namespace OpenNest.CNC.CuttingStrategy
|
namespace OpenNest.CNC.CuttingStrategy
|
||||||
{
|
{
|
||||||
@@ -17,16 +18,16 @@ namespace OpenNest.CNC.CuttingStrategy
|
|||||||
RotationType winding = RotationType.CW
|
RotationType winding = RotationType.CW
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
var arcCenterX = contourEndPoint.X + Radius * System.Math.Cos(contourNormalAngle);
|
var arcCenterX = contourEndPoint.X + Radius * PortableMath.Cos(contourNormalAngle);
|
||||||
var arcCenterY = contourEndPoint.Y + Radius * System.Math.Sin(contourNormalAngle);
|
var arcCenterY = contourEndPoint.Y + Radius * PortableMath.Sin(contourNormalAngle);
|
||||||
var arcCenter = new Vector(arcCenterX, arcCenterY);
|
var arcCenter = new Vector(arcCenterX, arcCenterY);
|
||||||
|
|
||||||
// The contour point sits opposite the normal from the center; turn a quarter on.
|
// The contour point sits opposite the normal from the center; turn a quarter on.
|
||||||
var endAngle = contourNormalAngle
|
var endAngle = contourNormalAngle
|
||||||
+ (winding == RotationType.CCW ? -System.Math.PI / 2 : System.Math.PI / 2);
|
+ (winding == RotationType.CCW ? -System.Math.PI / 2 : System.Math.PI / 2);
|
||||||
var endPoint = new Vector(
|
var endPoint = new Vector(
|
||||||
arcCenterX + Radius * System.Math.Cos(endAngle),
|
arcCenterX + Radius * PortableMath.Cos(endAngle),
|
||||||
arcCenterY + Radius * System.Math.Sin(endAngle)
|
arcCenterY + Radius * PortableMath.Sin(endAngle)
|
||||||
);
|
);
|
||||||
|
|
||||||
return new List<ICode>
|
return new List<ICode>
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ namespace OpenNest.CNC.CuttingStrategy
|
|||||||
{
|
{
|
||||||
var overcutAngle = contourNormalAngle + Angle.HalfPI - Angle.ToRadians(ApproachAngle);
|
var overcutAngle = contourNormalAngle + Angle.HalfPI - Angle.ToRadians(ApproachAngle);
|
||||||
var endPoint = new Vector(
|
var endPoint = new Vector(
|
||||||
contourEndPoint.X + Length * System.Math.Cos(overcutAngle),
|
contourEndPoint.X + Length * PortableMath.Cos(overcutAngle),
|
||||||
contourEndPoint.Y + Length * System.Math.Sin(overcutAngle)
|
contourEndPoint.Y + Length * PortableMath.Sin(overcutAngle)
|
||||||
);
|
);
|
||||||
|
|
||||||
return new List<ICode> { new LinearMove(endPoint) { Layer = LayerType.Leadout } };
|
return new List<ICode> { new LinearMove(endPoint) { Layer = LayerType.Leadout } };
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using OpenNest.Geometry;
|
using OpenNest.Geometry;
|
||||||
|
using OpenNest.Math;
|
||||||
|
|
||||||
namespace OpenNest.CNC.CuttingStrategy
|
namespace OpenNest.CNC.CuttingStrategy
|
||||||
{
|
{
|
||||||
@@ -23,8 +24,8 @@ namespace OpenNest.CNC.CuttingStrategy
|
|||||||
|
|
||||||
var scoreAngle = contourNormalAngle + System.Math.PI;
|
var scoreAngle = contourNormalAngle + System.Math.PI;
|
||||||
var scoreEnd = new Vector(
|
var scoreEnd = new Vector(
|
||||||
tabStartPoint.X + BreakerDepth * System.Math.Cos(scoreAngle),
|
tabStartPoint.X + BreakerDepth * PortableMath.Cos(scoreAngle),
|
||||||
tabStartPoint.Y + BreakerDepth * System.Math.Sin(scoreAngle)
|
tabStartPoint.Y + BreakerDepth * PortableMath.Sin(scoreAngle)
|
||||||
);
|
);
|
||||||
codes.Add(new LinearMove(scoreEnd));
|
codes.Add(new LinearMove(scoreEnd));
|
||||||
codes.Add(new RapidMove(tabEndPoint));
|
codes.Add(new RapidMove(tabEndPoint));
|
||||||
|
|||||||
@@ -103,8 +103,8 @@ namespace OpenNest.CNC
|
|||||||
|
|
||||||
if (subpgm.Offset.X != 0 || subpgm.Offset.Y != 0)
|
if (subpgm.Offset.X != 0 || subpgm.Offset.Y != 0)
|
||||||
{
|
{
|
||||||
var cos = System.Math.Cos(angle);
|
var cos = PortableMath.Cos(angle);
|
||||||
var sin = System.Math.Sin(angle);
|
var sin = PortableMath.Sin(angle);
|
||||||
var dx = subpgm.Offset.X - origin.X;
|
var dx = subpgm.Offset.X - origin.X;
|
||||||
var dy = subpgm.Offset.Y - origin.Y;
|
var dy = subpgm.Offset.Y - origin.Y;
|
||||||
subpgm.Offset = new Geometry.Vector(
|
subpgm.Offset = new Geometry.Vector(
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using OpenNest.Converters;
|
using OpenNest.Converters;
|
||||||
using OpenNest.Geometry;
|
using OpenNest.Geometry;
|
||||||
|
using OpenNest.Math;
|
||||||
|
|
||||||
namespace OpenNest
|
namespace OpenNest
|
||||||
{
|
{
|
||||||
@@ -123,7 +124,7 @@ namespace OpenNest
|
|||||||
// keeps offsets lying exactly on an axis (mirror-symmetric parts) away from the
|
// keeps offsets lying exactly on an axis (mirror-symmetric parts) away from the
|
||||||
// interval edge so floating-point noise cannot flip the choice.
|
// interval edge so floating-point noise cannot flip the choice.
|
||||||
var halfPi = System.Math.PI / 2.0;
|
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++)
|
for (var turns = 0; turns < 4; turns++)
|
||||||
{
|
{
|
||||||
var relative = direction + turns * halfPi - (System.Math.PI - PreferenceMargin);
|
var relative = direction + turns * halfPi - (System.Math.PI - PreferenceMargin);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using OpenNest.CNC;
|
using OpenNest.CNC;
|
||||||
using OpenNest.Geometry;
|
using OpenNest.Geometry;
|
||||||
|
using OpenNest.Math;
|
||||||
|
|
||||||
namespace OpenNest
|
namespace OpenNest
|
||||||
{
|
{
|
||||||
@@ -238,8 +239,8 @@ namespace OpenNest
|
|||||||
{
|
{
|
||||||
var din = (points[k] - points[k - 1]).Normalize();
|
var din = (points[k] - points[k - 1]).Normalize();
|
||||||
var dout = (points[k + 1] - points[k]).Normalize();
|
var dout = (points[k + 1] - points[k]).Normalize();
|
||||||
var turn = System.Math.Atan2(System.Math.Abs(Cross(din, dout)), Dot(din, dout));
|
var turn = PortableMath.Atan2(System.Math.Abs(Cross(din, dout)), Dot(din, dout));
|
||||||
snaps[k] = kerf / 2 * System.Math.Tan(turn / 2) + 2 * CommonLineGeometry.GapTolerance;
|
snaps[k] = kerf / 2 * PortableMath.Tan(turn / 2) + 2 * CommonLineGeometry.GapTolerance;
|
||||||
}
|
}
|
||||||
for (var k = 0; k + 1 < points.Length; k++)
|
for (var k = 0; k + 1 < points.Length; k++)
|
||||||
yield return new Leg(points[k], points[k + 1], snaps[k], snaps[k + 1]);
|
yield return new Leg(points[k], points[k + 1], snaps[k], snaps[k + 1]);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using System.Linq;
|
|||||||
using OpenNest.CNC;
|
using OpenNest.CNC;
|
||||||
using OpenNest.Diagnostics;
|
using OpenNest.Diagnostics;
|
||||||
using OpenNest.Geometry;
|
using OpenNest.Geometry;
|
||||||
|
using OpenNest.Math;
|
||||||
|
|
||||||
namespace OpenNest
|
namespace OpenNest
|
||||||
{
|
{
|
||||||
@@ -161,7 +162,7 @@ namespace OpenNest
|
|||||||
Vector offset, double kerf, double maxGap)
|
Vector offset, double kerf, double maxGap)
|
||||||
{
|
{
|
||||||
var along = chosen.First.Direction;
|
var along = chosen.First.Direction;
|
||||||
var minimum = System.Math.Sin(MinCornerTurn);
|
var minimum = PortableMath.Sin(MinCornerTurn);
|
||||||
var shifted = Shift(moving, offset);
|
var shifted = Shift(moving, offset);
|
||||||
var slides = FacingEdges(anchor, shifted, -maxGap, maxGap)
|
var slides = FacingEdges(anchor, shifted, -maxGap, maxGap)
|
||||||
.Select(facing => (Facing: facing, Across: Dot(along, facing.First.Outward)))
|
.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 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 dout = new Vector((bOther.X - bEnd.X) / bLength, (bOther.Y - bEnd.Y) / bLength);
|
||||||
var cross = Cross(din, dout);
|
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)
|
if (turn < MinCornerTurn || turn > MaxCornerTurn)
|
||||||
return null;
|
return null;
|
||||||
// aEnd + din·s = bEnd − dout·t: the corner lies s past a's end and t before b's end.
|
// 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 gap = bEnd - aEnd;
|
||||||
var s = Cross(gap, dout) / cross;
|
var s = Cross(gap, dout) / cross;
|
||||||
var t = Cross(din, gap) / 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)
|
if (s < -GapTolerance || t < -GapTolerance || s > reach || t > reach)
|
||||||
return null;
|
return null;
|
||||||
return new Vector(aEnd.X + din.X * s, aEnd.Y + din.Y * s);
|
return new Vector(aEnd.X + din.X * s, aEnd.Y + din.Y * s);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using OpenNest.Geometry;
|
using OpenNest.Geometry;
|
||||||
|
using OpenNest.Math;
|
||||||
|
|
||||||
namespace OpenNest.Diagnostics;
|
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));
|
System.Math.Max(Start.X, End.X), System.Math.Max(Start.Y, End.Y));
|
||||||
|
|
||||||
internal Vector Midpoint => Center is { } center
|
internal Vector Midpoint => Center is { } center
|
||||||
? new Vector(center.X + Radius * System.Math.Cos(StartAngle + Sweep / 2),
|
? new Vector(center.X + Radius * PortableMath.Cos(StartAngle + Sweep / 2),
|
||||||
center.Y + Radius * System.Math.Sin(StartAngle + Sweep / 2))
|
center.Y + Radius * PortableMath.Sin(StartAngle + Sweep / 2))
|
||||||
: (Start + End) * 0.5;
|
: (Start + End) * 0.5;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -287,7 +288,7 @@ internal static class PostVerificationGeometry
|
|||||||
|
|
||||||
internal double DistanceAlong(Vector point) => Center is { } center
|
internal double DistanceAlong(Vector point) => Center is { } center
|
||||||
? point.DistanceTo(Start) <= Epsilon ? 0
|
? 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));
|
: Dot(point - Start, (End - Start) * (1 / Length));
|
||||||
|
|
||||||
/// <summary>Unit direction of travel at the curve's start or end.</summary>
|
/// <summary>Unit direction of travel at the curve's start or end.</summary>
|
||||||
@@ -314,8 +315,8 @@ internal static class PostVerificationGeometry
|
|||||||
if (distance <= 0) return Start;
|
if (distance <= 0) return Start;
|
||||||
if (distance >= Length) return End;
|
if (distance >= Length) return End;
|
||||||
return Center is { } center
|
return Center is { } center
|
||||||
? center + new Vector(System.Math.Cos(StartAngle + System.Math.Sign(Sweep) * distance / Radius),
|
? center + new Vector(PortableMath.Cos(StartAngle + System.Math.Sign(Sweep) * distance / Radius),
|
||||||
System.Math.Sin(StartAngle + System.Math.Sign(Sweep) * distance / Radius)) * Radius
|
PortableMath.Sin(StartAngle + System.Math.Sign(Sweep) * distance / Radius)) * Radius
|
||||||
: Start + (End - Start) * (distance / Length);
|
: Start + (End - Start) * (distance / Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -331,7 +332,7 @@ internal static class PostVerificationGeometry
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal Curve Until(Vector point) => Center is { } center
|
internal Curve Until(Vector point) => Center is { } center
|
||||||
? new Curve(Start, point, center, Radius,
|
? 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);
|
: new Curve(Start, point, null, 0, 0);
|
||||||
|
|
||||||
internal bool Contains(Vector point) => Contains(point, Epsilon);
|
internal bool Contains(Vector point) => Contains(point, Epsilon);
|
||||||
@@ -524,16 +525,16 @@ internal static class PostVerificationGeometry
|
|||||||
if (radius <= Epsilon || !double.IsFinite(radius)
|
if (radius <= Epsilon || !double.IsFinite(radius)
|
||||||
|| System.Math.Abs(radius - end.DistanceTo(c)) > Epsilon * System.Math.Max(1, radius))
|
|| System.Math.Abs(radius - end.DistanceTo(c)) > Epsilon * System.Math.Max(1, radius))
|
||||||
throw new ArgumentException("Arc has zero or inconsistent radius.");
|
throw new ArgumentException("Arc has zero or inconsistent radius.");
|
||||||
var a = System.Math.Atan2(start.Y - c.Y, start.X - c.X);
|
var a = PortableMath.Atan2(start.Y - c.Y, start.X - c.X);
|
||||||
var b = System.Math.Atan2(end.Y - c.Y, end.X - c.X);
|
var b = PortableMath.Atan2(end.Y - c.Y, end.X - c.X);
|
||||||
var sweep = start.DistanceTo(end) <= Epsilon ? TwoPi
|
var sweep = start.DistanceTo(end) <= Epsilon ? TwoPi
|
||||||
: Normalize(clockwise ? a - b : b - a);
|
: Normalize(clockwise ? a - b : b - a);
|
||||||
return new Curve(start, end, c, radius, clockwise ? -sweep : sweep);
|
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 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.X - Center.Value.X)) <= System.Math.Abs(Sweep) + Epsilon / Radius
|
||||||
|| point.DistanceTo(Start) <= Epsilon || point.DistanceTo(End) <= Epsilon;
|
|| 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 a = StartAngle + System.Math.Sign(Sweep) * breaks[i - 1];
|
||||||
var b = StartAngle + System.Math.Sign(Sweep) * breaks[i];
|
var b = StartAngle + System.Math.Sign(Sweep) * breaks[i];
|
||||||
var ya = i == 1 ? Start.Y : center.Y + Radius * System.Math.Sin(a);
|
var ya = i == 1 ? Start.Y : center.Y + Radius * PortableMath.Sin(a);
|
||||||
var yb = i == breaks.Count - 1 ? End.Y : center.Y + Radius * System.Math.Sin(b);
|
var yb = i == breaks.Count - 1 ? End.Y : center.Y + Radius * PortableMath.Sin(b);
|
||||||
if ((ya > rayY) == (yb > rayY))
|
if ((ya > rayY) == (yb > rayY))
|
||||||
continue;
|
continue;
|
||||||
var dy = point.Y - center.Y;
|
var dy = point.Y - center.Y;
|
||||||
var dx = System.Math.Sqrt(System.Math.Max(0, Radius * Radius - dy * dy));
|
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)
|
if (x > point.X)
|
||||||
inside = !inside;
|
inside = !inside;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -189,7 +189,7 @@ namespace OpenNest.Dimensions
|
|||||||
var angle = arc != null && kind == DimensionKind.Radius
|
var angle = arc != null && kind == DimensionKind.Radius
|
||||||
? ClampToSweep(arc, dimension.Angle)
|
? ClampToSweep(arc, dimension.Angle)
|
||||||
: 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 arrow = center + direction * radius;
|
||||||
var far = center - direction * radius;
|
var far = center - direction * radius;
|
||||||
return new DimensionLayout
|
return new DimensionLayout
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using OpenNest.Geometry;
|
using OpenNest.Geometry;
|
||||||
|
using OpenNest.Math;
|
||||||
|
|
||||||
namespace OpenNest.Dimensions
|
namespace OpenNest.Dimensions
|
||||||
{
|
{
|
||||||
@@ -80,7 +81,7 @@ namespace OpenNest.Dimensions
|
|||||||
var dx = point.X - Center.X;
|
var dx = point.X - Center.X;
|
||||||
var dy = point.Y - Center.Y;
|
var dy = point.Y - Center.Y;
|
||||||
var distance = System.Math.Sqrt(dx * dx + dy * dy);
|
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)
|
if (Arc != null && Kind == DimensionKind.Radius)
|
||||||
angle = DimensionBuilder.ClampToSweep(Arc, angle);
|
angle = DimensionBuilder.ClampToSweep(Arc, angle);
|
||||||
|
|
||||||
|
|||||||
@@ -141,8 +141,8 @@ namespace OpenNest.Geometry
|
|||||||
public Vector StartPoint()
|
public Vector StartPoint()
|
||||||
{
|
{
|
||||||
return new Vector(
|
return new Vector(
|
||||||
Center.X + Radius * System.Math.Cos(StartAngle),
|
Center.X + Radius * PortableMath.Cos(StartAngle),
|
||||||
Center.Y + Radius * System.Math.Sin(StartAngle)
|
Center.Y + Radius * PortableMath.Sin(StartAngle)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,8 +153,8 @@ namespace OpenNest.Geometry
|
|||||||
public Vector EndPoint()
|
public Vector EndPoint()
|
||||||
{
|
{
|
||||||
return new Vector(
|
return new Vector(
|
||||||
Center.X + Radius * System.Math.Cos(EndAngle),
|
Center.X + Radius * PortableMath.Cos(EndAngle),
|
||||||
Center.Y + Radius * System.Math.Sin(EndAngle)
|
Center.Y + Radius * PortableMath.Sin(EndAngle)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,8 +165,8 @@ namespace OpenNest.Geometry
|
|||||||
{
|
{
|
||||||
var midAngle = StartAngle + (IsReversed ? -SweepAngle() / 2 : SweepAngle() / 2);
|
var midAngle = StartAngle + (IsReversed ? -SweepAngle() / 2 : SweepAngle() / 2);
|
||||||
return new Vector(
|
return new Vector(
|
||||||
Center.X + Radius * System.Math.Cos(midAngle),
|
Center.X + Radius * PortableMath.Cos(midAngle),
|
||||||
Center.Y + Radius * System.Math.Sin(midAngle)
|
Center.Y + Radius * PortableMath.Sin(midAngle)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,7 +231,7 @@ namespace OpenNest.Geometry
|
|||||||
if (tolerance >= Radius)
|
if (tolerance >= Radius)
|
||||||
return 1;
|
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(
|
return System.Math.Max(
|
||||||
1,
|
1,
|
||||||
(int)System.Math.Ceiling(System.Math.Abs(SweepAngle()) / maxAngle)
|
(int)System.Math.Ceiling(System.Math.Abs(SweepAngle()) / maxAngle)
|
||||||
@@ -250,7 +250,7 @@ namespace OpenNest.Geometry
|
|||||||
|
|
||||||
var r =
|
var r =
|
||||||
circumscribe && segments > 0
|
circumscribe && segments > 0
|
||||||
? Radius / System.Math.Cos(System.Math.Abs(stepAngle) / 2.0)
|
? Radius / PortableMath.Cos(System.Math.Abs(stepAngle) / 2.0)
|
||||||
: Radius;
|
: Radius;
|
||||||
|
|
||||||
for (int i = 0; i <= segments; ++i)
|
for (int i = 0; i <= segments; ++i)
|
||||||
@@ -259,8 +259,8 @@ namespace OpenNest.Geometry
|
|||||||
|
|
||||||
points.Add(
|
points.Add(
|
||||||
new Vector(
|
new Vector(
|
||||||
System.Math.Cos(angle) * r + Center.X,
|
PortableMath.Cos(angle) * r + Center.X,
|
||||||
System.Math.Sin(angle) * r + Center.Y
|
PortableMath.Sin(angle) * r + Center.Y
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -479,8 +479,8 @@ namespace OpenNest.Geometry
|
|||||||
if (Angle.IsBetweenRad(angle, StartAngle, EndAngle, IsReversed))
|
if (Angle.IsBetweenRad(angle, StartAngle, EndAngle, IsReversed))
|
||||||
{
|
{
|
||||||
return new Vector(
|
return new Vector(
|
||||||
System.Math.Cos(angle) * Radius + Center.X,
|
PortableMath.Cos(angle) * Radius + Center.X,
|
||||||
System.Math.Sin(angle) * Radius + Center.Y
|
PortableMath.Sin(angle) * Radius + Center.Y
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -102,8 +102,8 @@ namespace OpenNest.Geometry
|
|||||||
return (Vector.Invalid, 0, double.MaxValue);
|
return (Vector.Invalid, 0, double.MaxValue);
|
||||||
|
|
||||||
var halfChord = chordLen / 2;
|
var halfChord = chordLen / 2;
|
||||||
var radius = halfChord / System.Math.Abs(System.Math.Sin(theta));
|
var radius = halfChord / System.Math.Abs(PortableMath.Sin(theta));
|
||||||
var d = -halfChord / System.Math.Tan(theta);
|
var d = -halfChord / PortableMath.Tan(theta);
|
||||||
|
|
||||||
var cx = (p1.X + pn.X) / 2 + d * -uy;
|
var cx = (p1.X + pn.X) / 2 + d * -uy;
|
||||||
var cy = (p1.Y + pn.Y) / 2 + d * ux;
|
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);
|
var len = System.Math.Sqrt(to.X * to.X + to.Y * to.Y);
|
||||||
if (len < 1e-10)
|
if (len < 1e-10)
|
||||||
return 0;
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -128,8 +128,8 @@ namespace OpenNest.Geometry
|
|||||||
var total = 0.0;
|
var total = 0.0;
|
||||||
for (var i = 0; i < points.Count - 1; i++)
|
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 a1 = PortableMath.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 a2 = PortableMath.Atan2(points[i + 1].Y - center.Y, points[i + 1].X - center.X);
|
||||||
var da = a2 - a1;
|
var da = a2 - a1;
|
||||||
while (da > System.Math.PI)
|
while (da > System.Math.PI)
|
||||||
da -= Angle.TwoPI;
|
da -= Angle.TwoPI;
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ namespace OpenNest.Geometry
|
|||||||
if (tolerance >= Radius)
|
if (tolerance >= Radius)
|
||||||
return 3;
|
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));
|
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;
|
Rotation == RotationType.CW ? -Angle.TwoPI / segments : Angle.TwoPI / segments;
|
||||||
|
|
||||||
var r =
|
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)
|
for (int i = 0; i <= segments; ++i)
|
||||||
{
|
{
|
||||||
@@ -145,8 +145,8 @@ namespace OpenNest.Geometry
|
|||||||
|
|
||||||
points.Add(
|
points.Add(
|
||||||
new Vector(
|
new Vector(
|
||||||
System.Math.Cos(angle) * r + Center.X,
|
PortableMath.Cos(angle) * r + Center.X,
|
||||||
System.Math.Sin(angle) * r + Center.Y
|
PortableMath.Sin(angle) * r + Center.Y
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -312,8 +312,8 @@ namespace OpenNest.Geometry
|
|||||||
var angle = Center.AngleTo(pt);
|
var angle = Center.AngleTo(pt);
|
||||||
|
|
||||||
return new Vector(
|
return new Vector(
|
||||||
System.Math.Cos(angle) * Radius + Center.X,
|
PortableMath.Cos(angle) * Radius + Center.X,
|
||||||
System.Math.Sin(angle) * Radius + Center.Y
|
PortableMath.Sin(angle) * Radius + Center.Y
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -299,7 +299,7 @@ namespace OpenNest.Geometry
|
|||||||
var sweep = arc.SweepAngle();
|
var sweep = arc.SweepAngle();
|
||||||
var segments = CircumscribedSegments(arc.Radius, sweep, tolerance);
|
var segments = CircumscribedSegments(arc.Radius, sweep, tolerance);
|
||||||
var step = (arc.IsReversed ? -sweep : sweep) / segments;
|
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());
|
points.Add(arc.StartPoint());
|
||||||
|
|
||||||
@@ -308,8 +308,8 @@ namespace OpenNest.Geometry
|
|||||||
var angle = arc.StartAngle + step * (i + 0.5);
|
var angle = arc.StartAngle + step * (i + 0.5);
|
||||||
points.Add(
|
points.Add(
|
||||||
new Vector(
|
new Vector(
|
||||||
arc.Center.X + r * System.Math.Cos(angle),
|
arc.Center.X + r * PortableMath.Cos(angle),
|
||||||
arc.Center.Y + r * System.Math.Sin(angle)
|
arc.Center.Y + r * PortableMath.Sin(angle)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -332,14 +332,14 @@ namespace OpenNest.Geometry
|
|||||||
|
|
||||||
var segments = CircumscribedSegments(circle.Radius, Angle.TwoPI, tolerance);
|
var segments = CircumscribedSegments(circle.Radius, Angle.TwoPI, tolerance);
|
||||||
var step = Angle.TwoPI / segments;
|
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++)
|
for (var i = 0; i < segments; i++)
|
||||||
{
|
{
|
||||||
points.Add(
|
points.Add(
|
||||||
new Vector(
|
new Vector(
|
||||||
circle.Center.X + r * System.Math.Cos(step * i),
|
circle.Center.X + r * PortableMath.Cos(step * i),
|
||||||
circle.Center.Y + r * System.Math.Sin(step * i)
|
circle.Center.Y + r * PortableMath.Sin(step * i)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -352,7 +352,7 @@ namespace OpenNest.Geometry
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private static int CircumscribedSegments(double radius, double sweep, double tolerance)
|
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 segments = (int)System.Math.Ceiling(System.Math.Abs(sweep) / (2 * maxHalfStep));
|
||||||
var quarters = (int)System.Math.Ceiling(System.Math.Abs(sweep) / Angle.HalfPI);
|
var quarters = (int)System.Math.Ceiling(System.Math.Abs(sweep) / Angle.HalfPI);
|
||||||
|
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ namespace OpenNest.Geometry
|
|||||||
|
|
||||||
var dirX = dx / length;
|
var dirX = dx / length;
|
||||||
var dirY = dy / 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 count = System.Math.Max(1, (int)(length / spacing));
|
||||||
var step = length / (count + 1);
|
var step = length / (count + 1);
|
||||||
@@ -114,8 +114,8 @@ namespace OpenNest.Geometry
|
|||||||
if (radius < Tolerance.Epsilon)
|
if (radius < Tolerance.Epsilon)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var startAngle = System.Math.Atan2(start.Y - center.Y, start.X - center.X);
|
var startAngle = PortableMath.Atan2(start.Y - center.Y, start.X - center.X);
|
||||||
var endAngle = System.Math.Atan2(end.Y - center.Y, end.X - center.X);
|
var endAngle = PortableMath.Atan2(end.Y - center.Y, end.X - center.X);
|
||||||
|
|
||||||
double sweep;
|
double sweep;
|
||||||
if (rotation == RotationType.CCW)
|
if (rotation == RotationType.CCW)
|
||||||
@@ -147,8 +147,8 @@ namespace OpenNest.Geometry
|
|||||||
angle = startAngle - stepAngle * i;
|
angle = startAngle - stepAngle * i;
|
||||||
|
|
||||||
var pt = new Vector(
|
var pt = new Vector(
|
||||||
center.X + radius * System.Math.Cos(angle),
|
center.X + radius * PortableMath.Cos(angle),
|
||||||
center.Y + radius * System.Math.Sin(angle)
|
center.Y + radius * PortableMath.Sin(angle)
|
||||||
);
|
);
|
||||||
|
|
||||||
double tangent;
|
double tangent;
|
||||||
@@ -157,7 +157,7 @@ namespace OpenNest.Geometry
|
|||||||
else
|
else
|
||||||
tangent = angle - System.Math.PI / 2;
|
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);
|
var at = radius * System.Math.Abs(stepAngle * i);
|
||||||
output.Add(new ContourSample(pt, dir, tangent, at));
|
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 local = segLen > 0 ? (s - segStart) / segLen : 0.0;
|
||||||
var dir = SegmentDirection(a, b);
|
var dir = SegmentDirection(a, b);
|
||||||
var pt = new Vector(a.X + (b.X - a.X) * local, a.Y + (b.Y - a.Y) * local);
|
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
|
// Same sweep convention as ArcMoves: always in (0, 2*PI], so a full
|
||||||
// circle counts its whole circumference toward the walk's arclength.
|
// circle counts its whole circumference toward the walk's arclength.
|
||||||
var startAngle = System.Math.Atan2(start.Y - center.Y, start.X - center.X);
|
var startAngle = PortableMath.Atan2(start.Y - center.Y, start.X - center.X);
|
||||||
var endAngle = System.Math.Atan2(end.Y - center.Y, end.X - center.X);
|
var endAngle = PortableMath.Atan2(end.Y - center.Y, end.X - center.X);
|
||||||
var sweep =
|
var sweep =
|
||||||
rotation == RotationType.CCW ? endAngle - startAngle : startAngle - endAngle;
|
rotation == RotationType.CCW ? endAngle - startAngle : startAngle - endAngle;
|
||||||
if (sweep <= 0)
|
if (sweep <= 0)
|
||||||
|
|||||||
@@ -786,7 +786,7 @@ namespace OpenNest.Geometry
|
|||||||
if (!(covXX > 0) || double.IsNaN(covXY) || double.IsNaN(covXX))
|
if (!(covXX > 0) || double.IsNaN(covXY) || double.IsNaN(covXX))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
dTheta = System.Math.Atan2(covXY, covXX);
|
dTheta = PortableMath.Atan2(covXY, covXX);
|
||||||
if (double.IsNaN(dTheta) || double.IsInfinity(dTheta))
|
if (double.IsNaN(dTheta) || double.IsInfinity(dTheta))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|||||||
@@ -17,11 +17,11 @@ namespace OpenNest.Geometry
|
|||||||
double t
|
double t
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
var x = semiMajor * System.Math.Cos(t);
|
var x = semiMajor * PortableMath.Cos(t);
|
||||||
var y = semiMinor * System.Math.Sin(t);
|
var y = semiMinor * PortableMath.Sin(t);
|
||||||
|
|
||||||
var cos = System.Math.Cos(rotation);
|
var cos = PortableMath.Cos(rotation);
|
||||||
var sin = System.Math.Sin(rotation);
|
var sin = PortableMath.Sin(rotation);
|
||||||
|
|
||||||
return new Vector(center.X + x * cos - y * sin, center.Y + x * sin + y * cos);
|
return new Vector(center.X + x * cos - y * sin, center.Y + x * sin + y * cos);
|
||||||
}
|
}
|
||||||
@@ -33,11 +33,11 @@ namespace OpenNest.Geometry
|
|||||||
double t
|
double t
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
var tx = -semiMajor * System.Math.Sin(t);
|
var tx = -semiMajor * PortableMath.Sin(t);
|
||||||
var ty = semiMinor * System.Math.Cos(t);
|
var ty = semiMinor * PortableMath.Cos(t);
|
||||||
|
|
||||||
var cos = System.Math.Cos(rotation);
|
var cos = PortableMath.Cos(rotation);
|
||||||
var sin = System.Math.Sin(rotation);
|
var sin = PortableMath.Sin(rotation);
|
||||||
|
|
||||||
return new Vector(tx * cos - ty * sin, tx * sin + ty * cos);
|
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.
|
// Inward normal: perpendicular to tangent, pointing toward center of curvature.
|
||||||
// In local coords: N(t) = (-b*cos(t), -a*sin(t))
|
// In local coords: N(t) = (-b*cos(t), -a*sin(t))
|
||||||
var nx = -semiMinor * System.Math.Cos(t);
|
var nx = -semiMinor * PortableMath.Cos(t);
|
||||||
var ny = -semiMajor * System.Math.Sin(t);
|
var ny = -semiMajor * PortableMath.Sin(t);
|
||||||
|
|
||||||
var cos = System.Math.Cos(rotation);
|
var cos = PortableMath.Cos(rotation);
|
||||||
var sin = System.Math.Sin(rotation);
|
var sin = PortableMath.Sin(rotation);
|
||||||
|
|
||||||
return new Vector(nx * cos - ny * sin, nx * sin + ny * cos);
|
return new Vector(nx * cos - ny * sin, nx * sin + ny * cos);
|
||||||
}
|
}
|
||||||
@@ -326,8 +326,8 @@ namespace OpenNest.Geometry
|
|||||||
radius = p0.DistanceTo(cc);
|
radius = p0.DistanceTo(cc);
|
||||||
}
|
}
|
||||||
|
|
||||||
var startAngle = System.Math.Atan2(p0.Y - arcCenter.Y, p0.X - arcCenter.X);
|
var startAngle = PortableMath.Atan2(p0.Y - arcCenter.Y, p0.X - arcCenter.X);
|
||||||
var endAngle = System.Math.Atan2(p1.Y - arcCenter.Y, p1.X - arcCenter.X);
|
var endAngle = PortableMath.Atan2(p1.Y - arcCenter.Y, p1.X - arcCenter.X);
|
||||||
|
|
||||||
var points = new List<Vector> { p0, pMid, p1 };
|
var points = new List<Vector> { p0, pMid, p1 };
|
||||||
var isReversed = ArcFit.SumSignedAngles(arcCenter, points) < 0;
|
var isReversed = ArcFit.SumSignedAngles(arcCenter, points) < 0;
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ namespace OpenNest.Geometry
|
|||||||
System.Math.Abs(point.DistanceTo(center) - circle.Radius) <= Tolerance.ChainTolerance;
|
System.Math.Abs(point.DistanceTo(center) - circle.Radius) <= Tolerance.ChainTolerance;
|
||||||
bool OnOverlap(Vector point)
|
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 =>
|
return spans.Count(s =>
|
||||||
{
|
{
|
||||||
var offset = Angle.NormalizeRad(angle - s.Start);
|
var offset = Angle.NormalizeRad(angle - s.Start);
|
||||||
|
|||||||
@@ -333,10 +333,10 @@ public class GeometrySimplifier
|
|||||||
|
|
||||||
// Mirroring reverses winding — swap start/end to preserve arc direction
|
// Mirroring reverses winding — swap start/end to preserve arc direction
|
||||||
var mirrorStart = NormalizeAngle(
|
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(
|
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);
|
var result = new Arc(mirrorCenter, arc.Radius, mirrorStart, mirrorEnd, arc.IsReversed);
|
||||||
@@ -653,8 +653,8 @@ public class GeometrySimplifier
|
|||||||
/// <summary>The direction of travel along an arc at <paramref name="angle"/>.</summary>
|
/// <summary>The direction of travel along an arc at <paramref name="angle"/>.</summary>
|
||||||
private static Vector ArcTangent(Arc arc, double angle) =>
|
private static Vector ArcTangent(Arc arc, double angle) =>
|
||||||
arc.IsReversed
|
arc.IsReversed
|
||||||
? new Vector(System.Math.Sin(angle), -System.Math.Cos(angle))
|
? new Vector(PortableMath.Sin(angle), -PortableMath.Cos(angle))
|
||||||
: new Vector(-System.Math.Sin(angle), System.Math.Cos(angle));
|
: new Vector(-PortableMath.Sin(angle), PortableMath.Cos(angle));
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Computes the tangent direction at the last point of a fitted arc,
|
/// Computes the tangent direction at the last point of a fitted arc,
|
||||||
@@ -779,10 +779,10 @@ public class GeometrySimplifier
|
|||||||
var lastPoint = points[^1];
|
var lastPoint = points[^1];
|
||||||
|
|
||||||
var startAngle = NormalizeAngle(
|
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(
|
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;
|
var isReversed = ArcFit.SumSignedAngles(center, points) < 0;
|
||||||
|
|
||||||
@@ -815,8 +815,8 @@ public class GeometrySimplifier
|
|||||||
bool isReversed
|
bool isReversed
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
var startAngle = System.Math.Atan2(points[0].Y - center.Y, points[0].X - center.X);
|
var startAngle = PortableMath.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 endAngle = PortableMath.Atan2(points[^1].Y - center.Y, points[^1].X - center.X);
|
||||||
|
|
||||||
var sweep = endAngle - startAngle;
|
var sweep = endAngle - startAngle;
|
||||||
if (isReversed)
|
if (isReversed)
|
||||||
@@ -838,8 +838,8 @@ public class GeometrySimplifier
|
|||||||
{
|
{
|
||||||
var t = (double)i / sampleCount;
|
var t = (double)i / sampleCount;
|
||||||
var angle = startAngle + sweep * t;
|
var angle = startAngle + sweep * t;
|
||||||
var px = center.X + radius * System.Math.Cos(angle);
|
var px = center.X + radius * PortableMath.Cos(angle);
|
||||||
var py = center.Y + radius * System.Math.Sin(angle);
|
var py = center.Y + radius * PortableMath.Sin(angle);
|
||||||
var arcPt = new Vector(px, py);
|
var arcPt = new Vector(px, py);
|
||||||
|
|
||||||
var minDist = double.MaxValue;
|
var minDist = double.MaxValue;
|
||||||
|
|||||||
@@ -214,7 +214,7 @@ namespace OpenNest.Geometry
|
|||||||
{
|
{
|
||||||
var m1 = Slope();
|
var m1 = Slope();
|
||||||
var m2 = line.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)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -395,8 +395,8 @@ namespace OpenNest.Geometry
|
|||||||
{
|
{
|
||||||
var angle = OpenNest.Math.Angle.NormalizeRad(Angle() + OpenNest.Math.Angle.HalfPI);
|
var angle = OpenNest.Math.Angle.NormalizeRad(Angle() + OpenNest.Math.Angle.HalfPI);
|
||||||
|
|
||||||
var x = System.Math.Cos(angle) * distance;
|
var x = PortableMath.Cos(angle) * distance;
|
||||||
var y = System.Math.Sin(angle) * distance;
|
var y = PortableMath.Sin(angle) * distance;
|
||||||
|
|
||||||
var pt = side == OffsetSide.Left ? new Vector(x, y) : new Vector(-x, -y);
|
var pt = side == OffsetSide.Left ? new Vector(x, y) : new Vector(-x, -y);
|
||||||
|
|
||||||
|
|||||||
@@ -146,11 +146,11 @@ namespace OpenNest.Geometry
|
|||||||
}
|
}
|
||||||
else
|
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)
|
if (angleA < 0)
|
||||||
angleA += Angle.TwoPI;
|
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)
|
if (angleB < 0)
|
||||||
angleB += Angle.TwoPI;
|
angleB += Angle.TwoPI;
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ namespace OpenNest.Geometry
|
|||||||
{
|
{
|
||||||
var dx = vertices[1].X - vertices[0].X;
|
var dx = vertices[1].X - vertices[0].X;
|
||||||
var dy = vertices[1].Y - vertices[0].Y;
|
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);
|
var length = System.Math.Sqrt(dx * dx + dy * dy);
|
||||||
return new BoundingRectangleResult(angle, length, 0);
|
return new BoundingRectangleResult(angle, length, 0);
|
||||||
}
|
}
|
||||||
@@ -100,7 +100,7 @@ namespace OpenNest.Geometry
|
|||||||
|
|
||||||
if (best == null || area < best.Area - margin)
|
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);
|
best = new BoundingRectangleResult(edgeAngle, width, height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -137,7 +137,7 @@ namespace OpenNest.Geometry
|
|||||||
if (edgeLen.IsEqualTo(0))
|
if (edgeLen.IsEqualTo(0))
|
||||||
continue;
|
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)
|
// Each edge produces 4 candidate angles (0, 90, 180, 270 relative to edge)
|
||||||
for (int k = 0; k < 4; k++)
|
for (int k = 0; k < 4; k++)
|
||||||
@@ -172,8 +172,8 @@ namespace OpenNest.Geometry
|
|||||||
double angle
|
double angle
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
var cos = System.Math.Cos(angle);
|
var cos = PortableMath.Cos(angle);
|
||||||
var sin = System.Math.Sin(angle);
|
var sin = PortableMath.Sin(angle);
|
||||||
|
|
||||||
double minU = double.MaxValue,
|
double minU = double.MaxValue,
|
||||||
maxU = double.MinValue;
|
maxU = double.MinValue;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using OpenNest.Math;
|
||||||
|
|
||||||
namespace OpenNest.Geometry
|
namespace OpenNest.Geometry
|
||||||
{
|
{
|
||||||
@@ -661,8 +662,8 @@ namespace OpenNest.Geometry
|
|||||||
|
|
||||||
private static Vector ArcTangent(Arc arc, double angle)
|
private static Vector ArcTangent(Arc arc, double angle)
|
||||||
{
|
{
|
||||||
var sin = System.Math.Sin(angle);
|
var sin = PortableMath.Sin(angle);
|
||||||
var cos = System.Math.Cos(angle);
|
var cos = PortableMath.Cos(angle);
|
||||||
return arc.IsReversed ? new Vector(sin, -cos) : new Vector(-sin, cos);
|
return arc.IsReversed ? new Vector(sin, -cos) : new Vector(-sin, cos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -176,7 +176,7 @@ namespace OpenNest.Geometry
|
|||||||
var arc = (Arc)entity;
|
var arc = (Arc)entity;
|
||||||
var deltaX = weldedPoint.X - arc.Center.X;
|
var deltaX = weldedPoint.X - arc.Center.X;
|
||||||
var deltaY = weldedPoint.Y - arc.Center.Y;
|
var deltaY = weldedPoint.Y - arc.Center.Y;
|
||||||
var angle = System.Math.Atan2(deltaY, deltaX);
|
var angle = PortableMath.Atan2(deltaY, deltaX);
|
||||||
|
|
||||||
if (isStart)
|
if (isStart)
|
||||||
arc.StartAngle = angle;
|
arc.StartAngle = angle;
|
||||||
|
|||||||
@@ -237,8 +237,8 @@ namespace OpenNest.Geometry
|
|||||||
if (IsZero(inTangent) || IsZero(outTangent))
|
if (IsZero(inTangent) || IsZero(outTangent))
|
||||||
return ContactSide.Ambiguous;
|
return ContactSide.Ambiguous;
|
||||||
|
|
||||||
var outAngle = System.Math.Atan2(outTangent.Y, outTangent.X);
|
var outAngle = PortableMath.Atan2(outTangent.Y, outTangent.X);
|
||||||
var inAngle = System.Math.Atan2(-inTangent.Y, -inTangent.X);
|
var inAngle = PortableMath.Atan2(-inTangent.Y, -inTangent.X);
|
||||||
var left = materialLeft[loopOf[best]];
|
var left = materialLeft[loopOf[best]];
|
||||||
|
|
||||||
start = left ? outAngle : inAngle;
|
start = left ? outAngle : inAngle;
|
||||||
@@ -326,7 +326,7 @@ namespace OpenNest.Geometry
|
|||||||
private static Vector ArcTangent(double angle, bool clockwise)
|
private static Vector ArcTangent(double angle, bool clockwise)
|
||||||
{
|
{
|
||||||
var sign = clockwise ? -1.0 : 1.0;
|
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)
|
private static Vector Direction(Vector from, Vector to)
|
||||||
@@ -380,7 +380,7 @@ namespace OpenNest.Geometry
|
|||||||
var sweep = arc.IsReversed ? -arc.SweepAngle() : arc.SweepAngle();
|
var sweep = arc.IsReversed ? -arc.SweepAngle() : arc.SweepAngle();
|
||||||
var r = arc.Radius;
|
var r = arc.Radius;
|
||||||
area += Cross(arc.StartPoint(), arc.EndPoint()) / 2;
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -432,13 +432,13 @@ namespace OpenNest.Geometry
|
|||||||
{
|
{
|
||||||
var a = arc.StartAngle + sign * cuts[k - 1];
|
var a = arc.StartAngle + sign * cuts[k - 1];
|
||||||
var b = arc.StartAngle + sign * cuts[k];
|
var b = arc.StartAngle + sign * cuts[k];
|
||||||
var y1 = arc.Center.Y + arc.Radius * System.Math.Sin(a);
|
var y1 = arc.Center.Y + arc.Radius * PortableMath.Sin(a);
|
||||||
var y2 = arc.Center.Y + arc.Radius * System.Math.Sin(b);
|
var y2 = arc.Center.Y + arc.Radius * PortableMath.Sin(b);
|
||||||
if ((y1 > point.Y) == (y2 > point.Y))
|
if ((y1 > point.Y) == (y2 > point.Y))
|
||||||
continue;
|
continue;
|
||||||
var dy = point.Y - arc.Center.Y;
|
var dy = point.Y - arc.Center.Y;
|
||||||
var dx = System.Math.Sqrt(System.Math.Max(0, arc.Radius * arc.Radius - dy * dy));
|
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)
|
if (point.X < x)
|
||||||
inside = !inside;
|
inside = !inside;
|
||||||
}
|
}
|
||||||
@@ -752,7 +752,7 @@ namespace OpenNest.Geometry
|
|||||||
if (stationarySide == ContactSide.Ambiguous || movingSide == ContactSide.Ambiguous)
|
if (stationarySide == ContactSide.Ambiguous || movingSide == ContactSide.Ambiguous)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
var direction = System.Math.Atan2(dirY, dirX);
|
var direction = PortableMath.Atan2(dirY, dirX);
|
||||||
var stationaryPieces = Split(stationaryStart, stationaryWidth);
|
var stationaryPieces = Split(stationaryStart, stationaryWidth);
|
||||||
var movingPieces = Split(movingStart + System.Math.PI, movingWidth);
|
var movingPieces = Split(movingStart + System.Math.PI, movingWidth);
|
||||||
var onBoundary = false;
|
var onBoundary = false;
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ namespace OpenNest.Geometry
|
|||||||
// A zero-radius curve is a point: its angular range has no geometric meaning.
|
// A zero-radius curve is a point: its angular range has no geometric meaning.
|
||||||
if (arc == null || radius == 0)
|
if (arc == null || radius == 0)
|
||||||
return true;
|
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);
|
return Angle.IsBetweenRad(angle, arc.StartAngle, arc.EndAngle, arc.IsReversed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ namespace OpenNest.Geometry
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
var hitAngle = Angle.NormalizeRad(
|
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))
|
if (!Angle.IsBetweenRad(hitAngle, arc.StartAngle, arc.EndAngle, arc.IsReversed))
|
||||||
continue;
|
continue;
|
||||||
@@ -281,7 +281,7 @@ namespace OpenNest.Geometry
|
|||||||
// The directional distance from an arc point at angle θ to the
|
// The directional distance from an arc point at angle θ to the
|
||||||
// line is t(θ) = [A + r·(ey·cosθ − ex·sinθ)] / det.
|
// line is t(θ) = [A + r·(ey·cosθ − ex·sinθ)] / det.
|
||||||
// dt/dθ = 0 at θ = atan2(−ex, ey) and θ + π.
|
// 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);
|
var theta2 = Angle.NormalizeRad(theta1 + System.Math.PI);
|
||||||
|
|
||||||
for (var k = 0; k < 2; k++)
|
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))
|
if (arc != null && !Angle.IsBetweenRad(theta, arc.StartAngle, arc.EndAngle, arc.IsReversed))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var qx = cx + r * System.Math.Cos(theta);
|
var qx = cx + r * PortableMath.Cos(theta);
|
||||||
var qy = cy + r * System.Math.Sin(theta);
|
var qy = cy + r * PortableMath.Sin(theta);
|
||||||
|
|
||||||
RayLine(ref sink, qx, qy, p1x, p1y, p2x, p2y, rayX, rayY, arcMoves);
|
RayLine(ref sink, qx, qy, p1x, p1y, p2x, p2y, rayX, rayY, arcMoves);
|
||||||
if (sink.IsDone)
|
if (sink.IsDone)
|
||||||
|
|||||||
@@ -179,7 +179,7 @@ namespace OpenNest.Geometry
|
|||||||
if (t1 > -Tolerance.Epsilon)
|
if (t1 > -Tolerance.Epsilon)
|
||||||
{
|
{
|
||||||
var hitAngle = Angle.NormalizeRad(
|
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))
|
if (Angle.IsBetweenRad(hitAngle, startAngle, endAngle, reversed))
|
||||||
best = t1 > Tolerance.Epsilon ? t1 : 0;
|
best = t1 > Tolerance.Epsilon ? t1 : 0;
|
||||||
@@ -188,7 +188,7 @@ namespace OpenNest.Geometry
|
|||||||
if (t2 > -Tolerance.Epsilon && t2 < best)
|
if (t2 > -Tolerance.Epsilon && t2 < best)
|
||||||
{
|
{
|
||||||
var hitAngle = Angle.NormalizeRad(
|
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))
|
if (Angle.IsBetweenRad(hitAngle, startAngle, endAngle, reversed))
|
||||||
best = t2 > Tolerance.Epsilon ? t2 : 0;
|
best = t2 > Tolerance.Epsilon ? t2 : 0;
|
||||||
|
|||||||
@@ -167,8 +167,8 @@ namespace OpenNest.Geometry
|
|||||||
var firstPoint = points[0];
|
var firstPoint = points[0];
|
||||||
var lastPoint = points[^1];
|
var lastPoint = points[^1];
|
||||||
|
|
||||||
var startAngle = System.Math.Atan2(firstPoint.Y - center.Y, firstPoint.X - center.X);
|
var startAngle = PortableMath.Atan2(firstPoint.Y - center.Y, firstPoint.X - center.X);
|
||||||
var endAngle = System.Math.Atan2(lastPoint.Y - center.Y, lastPoint.X - center.X);
|
var endAngle = PortableMath.Atan2(lastPoint.Y - center.Y, lastPoint.X - center.X);
|
||||||
var isReversed = ArcFit.SumSignedAngles(center, points) < 0;
|
var isReversed = ArcFit.SumSignedAngles(center, points) < 0;
|
||||||
|
|
||||||
if (startAngle < 0)
|
if (startAngle < 0)
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ namespace OpenNest.Geometry
|
|||||||
|
|
||||||
public double Angle()
|
public double Angle()
|
||||||
{
|
{
|
||||||
return OpenNest.Math.Angle.NormalizeRad(System.Math.Atan2(Y, X));
|
return OpenNest.Math.Angle.NormalizeRad(PortableMath.Atan2(Y, X));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -99,7 +99,7 @@ namespace OpenNest.Geometry
|
|||||||
var dot = v1.X * v2.X + v1.Y + v2.Y;
|
var dot = v1.X * v2.X + v1.Y + v2.Y;
|
||||||
var det = 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)
|
public static Vector operator +(Vector pt1, Vector pt2)
|
||||||
@@ -161,8 +161,8 @@ namespace OpenNest.Geometry
|
|||||||
{
|
{
|
||||||
var v = new Vector();
|
var v = new Vector();
|
||||||
|
|
||||||
var cos = System.Math.Cos(angle);
|
var cos = PortableMath.Cos(angle);
|
||||||
var sin = System.Math.Sin(angle);
|
var sin = PortableMath.Sin(angle);
|
||||||
|
|
||||||
v.X = X * cos - Y * sin;
|
v.X = X * cos - Y * sin;
|
||||||
v.Y = X * sin + Y * cos;
|
v.Y = X * sin + Y * cos;
|
||||||
@@ -175,8 +175,8 @@ namespace OpenNest.Geometry
|
|||||||
var v = new Vector();
|
var v = new Vector();
|
||||||
var pt = this - origin;
|
var pt = this - origin;
|
||||||
|
|
||||||
var cos = System.Math.Cos(angle);
|
var cos = PortableMath.Cos(angle);
|
||||||
var sin = System.Math.Sin(angle);
|
var sin = PortableMath.Sin(angle);
|
||||||
|
|
||||||
v.X = pt.X * cos - pt.Y * sin + origin.X;
|
v.X = pt.X * cos - pt.Y * sin + origin.X;
|
||||||
v.Y = pt.X * sin + pt.Y * cos + origin.Y;
|
v.Y = pt.X * sin + pt.Y * cos + origin.Y;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using OpenNest.Geometry;
|
using OpenNest.Geometry;
|
||||||
|
using OpenNest.Math;
|
||||||
|
|
||||||
namespace OpenNest.Shapes
|
namespace OpenNest.Shapes
|
||||||
{
|
{
|
||||||
@@ -27,7 +28,7 @@ namespace OpenNest.Shapes
|
|||||||
{
|
{
|
||||||
var n = Sides < 3 ? 3 : Sides;
|
var n = Sides < 3 ? 3 : Sides;
|
||||||
var center = Width / 2.0;
|
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 step = 2.0 * System.Math.PI / n;
|
||||||
var start = System.Math.PI / n;
|
var start = System.Math.PI / n;
|
||||||
|
|
||||||
@@ -36,8 +37,8 @@ namespace OpenNest.Shapes
|
|||||||
{
|
{
|
||||||
var angle = start + i * step;
|
var angle = start + i * step;
|
||||||
vertices[i] = new Vector(
|
vertices[i] = new Vector(
|
||||||
center + circumRadius * System.Math.Cos(angle),
|
center + circumRadius * PortableMath.Cos(angle),
|
||||||
center + circumRadius * System.Math.Sin(angle)
|
center + circumRadius * PortableMath.Sin(angle)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using OpenNest.Geometry;
|
using OpenNest.Geometry;
|
||||||
|
using OpenNest.Math;
|
||||||
|
|
||||||
namespace OpenNest.Shapes
|
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.Linear(nameof(OD), new Vector(-OD / 2, 0), new Vector(OD / 2, 0), ShapeDimension.Up, "Ø"),
|
||||||
ShapeDimension.CircleLeader(nameof(HolePatternDiameter), Vector.Zero, HolePatternDiameter / 2,
|
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.
|
// 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 step = 2.0 * System.Math.PI / HoleCount;
|
||||||
var angle = System.Math.Round(System.Math.PI / 4 / step) * step;
|
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 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, "Ø")
|
dimensions.Add(ShapeDimension.HoleLeader(nameof(HoleDiameter), center, HoleDiameter / 2, out45, "Ø")
|
||||||
.WithCount(nameof(HoleCount), "holes"));
|
.WithCount(nameof(HoleCount), "holes"));
|
||||||
}
|
}
|
||||||
@@ -76,8 +77,8 @@ namespace OpenNest.Shapes
|
|||||||
for (var i = 0; i < HoleCount; i++)
|
for (var i = 0; i < HoleCount; i++)
|
||||||
{
|
{
|
||||||
var angle = i * angleStep;
|
var angle = i * angleStep;
|
||||||
var cx = boltCircleRadius * System.Math.Cos(angle);
|
var cx = boltCircleRadius * PortableMath.Cos(angle);
|
||||||
var cy = boltCircleRadius * System.Math.Sin(angle);
|
var cy = boltCircleRadius * PortableMath.Sin(angle);
|
||||||
entities.Add(new Circle(cx, cy, holeRadius));
|
entities.Add(new Circle(cx, cy, holeRadius));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using OpenNest.Converters;
|
using OpenNest.Converters;
|
||||||
using OpenNest.Geometry;
|
using OpenNest.Geometry;
|
||||||
|
using OpenNest.Math;
|
||||||
|
|
||||||
namespace OpenNest;
|
namespace OpenNest;
|
||||||
|
|
||||||
@@ -470,8 +471,8 @@ public static class DrawingSplitter
|
|||||||
{
|
{
|
||||||
var midAngle = (arc.StartAngle + arc.EndAngle) / 2;
|
var midAngle = (arc.StartAngle + arc.EndAngle) / 2;
|
||||||
return new Vector(
|
return new Vector(
|
||||||
arc.Center.X + arc.Radius * System.Math.Cos(midAngle),
|
arc.Center.X + arc.Radius * PortableMath.Cos(midAngle),
|
||||||
arc.Center.Y + arc.Radius * System.Math.Sin(midAngle)
|
arc.Center.Y + arc.Radius * PortableMath.Sin(midAngle)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using OpenNest.Geometry;
|
using OpenNest.Geometry;
|
||||||
|
using OpenNest.Math;
|
||||||
|
|
||||||
namespace OpenNest;
|
namespace OpenNest;
|
||||||
|
|
||||||
@@ -25,8 +26,8 @@ public class SpikeGrooveSplit : ISplitFeature
|
|||||||
var spikeDepth = parameters.SpikeDepth;
|
var spikeDepth = parameters.SpikeDepth;
|
||||||
var grooveDepth = parameters.GrooveDepth;
|
var grooveDepth = parameters.GrooveDepth;
|
||||||
var angleRad = OpenNest.Math.Angle.ToRadians(parameters.SpikeAngle / 2);
|
var angleRad = OpenNest.Math.Angle.ToRadians(parameters.SpikeAngle / 2);
|
||||||
var spikeHalfWidth = spikeDepth * System.Math.Tan(angleRad);
|
var spikeHalfWidth = spikeDepth * PortableMath.Tan(angleRad);
|
||||||
var grooveHalfWidth = grooveDepth * System.Math.Tan(angleRad);
|
var grooveHalfWidth = grooveDepth * PortableMath.Tan(angleRad);
|
||||||
|
|
||||||
var isVertical = line.Axis == CutOffAxis.Vertical;
|
var isVertical = line.Axis == CutOffAxis.Vertical;
|
||||||
var pos = line.Position;
|
var pos = line.Position;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using OpenNest.Geometry;
|
using OpenNest.Geometry;
|
||||||
|
using OpenNest.Math;
|
||||||
|
|
||||||
namespace OpenNest.Support
|
namespace OpenNest.Support
|
||||||
{
|
{
|
||||||
@@ -177,7 +178,7 @@ namespace OpenNest.Support
|
|||||||
{
|
{
|
||||||
var box = BoundingRectangle(outline);
|
var box = BoundingRectangle(outline);
|
||||||
var angle = box.Width >= box.Height ? box.Angle : box.Angle + Math.Angle.HalfPI;
|
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
|
// 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);
|
minV = System.Math.Min(minV, pv);
|
||||||
maxV = System.Math.Max(maxV, 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)
|
if (candidates.Count == 0)
|
||||||
return RotatingCalipers.MinimumBoundingRectangle(outline.ToList());
|
return RotatingCalipers.MinimumBoundingRectangle(outline.ToList());
|
||||||
@@ -262,7 +263,7 @@ namespace OpenNest.Support
|
|||||||
var outOf = outline[(i + 1) % count] - outline[i];
|
var outOf = outline[(i + 1) % count] - outline[i];
|
||||||
var cross = into.X * outOf.Y - into.Y * outOf.X;
|
var cross = into.X * outOf.Y - into.Y * outOf.X;
|
||||||
var dot = into.X * outOf.X + into.Y * outOf.Y;
|
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.
|
// Start after a vertex that does not turn inward so no run is split by the wrap.
|
||||||
|
|||||||
@@ -40,6 +40,10 @@ namespace OpenNest.Engine.Tests.Jobs;
|
|||||||
/// Re-captured when the pairs phase began tiling herringbone pairs (<c>HerringbonePatterns</c>):
|
/// Re-captured when the pairs phase began tiling herringbone pairs (<c>HerringbonePatterns</c>):
|
||||||
/// the mixed Strip nest keeps 35 laid out differently, and herringbone pairs won the chevron of
|
/// 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 whatever angles were learned, so a cross replaced it.
|
||||||
|
/// The learned-angle nests were re-captured when geometry moved to <c>PortableMath</c>: 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
|
/// 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
|
/// with other irregular items. Their Fill and Vertical Remnant nests vary between runs, most
|
||||||
/// likely because best-fit pair candidates are gathered in a <c>ConcurrentBag</c> and equal
|
/// likely because best-fit pair candidates are gathered in a <c>ConcurrentBag</c> and equal
|
||||||
@@ -47,16 +51,6 @@ namespace OpenNest.Engine.Tests.Jobs;
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
public class FillStackCharacterizationTests
|
public class FillStackCharacterizationTests
|
||||||
{
|
{
|
||||||
/// <summary>A theory that runs only on Windows.</summary>
|
|
||||||
private sealed class WindowsOnlyTheoryAttribute : TheoryAttribute
|
|
||||||
{
|
|
||||||
public WindowsOnlyTheoryAttribute()
|
|
||||||
{
|
|
||||||
if (!OperatingSystem.IsWindows())
|
|
||||||
Skip = "Layout differs off Windows (Project Memory 518f3b58)";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>6x4 block with a 2x2 notch cut into the middle of its top edge.</summary>
|
/// <summary>6x4 block with a 2x2 notch cut into the middle of its top edge.</summary>
|
||||||
private static Program Notched()
|
private static Program Notched()
|
||||||
{
|
{
|
||||||
@@ -309,17 +303,11 @@ public class FillStackCharacterizationTests
|
|||||||
AssertPinned(layout, expectedCount, expectedDigest);
|
AssertPinned(layout, expectedCount, expectedDigest);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <remarks>
|
[Theory]
|
||||||
/// Windows-only: these pins were captured on Windows, and Linux lays this nest out
|
[InlineData("Fill", 28, "58E544673B69F3CB")]
|
||||||
/// differently (Strip places 28, not 23) because the two platforms' trig differs in the last
|
[InlineData("Strip", 28, "BB448C553CFECBD8")]
|
||||||
/// bit and the fill stack turns that into different tie and threshold decisions (Project
|
[InlineData("Vertical Remnant", 28, "E146E6F08C26C375")]
|
||||||
/// Memory 518f3b58). Unskip on every platform when that is fixed.
|
[InlineData("Horizontal Remnant", 24, "5D5B0413007B0B07")]
|
||||||
/// </remarks>
|
|
||||||
[WindowsOnlyTheory]
|
|
||||||
[InlineData("Fill", 28, "BE363E3A47E9B036")]
|
|
||||||
[InlineData("Strip", 23, "E4AA82327508D538")]
|
|
||||||
[InlineData("Vertical Remnant", 28, "DB2409DB39642666")]
|
|
||||||
[InlineData("Horizontal Remnant", 24, "883BD209AE87E2C5")]
|
|
||||||
public void Nest_LearnedAngleItems_PinnedLayout(
|
public void Nest_LearnedAngleItems_PinnedLayout(
|
||||||
string strategy,
|
string strategy,
|
||||||
int expectedCount,
|
int expectedCount,
|
||||||
|
|||||||
@@ -213,7 +213,7 @@ namespace OpenNest.Engine.BestFit
|
|||||||
if (dx * dx + dy * dy < Tolerance.Epsilon)
|
if (dx * dx + dy * dy < Tolerance.Epsilon)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var angle = Angle.NormalizeRad(System.Math.Atan2(dy, dx));
|
var angle = Angle.NormalizeRad(PortableMath.Atan2(dy, dx));
|
||||||
AddUniqueAngle(hullAngles, angle);
|
AddUniqueAngle(hullAngles, angle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -54,8 +54,8 @@ namespace OpenNest.Engine.BestFit
|
|||||||
return polygon;
|
return polygon;
|
||||||
|
|
||||||
var result = new Polygon();
|
var result = new Polygon();
|
||||||
var cos = System.Math.Cos(angle);
|
var cos = PortableMath.Cos(angle);
|
||||||
var sin = System.Math.Sin(angle);
|
var sin = PortableMath.Sin(angle);
|
||||||
|
|
||||||
foreach (var v in polygon.Vertices)
|
foreach (var v in polygon.Vertices)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ namespace OpenNest.Engine.Fill
|
|||||||
/// Pushes movingParts along an arbitrary angle (radians, 0 = right, π/2 = up).
|
/// Pushes movingParts along an arbitrary angle (radians, 0 = right, π/2 = up).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static double Push(List<Part> movingParts, Plate plate, double angle) =>
|
public static double Push(List<Part> 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<Part> movingParts, Plate plate, Vector direction)
|
private static double Push(List<Part> movingParts, Plate plate, Vector direction)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ namespace OpenNest.Engine.Fill
|
|||||||
if (lengthSq < Tolerance.Epsilon)
|
if (lengthSq < Tolerance.Epsilon)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var angle = -System.Math.Atan2(dy, dx);
|
var angle = -PortableMath.Atan2(dy, dx);
|
||||||
|
|
||||||
if (!edges.Any(e => e.angle.IsEqualTo(angle)))
|
if (!edges.Any(e => e.angle.IsEqualTo(angle)))
|
||||||
edges.Add((angle, lengthSq));
|
edges.Add((angle, lengthSq));
|
||||||
|
|||||||
@@ -260,8 +260,8 @@ namespace OpenNest.Engine.Fill
|
|||||||
|
|
||||||
private static (double Along, double Cross) Extents(List<Vector> hull, double angle, bool alongX)
|
private static (double Along, double Cross) Extents(List<Vector> hull, double angle, bool alongX)
|
||||||
{
|
{
|
||||||
var cos = System.Math.Cos(angle);
|
var cos = PortableMath.Cos(angle);
|
||||||
var sin = System.Math.Sin(angle);
|
var sin = PortableMath.Sin(angle);
|
||||||
double minX = double.MaxValue, maxX = double.MinValue;
|
double minX = double.MaxValue, maxX = double.MinValue;
|
||||||
double minY = double.MaxValue, maxY = double.MinValue;
|
double minY = double.MaxValue, maxY = double.MinValue;
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using OpenNest.Math;
|
||||||
|
|
||||||
namespace OpenNest.Engine.Jobs.CommonLines;
|
namespace OpenNest.Engine.Jobs.CommonLines;
|
||||||
|
|
||||||
@@ -138,8 +139,8 @@ internal sealed class CommonLinePipelinePrepass : IPipelinePrepass
|
|||||||
poses.Add((pose.PartId, pose.X, pose.Y, pose.Rotation));
|
poses.Add((pose.PartId, pose.X, pose.Y, pose.Rotation));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var sine = System.Math.Sin(pose.Rotation);
|
var sine = PortableMath.Sin(pose.Rotation);
|
||||||
var cosine = System.Math.Cos(pose.Rotation);
|
var cosine = PortableMath.Cos(pose.Rotation);
|
||||||
var pair = proxy.Pair;
|
var pair = proxy.Pair;
|
||||||
groups.Add(new NestJobCommonLine(kerf, new[] { poses.Count, poses.Count + 1 }));
|
groups.Add(new NestJobCommonLine(kerf, new[] { poses.Count, poses.Count + 1 }));
|
||||||
poses.Add((proxy.PartId, pose.X, pose.Y, pose.Rotation));
|
poses.Add((proxy.PartId, pose.X, pose.Y, pose.Rotation));
|
||||||
|
|||||||
@@ -247,7 +247,7 @@ internal static class CutoutCirclePacker
|
|||||||
{
|
{
|
||||||
if (p.DistanceTo(0, 0) < Epsilon)
|
if (p.DistanceTo(0, 0) < Epsilon)
|
||||||
return -1;
|
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;
|
return angle < 0 ? angle + 2 * System.Math.PI : angle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,7 @@ using System.Linq;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using OpenNest.Engine.CirclePacking;
|
using OpenNest.Engine.CirclePacking;
|
||||||
using OpenNest.Engine.Jobs.Adapters;
|
using OpenNest.Engine.Jobs.Adapters;
|
||||||
|
using OpenNest.Math;
|
||||||
|
|
||||||
namespace OpenNest.Engine.Jobs.Cutouts;
|
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,
|
private void Expand(Bundle bundle, double x, double y, double rotation,
|
||||||
List<(string PartId, double X, double Y, double Rotation)> poses)
|
List<(string PartId, double X, double Y, double Rotation)> poses)
|
||||||
{
|
{
|
||||||
var sine = System.Math.Sin(rotation);
|
var sine = PortableMath.Sin(rotation);
|
||||||
var cosine = System.Math.Cos(rotation);
|
var cosine = PortableMath.Cos(rotation);
|
||||||
foreach (var local in bundle.LocalInserts)
|
foreach (var local in bundle.LocalInserts)
|
||||||
{
|
{
|
||||||
var insertX = x + local.X * cosine - local.Y * sine;
|
var insertX = x + local.X * cosine - local.Y * sine;
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ public sealed class JobPartGeometry
|
|||||||
var arc = (Arc)entity;
|
var arc = (Arc)entity;
|
||||||
var angle = arc.StartAngle + (arc.IsReversed ? -1 : 1) * arc.SweepAngle() * value;
|
var angle = arc.StartAngle + (arc.IsReversed ? -1 : 1) * arc.SweepAngle() * value;
|
||||||
return arc.Center
|
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)
|
void Check(Vector point)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -83,16 +83,16 @@ internal sealed class PlateFillPolicy
|
|||||||
private static double RotatedWidth(NestItem item, double angle)
|
private static double RotatedWidth(NestItem item, double angle)
|
||||||
{
|
{
|
||||||
var boundingBox = item.Drawing.Program.BoundingBox();
|
var boundingBox = item.Drawing.Program.BoundingBox();
|
||||||
var cos = System.Math.Abs(System.Math.Cos(angle));
|
var cos = System.Math.Abs(PortableMath.Cos(angle));
|
||||||
var sin = System.Math.Abs(System.Math.Sin(angle));
|
var sin = System.Math.Abs(PortableMath.Sin(angle));
|
||||||
return boundingBox.Length * cos + boundingBox.Width * sin;
|
return boundingBox.Length * cos + boundingBox.Width * sin;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static double RotatedHeight(NestItem item, double angle)
|
private static double RotatedHeight(NestItem item, double angle)
|
||||||
{
|
{
|
||||||
var boundingBox = item.Drawing.Program.BoundingBox();
|
var boundingBox = item.Drawing.Program.BoundingBox();
|
||||||
var cos = System.Math.Abs(System.Math.Cos(angle));
|
var cos = System.Math.Abs(PortableMath.Cos(angle));
|
||||||
var sin = System.Math.Abs(System.Math.Sin(angle));
|
var sin = System.Math.Abs(PortableMath.Sin(angle));
|
||||||
return boundingBox.Width * cos + boundingBox.Length * sin;
|
return boundingBox.Width * cos + boundingBox.Length * sin;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,7 @@ using System.Threading;
|
|||||||
using OpenNest.Engine.Jobs.Adapters;
|
using OpenNest.Engine.Jobs.Adapters;
|
||||||
using OpenNest.Engine.Jobs.CommonLines;
|
using OpenNest.Engine.Jobs.CommonLines;
|
||||||
using OpenNest.Geometry;
|
using OpenNest.Geometry;
|
||||||
|
using OpenNest.Math;
|
||||||
|
|
||||||
namespace OpenNest.Engine.Jobs.Placement;
|
namespace OpenNest.Engine.Jobs.Placement;
|
||||||
|
|
||||||
@@ -83,8 +84,8 @@ public static partial class PlateFillService
|
|||||||
{
|
{
|
||||||
var rotation = placed.Rotation;
|
var rotation = placed.Rotation;
|
||||||
var first = Place(item.Drawing, rotation, placed.Location);
|
var first = Place(item.Drawing, rotation, placed.Location);
|
||||||
var sine = System.Math.Sin(rotation);
|
var sine = PortableMath.Sin(rotation);
|
||||||
var cosine = System.Math.Cos(rotation);
|
var cosine = PortableMath.Cos(rotation);
|
||||||
var second = Place(item.Drawing, rotation + CommonLinePair.SecondRotation,
|
var second = Place(item.Drawing, rotation + CommonLinePair.SecondRotation,
|
||||||
new Vector(placed.Location.X + pair.SecondX * cosine - pair.SecondY * sine,
|
new Vector(placed.Location.X + pair.SecondX * cosine - pair.SecondY * sine,
|
||||||
placed.Location.Y + pair.SecondX * sine + pair.SecondY * cosine));
|
placed.Location.Y + pair.SecondX * sine + pair.SecondY * cosine));
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Clipper2Lib;
|
using Clipper2Lib;
|
||||||
using OpenNest.Geometry;
|
using OpenNest.Geometry;
|
||||||
|
using OpenNest.Math;
|
||||||
|
|
||||||
namespace OpenNest.Engine.NestingEngines.Irregular;
|
namespace OpenNest.Engine.NestingEngines.Irregular;
|
||||||
|
|
||||||
@@ -83,8 +84,8 @@ internal static class ContactFit
|
|||||||
|
|
||||||
double theta = 0, x = centerX, y = centerY;
|
double theta = 0, x = centerX, y = centerY;
|
||||||
Pose At() => new(start.Rotation + theta,
|
Pose At() => new(start.Rotation + theta,
|
||||||
x - (System.Math.Cos(theta) * pivotX - System.Math.Sin(theta) * pivotY),
|
x - (PortableMath.Cos(theta) * pivotX - PortableMath.Sin(theta) * pivotY),
|
||||||
y - (System.Math.Sin(theta) * pivotX + System.Math.Cos(theta) * pivotY));
|
y - (PortableMath.Sin(theta) * pivotX + PortableMath.Cos(theta) * pivotY));
|
||||||
Pose? best = null;
|
Pose? best = null;
|
||||||
var bestWorst = double.PositiveInfinity;
|
var bestWorst = double.PositiveInfinity;
|
||||||
var cap = MaxStep;
|
var cap = MaxStep;
|
||||||
@@ -205,8 +206,8 @@ internal static class ContactFit
|
|||||||
|
|
||||||
private static PathD Place(PathD local, double theta, double x, double y)
|
private static PathD Place(PathD local, double theta, double x, double y)
|
||||||
{
|
{
|
||||||
var cos = System.Math.Cos(theta);
|
var cos = PortableMath.Cos(theta);
|
||||||
var sin = System.Math.Sin(theta);
|
var sin = PortableMath.Sin(theta);
|
||||||
var result = new PathD(local.Count);
|
var result = new PathD(local.Count);
|
||||||
foreach (var p in local)
|
foreach (var p in local)
|
||||||
result.Add(new PointD(cos * p.x - sin * p.y + x, sin * p.x + cos * p.y + y));
|
result.Add(new PointD(cos * p.x - sin * p.y + x, sin * p.x + cos * p.y + y));
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using System.Linq;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using Clipper2Lib;
|
using Clipper2Lib;
|
||||||
using OpenNest.Engine.Jobs;
|
using OpenNest.Engine.Jobs;
|
||||||
|
using OpenNest.Math;
|
||||||
|
|
||||||
namespace OpenNest.Engine.NestingEngines.Irregular;
|
namespace OpenNest.Engine.NestingEngines.Irregular;
|
||||||
|
|
||||||
@@ -202,7 +203,7 @@ internal sealed class NoFitCache
|
|||||||
|
|
||||||
private static double EdgeAngle(PointD edge)
|
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;
|
return angle < 0 ? angle + System.Math.PI * 2 : angle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using System.Collections.Generic;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using OpenNest.Geometry;
|
using OpenNest.Geometry;
|
||||||
|
using OpenNest.Math;
|
||||||
|
|
||||||
namespace OpenNest.IO
|
namespace OpenNest.IO
|
||||||
{
|
{
|
||||||
@@ -383,9 +384,9 @@ namespace OpenNest.IO
|
|||||||
) / d;
|
) / d;
|
||||||
var radius = System.Math.Sqrt((ax - ux) * (ax - ux) + (ay - uy) * (ay - uy));
|
var radius = System.Math.Sqrt((ax - ux) * (ax - ux) + (ay - uy) * (ay - uy));
|
||||||
|
|
||||||
var a0 = System.Math.Atan2(ay - uy, ax - ux);
|
var a0 = PortableMath.Atan2(ay - uy, ax - ux);
|
||||||
var am = System.Math.Atan2(by - uy, bx - ux);
|
var am = PortableMath.Atan2(by - uy, bx - ux);
|
||||||
var a1 = System.Math.Atan2(cy - uy, cx - ux);
|
var a1 = PortableMath.Atan2(cy - uy, cx - ux);
|
||||||
|
|
||||||
var ccwSweep = a1 - a0;
|
var ccwSweep = a1 - a0;
|
||||||
while (ccwSweep <= 0)
|
while (ccwSweep <= 0)
|
||||||
@@ -403,8 +404,8 @@ namespace OpenNest.IO
|
|||||||
var angle = a0 + sweep * t;
|
var angle = a0 + sweep * t;
|
||||||
output.Add(
|
output.Add(
|
||||||
new Vector(
|
new Vector(
|
||||||
ux + radius * System.Math.Cos(angle),
|
ux + radius * PortableMath.Cos(angle),
|
||||||
uy + radius * System.Math.Sin(angle)
|
uy + radius * PortableMath.Sin(angle)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -398,9 +398,9 @@ namespace OpenNest.IO
|
|||||||
center = new XYZ(center.X + CurPos.X, center.Y + CurPos.Y, 0);
|
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)
|
if (arc.Rotation == RotationType.CW)
|
||||||
Generic.Swap(ref startAngle, ref endAngle);
|
Generic.Swap(ref startAngle, ref endAngle);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using System.Drawing;
|
|||||||
using ACadSharp.Entities;
|
using ACadSharp.Entities;
|
||||||
using CSMath;
|
using CSMath;
|
||||||
using OpenNest.Geometry;
|
using OpenNest.Geometry;
|
||||||
|
using OpenNest.Math;
|
||||||
|
|
||||||
namespace OpenNest.IO
|
namespace OpenNest.IO
|
||||||
{
|
{
|
||||||
@@ -210,7 +211,7 @@ namespace OpenNest.IO
|
|||||||
|
|
||||||
var semiMajor = System.Math.Sqrt(majorAxis.X * majorAxis.X + majorAxis.Y * majorAxis.Y);
|
var semiMajor = System.Math.Sqrt(majorAxis.X * majorAxis.X + majorAxis.Y * majorAxis.Y);
|
||||||
var semiMinor = semiMajor * ellipse.RadiusRatio;
|
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 layer = ellipse.Layer.ToOpenNest();
|
||||||
var color = ellipse.ResolveColor();
|
var color = ellipse.ResolveColor();
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using ACadSharp.Tables;
|
|||||||
using CSMath;
|
using CSMath;
|
||||||
using OpenNest.Bending;
|
using OpenNest.Bending;
|
||||||
using OpenNest.Converters;
|
using OpenNest.Converters;
|
||||||
|
using OpenNest.Math;
|
||||||
// Disambiguate Entity — both ACadSharp.Entities and OpenNest.Geometry define it
|
// Disambiguate Entity — both ACadSharp.Entities and OpenNest.Geometry define it
|
||||||
using GeoEntity = OpenNest.Geometry.Entity;
|
using GeoEntity = OpenNest.Geometry.Entity;
|
||||||
|
|
||||||
@@ -174,8 +175,8 @@ namespace OpenNest.IO
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
var angle = start.AngleTo(end);
|
var angle = start.AngleTo(end);
|
||||||
var dx = System.Math.Cos(angle) * EtchLength;
|
var dx = PortableMath.Cos(angle) * EtchLength;
|
||||||
var dy = System.Math.Sin(angle) * EtchLength;
|
var dy = PortableMath.Sin(angle) * EtchLength;
|
||||||
|
|
||||||
doc.Entities.Add(
|
doc.Entities.Add(
|
||||||
new ACadSharp.Entities.Line
|
new ACadSharp.Entities.Line
|
||||||
|
|||||||
+10
-10
@@ -703,8 +703,8 @@ namespace OpenNest.IO
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var cos = System.Math.Cos(rotation);
|
var cos = PortableMath.Cos(rotation);
|
||||||
var sin = System.Math.Sin(rotation);
|
var sin = PortableMath.Sin(rotation);
|
||||||
var halfX = (from.X - to.X) / 2;
|
var halfX = (from.X - to.X) / 2;
|
||||||
var halfY = (from.Y - to.Y) / 2;
|
var halfY = (from.Y - to.Y) / 2;
|
||||||
var x = cos * halfX + sin * halfY;
|
var x = cos * halfX + sin * halfY;
|
||||||
@@ -731,8 +731,8 @@ namespace OpenNest.IO
|
|||||||
sin * centerX + cos * centerY + (from.Y + to.Y) / 2
|
sin * centerX + cos * centerY + (from.Y + to.Y) / 2
|
||||||
);
|
);
|
||||||
|
|
||||||
var startAngle = System.Math.Atan2((y - centerY) / ry, (x - centerX) / rx);
|
var startAngle = PortableMath.Atan2((y - centerY) / ry, (x - centerX) / rx);
|
||||||
var endAngle = System.Math.Atan2((-y - centerY) / ry, (-x - centerX) / rx);
|
var endAngle = PortableMath.Atan2((-y - centerY) / ry, (-x - centerX) / rx);
|
||||||
var delta = endAngle - startAngle;
|
var delta = endAngle - startAngle;
|
||||||
if (sweep && delta < 0)
|
if (sweep && delta < 0)
|
||||||
delta += Angle.TwoPI;
|
delta += Angle.TwoPI;
|
||||||
@@ -758,17 +758,17 @@ namespace OpenNest.IO
|
|||||||
Layer layer
|
Layer layer
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
var cos = System.Math.Cos(rotation);
|
var cos = PortableMath.Cos(rotation);
|
||||||
var sin = System.Math.Sin(rotation);
|
var sin = PortableMath.Sin(rotation);
|
||||||
var axisX = t.ApplyLinear(new Vector(rx * cos, rx * sin));
|
var axisX = t.ApplyLinear(new Vector(rx * cos, rx * sin));
|
||||||
var axisY = t.ApplyLinear(new Vector(-ry * sin, ry * cos));
|
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),
|
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
|
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 major = axisX * PortableMath.Cos(offset) + axisY * PortableMath.Sin(offset);
|
||||||
var minor = axisY * System.Math.Cos(offset) - axisX * System.Math.Sin(offset);
|
var minor = axisY * PortableMath.Cos(offset) - axisX * PortableMath.Sin(offset);
|
||||||
|
|
||||||
var semiMajor = Length2(major);
|
var semiMajor = Length2(major);
|
||||||
var semiMinor = Length2(minor);
|
var semiMinor = Length2(minor);
|
||||||
@@ -784,7 +784,7 @@ namespace OpenNest.IO
|
|||||||
t.Apply(center),
|
t.Apply(center),
|
||||||
semiMajor,
|
semiMajor,
|
||||||
semiMinor,
|
semiMinor,
|
||||||
System.Math.Atan2(major.Y, major.X),
|
PortableMath.Atan2(major.Y, major.X),
|
||||||
System.Math.Min(from, to),
|
System.Math.Min(from, to),
|
||||||
System.Math.Max(from, to),
|
System.Math.Max(from, to),
|
||||||
tolerance
|
tolerance
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using OpenNest.Geometry;
|
using OpenNest.Geometry;
|
||||||
|
using OpenNest.Math;
|
||||||
|
|
||||||
namespace OpenNest.IO
|
namespace OpenNest.IO
|
||||||
{
|
{
|
||||||
@@ -126,12 +127,12 @@ namespace OpenNest.IO
|
|||||||
private static SvgTransform Rotation(double degrees)
|
private static SvgTransform Rotation(double degrees)
|
||||||
{
|
{
|
||||||
var radians = degrees * System.Math.PI / 180;
|
var radians = degrees * System.Math.PI / 180;
|
||||||
var cos = System.Math.Cos(radians);
|
var cos = PortableMath.Cos(radians);
|
||||||
var sin = System.Math.Sin(radians);
|
var sin = PortableMath.Sin(radians);
|
||||||
return new SvgTransform(cos, sin, -sin, cos, 0, 0);
|
return new SvgTransform(cos, sin, -sin, cos, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static double Tan(double degrees) =>
|
private static double Tan(double degrees) =>
|
||||||
System.Math.Tan(degrees * System.Math.PI / 180);
|
PortableMath.Tan(degrees * System.Math.PI / 180);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -112,11 +112,12 @@ public class BoundingBoxHelperTests
|
|||||||
return new Box(l, bo, r - l, t - bo);
|
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)
|
private static Vector FrozenRotate(Vector v, double angle)
|
||||||
{
|
{
|
||||||
var sine = System.Math.Sin(angle);
|
var sine = OpenNest.Math.PortableMath.Sin(angle);
|
||||||
var cosine = System.Math.Cos(angle);
|
var cosine = OpenNest.Math.PortableMath.Cos(angle);
|
||||||
return new Vector(v.X * cosine - v.Y * sine, v.X * sine + v.Y * cosine);
|
return new Vector(v.X * cosine - v.Y * sine, v.X * sine + v.Y * cosine);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,8 @@
|
|||||||
// commit ad5f2a9041ee14d0a85aab00e0c8a9f5082d5b6b, before the axis band filter, used only as
|
// commit ad5f2a9041ee14d0a85aab00e0c8a9f5082d5b6b, before the axis band filter, used only as
|
||||||
// the differential oracle in SlideBandTests. Mechanical edits only: renamed types, internal
|
// the differential oracle in SlideBandTests. Mechanical edits only: renamed types, internal
|
||||||
// visibility, calls to unchanged SlideEvents members qualified, the private Eps constant
|
// 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
|
#nullable disable
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using OpenNest.Geometry;
|
using OpenNest.Geometry;
|
||||||
@@ -54,7 +55,7 @@ namespace OpenNest.Tests.Geometry
|
|||||||
// The directional distance from an arc point at angle θ to the
|
// The directional distance from an arc point at angle θ to the
|
||||||
// line is t(θ) = [A + r·(ey·cosθ − ex·sinθ)] / det.
|
// line is t(θ) = [A + r·(ey·cosθ − ex·sinθ)] / det.
|
||||||
// dt/dθ = 0 at θ = atan2(−ex, ey) and θ + π.
|
// 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);
|
var theta2 = Angle.NormalizeRad(theta1 + System.Math.PI);
|
||||||
|
|
||||||
for (var k = 0; k < 2; k++)
|
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))
|
if (arc != null && !Angle.IsBetweenRad(theta, arc.StartAngle, arc.EndAngle, arc.IsReversed))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var qx = cx + r * System.Math.Cos(theta);
|
var qx = cx + r * PortableMath.Cos(theta);
|
||||||
var qy = cy + r * System.Math.Sin(theta);
|
var qy = cy + r * PortableMath.Sin(theta);
|
||||||
|
|
||||||
SlideEvents.RayLine(ref sink, qx, qy, p1x, p1y, p2x, p2y, rayX, rayY, arcMoves);
|
SlideEvents.RayLine(ref sink, qx, qy, p1x, p1y, p2x, p2y, rayX, rayY, arcMoves);
|
||||||
if (sink.IsDone)
|
if (sink.IsDone)
|
||||||
|
|||||||
@@ -87,6 +87,47 @@ public class PortableMathTests
|
|||||||
Assert.True(double.IsNaN(PortableMath.Atan2(double.NaN, 1)));
|
Assert.True(double.IsNaN(PortableMath.Atan2(double.NaN, 1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
[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<string>();
|
||||||
|
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)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The point of PortableMath: the same bits on every platform. The digest was captured on
|
/// 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).
|
/// Linux and must also pass on Windows (Project Memory 518f3b58).
|
||||||
|
|||||||
Reference in new issue
Block a user