mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-11-10 17:51:09 -05:00
test: share the circle, square, rectangle and contour fixtures
Cutting-planning, diagnostics and fill tests drew the same circles, squares, clockwise rectangles, closed polygons, rounded rectangles and right-midpoint rectangle contours in private helpers. They now use TestHelpers; files whose helper had its own default size keep a one-line wrapper for it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a5e219008e
commit
f0d84e041c
20 files changed
+193
-278
No files matched your search
@@ -14,18 +14,8 @@ public class BestFitOverlapTests
|
||||
|
||||
public BestFitOverlapTests(ITestOutputHelper output) => _output = output;
|
||||
|
||||
private static Drawing MakeRoundedRect(double w = 7.25, double h = 3.31, double r = 0.5)
|
||||
{
|
||||
var pgm = new Program();
|
||||
pgm.Codes.Add(new RapidMove(new Vector(0, 0)));
|
||||
pgm.Codes.Add(new LinearMove(new Vector(w - r, 0)));
|
||||
pgm.Codes.Add(new ArcMove(new Vector(w, r), new Vector(w - r, r), RotationType.CW));
|
||||
pgm.Codes.Add(new LinearMove(new Vector(w, h - r)));
|
||||
pgm.Codes.Add(new ArcMove(new Vector(w - r, h), new Vector(w - r, h - r), RotationType.CW));
|
||||
pgm.Codes.Add(new LinearMove(new Vector(0, h)));
|
||||
pgm.Codes.Add(new LinearMove(new Vector(0, 0)));
|
||||
return new Drawing("rounded-rect", pgm);
|
||||
}
|
||||
private static Drawing MakeRoundedRect(double w = 7.25, double h = 3.31, double r = 0.5) =>
|
||||
TestHelpers.MakeRoundedRectDrawing(w, h, r);
|
||||
|
||||
private static Drawing ImportDxf()
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using OpenNest.CNC;
|
||||
using OpenNest.CNC.CuttingPlanning;
|
||||
using OpenNest.Geometry;
|
||||
using static OpenNest.Tests.TestHelpers;
|
||||
|
||||
namespace OpenNest.Tests.CuttingPlanning;
|
||||
|
||||
@@ -16,15 +17,6 @@ public class ContourEntryCandidateTests
|
||||
|
||||
// --- fixtures ---------------------------------------------------------------
|
||||
|
||||
private static Program ClosedContour(IEnumerable<Vector> vertices)
|
||||
{
|
||||
var p = new Program();
|
||||
p.MoveTo(vertices.First().X, vertices.First().Y);
|
||||
foreach (var v in vertices.Skip(1).Append(vertices.First()))
|
||||
p.LineTo(v.X, v.Y);
|
||||
return p;
|
||||
}
|
||||
|
||||
/// <summary>Square perimeter with an L-notch hole slug; its concave vertex (3,3.5) is reflex for the slug's own travel.</summary>
|
||||
private static Program NotchedHole()
|
||||
{
|
||||
@@ -185,7 +177,7 @@ public class ContourEntryCandidateTests
|
||||
public void CollinearSplit_IsNotATangentJoint_ButBothMidpointsRemain()
|
||||
{
|
||||
// Bottom edge split into two collinear lines at (5,0).
|
||||
var p = ClosedContour(new[]
|
||||
var p = MakeClosedProgram(new[]
|
||||
{
|
||||
new Vector(0, 0), new Vector(5, 0), new Vector(10, 0), new Vector(10, 10), new Vector(0, 10),
|
||||
});
|
||||
@@ -225,7 +217,7 @@ public class ContourEntryCandidateTests
|
||||
public void ReflexVertexOfOuterOutline_IsNeverEnumerated()
|
||||
{
|
||||
// L-shaped outline: (5,5) is reflex for the part's own CCW travel.
|
||||
var p = ClosedContour(new[]
|
||||
var p = MakeClosedProgram(new[]
|
||||
{
|
||||
new Vector(0, 0), new Vector(10, 0), new Vector(10, 10), new Vector(5, 10),
|
||||
new Vector(5, 5), new Vector(0, 5),
|
||||
@@ -255,7 +247,7 @@ public class ContourEntryCandidateTests
|
||||
public void ManualEntry_StillAcceptsAReflexPoint()
|
||||
{
|
||||
// The manual path is unchanged: an explicit reflex vertex is a valid choice.
|
||||
var prepared = Capture(ClosedContour(new[]
|
||||
var prepared = Capture(MakeClosedProgram(new[]
|
||||
{
|
||||
new Vector(0, 0), new Vector(10, 0), new Vector(10, 10), new Vector(5, 10),
|
||||
new Vector(5, 5), new Vector(0, 5),
|
||||
@@ -437,7 +429,7 @@ public class ContourEntryCandidateTests
|
||||
{
|
||||
// 10 x 0.4 rectangle: every convex corner's vertical edge (0.4) is shorter than
|
||||
// 2 x lead-in (0.6), so only the horizontal edges carry near-corner points.
|
||||
var p = ClosedContour(new[]
|
||||
var p = MakeClosedProgram(new[]
|
||||
{
|
||||
new Vector(0, 0), new Vector(10, 0), new Vector(10, 0.4), new Vector(0, 0.4),
|
||||
});
|
||||
@@ -464,7 +456,7 @@ public class ContourEntryCandidateTests
|
||||
// Boundary notch 0.6 deep — exactly 2 x lead-in. The inset from each convex
|
||||
// opening corner lands exactly on the reflex inner corner, so neither inner
|
||||
// corner may appear in the automatic catalog at all.
|
||||
var p = ClosedContour(new[]
|
||||
var p = MakeClosedProgram(new[]
|
||||
{
|
||||
new Vector(0, 0), new Vector(10, 0), new Vector(10, 10), new Vector(0, 10),
|
||||
new Vector(0, 4.6), new Vector(0.6, 4.6), new Vector(0.6, 4), new Vector(0, 4),
|
||||
@@ -480,7 +472,7 @@ public class ContourEntryCandidateTests
|
||||
{
|
||||
// L-outline; (5,5) is reflex for its own travel. The raw closest point from that
|
||||
// position lands exactly on the reflex vertex and must be dropped.
|
||||
var p = ClosedContour(new[]
|
||||
var p = MakeClosedProgram(new[]
|
||||
{
|
||||
new Vector(0, 0), new Vector(10, 0), new Vector(10, 10), new Vector(5, 10),
|
||||
new Vector(5, 5), new Vector(0, 5),
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using OpenNest.CNC;
|
||||
using OpenNest.CNC.CuttingPlanning;
|
||||
using OpenNest.Geometry;
|
||||
using static OpenNest.Tests.TestHelpers;
|
||||
|
||||
namespace OpenNest.Tests.CuttingPlanning;
|
||||
|
||||
@@ -17,16 +18,6 @@ public class ContourEntryRankingTests
|
||||
|
||||
// --- fixtures ---------------------------------------------------------------
|
||||
|
||||
/// <summary>Square 0..10 in travel order, optionally cyclically reindexed or reversed.</summary>
|
||||
private static Program Square(IEnumerable<Vector> vertices)
|
||||
{
|
||||
var p = new Program();
|
||||
p.MoveTo(vertices.First().X, vertices.First().Y);
|
||||
foreach (var v in vertices.Skip(1).Append(vertices.First()))
|
||||
p.LineTo(v.X, v.Y);
|
||||
return p;
|
||||
}
|
||||
|
||||
private static readonly Vector[] SquareVertices =
|
||||
{
|
||||
new(0, 0), new(10, 0), new(10, 10), new(0, 10),
|
||||
@@ -55,7 +46,8 @@ public class ContourEntryRankingTests
|
||||
[Fact]
|
||||
public void LowerRightTarget_PrefersTheBottomRightCorner()
|
||||
{
|
||||
var ranked = Catalog(Square(SquareVertices)).RankTowardNextCut(new Vector(14, -2));
|
||||
var ranked = Catalog(MakeClosedProgram(SquareVertices))
|
||||
.RankTowardNextCut(new Vector(14, -2));
|
||||
|
||||
// Facing sides right and bottom; the shared corner is the ideal start.
|
||||
var first = Assert.Single(ranked.Take(1));
|
||||
@@ -70,7 +62,8 @@ public class ContourEntryRankingTests
|
||||
public void FacingMidpoint_BeatsANonFacingCorner()
|
||||
{
|
||||
// Target due right: only the right side faces it; the left corners face away.
|
||||
var ranked = Catalog(Square(SquareVertices)).RankTowardNextCut(new Vector(14, 5));
|
||||
var ranked = Catalog(MakeClosedProgram(SquareVertices))
|
||||
.RankTowardNextCut(new Vector(14, 5));
|
||||
var order = ranked.ToList();
|
||||
|
||||
var rightMid = order.FindIndex(c => c.Kind == AutomaticEntryKind.StraightMidpoint && At(c, 10, 5));
|
||||
@@ -81,7 +74,8 @@ public class ContourEntryRankingTests
|
||||
[Fact]
|
||||
public void SameFacingClass_CornerBeatsMidpoint()
|
||||
{
|
||||
var ranked = Catalog(Square(SquareVertices)).RankTowardNextCut(new Vector(14, -2));
|
||||
var ranked = Catalog(MakeClosedProgram(SquareVertices))
|
||||
.RankTowardNextCut(new Vector(14, -2));
|
||||
var order = ranked.ToList();
|
||||
|
||||
var corner = order.FindIndex(c => c.Kind == AutomaticEntryKind.ConvexCorner && At(c, 10, 0));
|
||||
@@ -140,8 +134,10 @@ public class ContourEntryRankingTests
|
||||
var target = new Vector(14, -2);
|
||||
|
||||
Assert.Equal(
|
||||
Shape(Catalog(Square(SquareVertices)).RankTowardNextCut(target, new Vector(-1, -1))),
|
||||
Shape(Catalog(Square(vertices)).RankTowardNextCut(target, new Vector(-1, -1))));
|
||||
Shape(Catalog(MakeClosedProgram(SquareVertices))
|
||||
.RankTowardNextCut(target, new Vector(-1, -1))),
|
||||
Shape(Catalog(MakeClosedProgram(vertices))
|
||||
.RankTowardNextCut(target, new Vector(-1, -1))));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -162,7 +158,7 @@ public class ContourEntryRankingTests
|
||||
public void Ranking_NeverIntroducesReflexCandidates()
|
||||
{
|
||||
// L-outline: (5,5) is reflex for its own travel and absent from the catalog.
|
||||
var p = Square(new[]
|
||||
var p = MakeClosedProgram(new[]
|
||||
{
|
||||
new Vector(0, 0), new Vector(10, 0), new Vector(10, 10), new Vector(5, 10),
|
||||
new Vector(5, 5), new Vector(0, 5),
|
||||
@@ -178,7 +174,7 @@ public class ContourEntryRankingTests
|
||||
[Fact]
|
||||
public void NonFiniteTarget_IsRejected()
|
||||
{
|
||||
var catalog = Catalog(Square(SquareVertices));
|
||||
var catalog = Catalog(MakeClosedProgram(SquareVertices));
|
||||
Assert.Throws<ArgumentException>(() =>
|
||||
catalog.RankTowardNextCut(new Vector(double.NaN, 5)));
|
||||
}
|
||||
@@ -188,7 +184,8 @@ public class ContourEntryRankingTests
|
||||
[Fact]
|
||||
public void NoTarget_TierFirstThenDistanceToArrival()
|
||||
{
|
||||
var ranked = Catalog(Square(SquareVertices)).RankTowardNextCut(arrival: new Vector(5, 10));
|
||||
var ranked = Catalog(MakeClosedProgram(SquareVertices))
|
||||
.RankTowardNextCut(arrival: new Vector(5, 10));
|
||||
var order = ranked.ToList();
|
||||
|
||||
// Tier first: an outside corner outranks the midpoint the arrival sits on.
|
||||
@@ -208,7 +205,7 @@ public class ContourEntryRankingTests
|
||||
{
|
||||
// The last part faces where the head already is; the ranking must not drag it to
|
||||
// the plate origin when the arrival is elsewhere.
|
||||
var ranked = Catalog(Square(SquareVertices))
|
||||
var ranked = Catalog(MakeClosedProgram(SquareVertices))
|
||||
.RankTowardNextCut(arrival: new Vector(arrivalX, arrivalY));
|
||||
|
||||
Assert.True(At(ranked[0], expectedX, expectedY));
|
||||
|
||||
@@ -3,6 +3,7 @@ using OpenNest.CNC.CuttingPlanning;
|
||||
using OpenNest.CNC.CuttingStrategy;
|
||||
using OpenNest.Engine.CuttingPlanning;
|
||||
using OpenNest.Geometry;
|
||||
using static OpenNest.Tests.TestHelpers;
|
||||
|
||||
namespace OpenNest.Tests.CuttingPlanning;
|
||||
|
||||
@@ -15,7 +16,7 @@ public class CuttingCaptureRotationTests
|
||||
[InlineData(0.3, true)]
|
||||
public void SharedDiamond_CaptureAndReadyPreserveOnceRotatedCleanGeometry(double angle, bool incrementalRoot)
|
||||
{
|
||||
var leaf = Rectangle(1, 2);
|
||||
var leaf = MakeClockwiseRectProgram(1, 2);
|
||||
leaf.Mode = Mode.Incremental;
|
||||
var left = new Program();
|
||||
left.Codes.Add(new SubProgramCall(leaf, 0));
|
||||
@@ -23,7 +24,7 @@ public class CuttingCaptureRotationTests
|
||||
var right = new Program();
|
||||
right.Codes.Add(new SubProgramCall(leaf, 0));
|
||||
right.Mode = Mode.Incremental;
|
||||
var clean = Rectangle(10, 10);
|
||||
var clean = MakeClockwiseRectProgram(10, 10);
|
||||
clean.Codes.Add(new SubProgramCall(left, 0) { Offset = new Vector(3, 3) });
|
||||
clean.Codes.Add(new SubProgramCall(right, 0) { Offset = new Vector(7, 3) });
|
||||
if (incrementalRoot) clean.Mode = Mode.Incremental;
|
||||
@@ -80,15 +81,4 @@ public class CuttingCaptureRotationTests
|
||||
}
|
||||
|
||||
private static bool IsCut(ExecutionMotion motion) => !motion.Rapid && motion.Layer is LayerType.Cut or LayerType.Display;
|
||||
|
||||
private static Program Rectangle(double width, double height)
|
||||
{
|
||||
var program = new Program();
|
||||
program.MoveTo(0, 0);
|
||||
program.LineTo(0, height);
|
||||
program.LineTo(width, height);
|
||||
program.LineTo(width, 0);
|
||||
program.LineTo(0, 0);
|
||||
return program;
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ using OpenNest.CNC.CuttingPlanning;
|
||||
using OpenNest.CNC.CuttingStrategy;
|
||||
using OpenNest.Engine.CuttingPlanning;
|
||||
using OpenNest.Geometry;
|
||||
using static OpenNest.Tests.TestHelpers;
|
||||
|
||||
namespace OpenNest.Tests.CuttingPlanning;
|
||||
|
||||
@@ -482,26 +483,17 @@ public class CuttingDependencyTests
|
||||
{
|
||||
var clean = new Program();
|
||||
clean.MoveTo(width, height / 2);
|
||||
Contour(clean, width, height);
|
||||
TraceRectangleFromRightMidpoint(clean, width, height);
|
||||
var part = new Part(new Drawing("same", clean), new Vector(x, y));
|
||||
var placed = new Program();
|
||||
placed.MoveTo(width + 0.25, height / 2);
|
||||
placed.Codes.Add(new LinearMove(width, height / 2) { Layer = LayerType.Leadin });
|
||||
Contour(placed, width, height);
|
||||
TraceRectangleFromRightMidpoint(placed, width, height);
|
||||
placed.Codes.Add(new LinearMove(width + 0.25, height / 2) { Layer = LayerType.Leadout });
|
||||
Assert.True(part.RestoreLeadInProgram(placed, false));
|
||||
return part;
|
||||
}
|
||||
|
||||
private static void Contour(Program program, double width, double height)
|
||||
{
|
||||
program.LineTo(width, 0);
|
||||
program.LineTo(0, 0);
|
||||
program.LineTo(0, height);
|
||||
program.LineTo(width, height);
|
||||
program.LineTo(width, height / 2);
|
||||
}
|
||||
|
||||
private static string Describe(CuttingPlanResult r) =>
|
||||
$"{r.Status}, expanded {r.Expansions}: " + string.Join("; ", r.Findings.Select(f => f.Message));
|
||||
}
|
||||
@@ -6,6 +6,7 @@ using OpenNest.Collections;
|
||||
using OpenNest.Diagnostics;
|
||||
using OpenNest.Engine.CuttingPlanning;
|
||||
using OpenNest.Geometry;
|
||||
using static OpenNest.Tests.TestHelpers;
|
||||
|
||||
namespace OpenNest.Tests.CuttingPlanning;
|
||||
|
||||
@@ -552,24 +553,15 @@ public class CuttingPlanCommitTests
|
||||
{
|
||||
var clean = new Program();
|
||||
clean.MoveTo(width, height / 2);
|
||||
Contour(clean, width, height);
|
||||
TraceRectangleFromRightMidpoint(clean, width, height);
|
||||
var part = new Part(new Drawing("same", clean), new Vector(x, y));
|
||||
var placed = new Program();
|
||||
placed.MoveTo(width + 0.25, height / 2);
|
||||
placed.Codes.Add(new LinearMove(width, height / 2) { Layer = LayerType.Leadin });
|
||||
Contour(placed, width, height);
|
||||
TraceRectangleFromRightMidpoint(placed, width, height);
|
||||
placed.Codes.Add(new LinearMove(width + 0.25, height / 2) { Layer = LayerType.Leadout });
|
||||
Assert.True(part.RestoreLeadInProgram(placed, false));
|
||||
part.CuttingParameters = new CuttingParameters();
|
||||
return part;
|
||||
}
|
||||
|
||||
private static void Contour(Program program, double width, double height)
|
||||
{
|
||||
program.LineTo(width, 0);
|
||||
program.LineTo(0, 0);
|
||||
program.LineTo(0, height);
|
||||
program.LineTo(width, height);
|
||||
program.LineTo(width, height / 2);
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ using OpenNest.CNC.CuttingStrategy;
|
||||
using OpenNest.Diagnostics;
|
||||
using OpenNest.Engine.CuttingPlanning;
|
||||
using OpenNest.Geometry;
|
||||
using static OpenNest.Tests.TestHelpers;
|
||||
|
||||
namespace OpenNest.Tests.CuttingPlanning;
|
||||
|
||||
@@ -384,27 +385,18 @@ public class CuttingPlanServiceTests
|
||||
{
|
||||
var clean = new Program();
|
||||
clean.MoveTo(width, height / 2);
|
||||
Contour(clean, width, height);
|
||||
TraceRectangleFromRightMidpoint(clean, width, height);
|
||||
var part = new Part(new Drawing(name, clean), new Vector(x, y));
|
||||
var placed = new Program();
|
||||
placed.MoveTo(width + 0.25, height / 2);
|
||||
placed.Codes.Add(new LinearMove(width, height / 2) { Layer = LayerType.Leadin });
|
||||
Contour(placed, width, height);
|
||||
TraceRectangleFromRightMidpoint(placed, width, height);
|
||||
placed.Codes.Add(new LinearMove(width + 0.25, height / 2) { Layer = LayerType.Leadout });
|
||||
Assert.True(part.RestoreLeadInProgram(placed, false));
|
||||
part.CuttingParameters = new CuttingParameters();
|
||||
return part;
|
||||
}
|
||||
|
||||
private static void Contour(Program program, double width, double height)
|
||||
{
|
||||
program.LineTo(width, 0);
|
||||
program.LineTo(0, 0);
|
||||
program.LineTo(0, height);
|
||||
program.LineTo(width, height);
|
||||
program.LineTo(width, height / 2);
|
||||
}
|
||||
|
||||
// Independent replay uses private placements/drawings, never an event-wired plate
|
||||
// around the source drawings (which would change their quantity accounting).
|
||||
private static PostVerificationReport Analyze(params Part[] parts) => AnalyzeFrom(Vector.Zero, parts);
|
||||
|
||||
@@ -3,6 +3,7 @@ using OpenNest.CNC.CuttingPlanning;
|
||||
using OpenNest.CNC.CuttingStrategy;
|
||||
using OpenNest.Engine.CuttingPlanning;
|
||||
using OpenNest.Geometry;
|
||||
using static OpenNest.Tests.TestHelpers;
|
||||
|
||||
namespace OpenNest.Tests.CuttingPlanning;
|
||||
|
||||
@@ -42,7 +43,7 @@ public class ErodedPierceTests
|
||||
{
|
||||
var program = new Program();
|
||||
program.Merge(Rectangle(2, 1, 2.4, 1.05));
|
||||
program.Merge(Circle(5, 1.5, 0.03));
|
||||
program.Merge(MakeCircleProgram(5, 1.5, 0.03));
|
||||
program.Merge(Rectangle(0, 0, 10, 3));
|
||||
var part = new Part(new Drawing("tag", program));
|
||||
var parameters = new CuttingParameters
|
||||
@@ -89,7 +90,7 @@ public class ErodedPierceTests
|
||||
public void Planner_RoundHoleSmallerThanItsLeadIsPiercedAtItsCenter(double approach)
|
||||
{
|
||||
var program = new Program();
|
||||
program.Merge(Circle(2, 1.5, 0.09));
|
||||
program.Merge(MakeCircleProgram(2, 1.5, 0.09));
|
||||
program.Merge(Rectangle(0, 0, 4, 3));
|
||||
var part = new Part(new Drawing("plate", program));
|
||||
var parameters = new CuttingParameters
|
||||
@@ -244,14 +245,6 @@ public class ErodedPierceTests
|
||||
private static Program Rectangle(double x1, double y1, double x2, double y2) =>
|
||||
LeadPathValidationTests.Rectangle(x1, y1, x2, y2);
|
||||
|
||||
private static Program Circle(double x, double y, double radius)
|
||||
{
|
||||
var p = new Program();
|
||||
p.MoveTo(x + radius, y);
|
||||
p.ArcTo(x + radius, y, x, y, RotationType.CCW);
|
||||
return p;
|
||||
}
|
||||
|
||||
private static string Describe(CuttingPlanResult result) =>
|
||||
$"{result.Status}: " + string.Join("; ", result.Findings.Select(f => f.Message).Take(5));
|
||||
}
|
||||
@@ -2,6 +2,7 @@ using OpenNest.CNC;
|
||||
using OpenNest.CNC.CuttingPlanning;
|
||||
using OpenNest.CNC.CuttingStrategy;
|
||||
using OpenNest.Geometry;
|
||||
using static OpenNest.Tests.TestHelpers;
|
||||
|
||||
namespace OpenNest.Tests.CuttingPlanning;
|
||||
|
||||
@@ -79,7 +80,7 @@ public class LeadPathValidationTests
|
||||
var hole = LeadMaterialSnapshot.Capture(ring, Vector.Zero);
|
||||
Assert.True(hole.IsComplete, hole.Reason);
|
||||
AssertClear(ExternalLead(), target, hole);
|
||||
var tangent = LeadMaterialSnapshot.Capture(Circle(-1.5, 6, 1), Vector.Zero);
|
||||
var tangent = LeadMaterialSnapshot.Capture(MakeCircleProgram(-1.5, 6, 1), Vector.Zero);
|
||||
Assert.True(tangent.IsComplete, tangent.Reason);
|
||||
AssertUnsafe(ExternalLead(), target, tangent);
|
||||
var arc = new Program();
|
||||
@@ -87,7 +88,8 @@ public class LeadPathValidationTests
|
||||
arc.Codes.Add(new ArcMove(new Vector(0, 5), new Vector(-1, 5), RotationType.CW) { Layer = LayerType.Leadin });
|
||||
arc.LineTo(0, 10);
|
||||
AssertClear(arc, target);
|
||||
AssertUnsafe(arc, target, LeadMaterialSnapshot.Capture(Circle(-1, 7, 1), Vector.Zero));
|
||||
AssertUnsafe(arc, target,
|
||||
LeadMaterialSnapshot.Capture(MakeCircleProgram(-1, 7, 1), Vector.Zero));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -102,7 +104,7 @@ public class LeadPathValidationTests
|
||||
var arbitrary = ExternalLead();
|
||||
arbitrary.Codes[2] = new LinearMove(5, 5); // Adjacent move is NOT nominal contour.
|
||||
AssertUnsafe(arbitrary, target);
|
||||
var circular = LeadMaterialSnapshot.Capture(Circle(0, 0, 2), Vector.Zero);
|
||||
var circular = LeadMaterialSnapshot.Capture(MakeCircleProgram(0, 0, 2), Vector.Zero);
|
||||
var coincident = new Program();
|
||||
coincident.MoveTo(2, 0);
|
||||
coincident.Codes.Add(new ArcMove(new Vector(-2, 0), Vector.Zero) { Layer = LayerType.Leadin });
|
||||
@@ -124,7 +126,8 @@ public class LeadPathValidationTests
|
||||
public void TinyNativeUncertainContactRefusesRatherThanApproves()
|
||||
{
|
||||
var target = LeadMaterialSnapshot.Capture(Rectangle(0, 0, 10, 10), Vector.Zero);
|
||||
var obstacle = LeadMaterialSnapshot.Capture(Circle(-0.000005, 5, 0.000001), Vector.Zero);
|
||||
var obstacle = LeadMaterialSnapshot.Capture(
|
||||
MakeCircleProgram(-0.000005, 5, 0.000001), Vector.Zero);
|
||||
Assert.True(obstacle.IsComplete, obstacle.Reason);
|
||||
var p = ExternalLead(); p.Codes[0] = new RapidMove(-0.00001, 5);
|
||||
var check = LeadPathValidator.Check(Read(p), target, [obstacle]);
|
||||
@@ -152,7 +155,8 @@ public class LeadPathValidationTests
|
||||
var p = new Program(); p.MoveTo(0, 10); p.LineTo(0, 5);
|
||||
p.Codes.Add(new ArcMove(new Vector(-1, 4), new Vector(-1, 5), RotationType.CW) { Layer = LayerType.Leadout });
|
||||
AssertClear(p, target);
|
||||
AssertUnsafe(p, target, LeadMaterialSnapshot.Capture(Circle(-1, 3, 1), Vector.Zero));
|
||||
AssertUnsafe(p, target,
|
||||
LeadMaterialSnapshot.Capture(MakeCircleProgram(-1, 3, 1), Vector.Zero));
|
||||
p.Codes[^1] = new ArcMove(new Vector(-1, 4), new Vector(0, 4), RotationType.CW) { Layer = LayerType.Leadout };
|
||||
AssertUnsafe(p, target);
|
||||
}
|
||||
@@ -292,7 +296,7 @@ public class LeadPathValidationTests
|
||||
public void RealStrategyEmission_ClearExternalAndInternalLeads(string style)
|
||||
{
|
||||
var clean = ExplicitContourTests.Square(false);
|
||||
clean.Codes.AddRange(Circle(5, 5, 2).Codes);
|
||||
clean.Codes.AddRange(MakeCircleProgram(5, 5, 2).Codes);
|
||||
var target = LeadMaterialSnapshot.Capture(clean, Vector.Zero);
|
||||
Assert.True(target.IsComplete, target.Reason);
|
||||
var parameters = ExplicitContourTests.Parameters(style);
|
||||
@@ -310,7 +314,7 @@ public class LeadPathValidationTests
|
||||
public void RealStrategyEmission_UnsafeCompositePierceRefusesWithoutRepair()
|
||||
{
|
||||
var clean = ExplicitContourTests.Square(false);
|
||||
clean.Codes.AddRange(Circle(5, 5, 2).Codes);
|
||||
clean.Codes.AddRange(MakeCircleProgram(5, 5, 2).Codes);
|
||||
var target = LeadMaterialSnapshot.Capture(clean, Vector.Zero);
|
||||
var emitted = new ContourCuttingStrategy { Parameters = ExplicitContourTests.Parameters("linearc") }.Apply(clean, new Vector(-2, 5));
|
||||
AssertUnsafe(emitted.Program, target);
|
||||
@@ -319,11 +323,6 @@ public class LeadPathValidationTests
|
||||
AssertUnsafe(orphan, target);
|
||||
}
|
||||
|
||||
private static Program Circle(double x, double y, double radius)
|
||||
{
|
||||
var p = new Program(); p.MoveTo(x + radius, y); p.ArcTo(x + radius, y, x, y, RotationType.CCW); return p;
|
||||
}
|
||||
|
||||
private static void AssertClear(Program p, LeadMaterialSnapshot target, params LeadMaterialSnapshot[] others)
|
||||
{
|
||||
var result = LeadPathValidator.Check(Read(p), target, others);
|
||||
|
||||
@@ -4,6 +4,7 @@ using OpenNest.CNC.CuttingStrategy;
|
||||
using OpenNest.Diagnostics;
|
||||
using OpenNest.Engine.CuttingPlanning;
|
||||
using OpenNest.Geometry;
|
||||
using static OpenNest.Tests.TestHelpers;
|
||||
|
||||
namespace OpenNest.Tests.CuttingPlanning;
|
||||
|
||||
@@ -107,8 +108,8 @@ public class PierceZoneTests
|
||||
public void RoundHole_ShortLeadPiercesAtTheZoneEdge_TooSmallHolePiercesAtItsCenter()
|
||||
{
|
||||
var clean = Rectangle(0, 0, 10, 10);
|
||||
clean.Codes.AddRange(Circle(3, 5, 2).Codes);
|
||||
clean.Codes.AddRange(Circle(7.5, 5, 0.3).Codes);
|
||||
clean.Codes.AddRange(MakeCircleProgram(3, 5, 2).Codes);
|
||||
clean.Codes.AddRange(MakeCircleProgram(7.5, 5, 0.3).Codes);
|
||||
var target = Snapshot(clean);
|
||||
|
||||
var execution = Read(new ContourCuttingStrategy { Parameters = Parameters(1) }.Apply(clean, Start).Program);
|
||||
@@ -350,14 +351,6 @@ public class PierceZoneTests
|
||||
private static Program Rectangle(double x1, double y1, double x2, double y2) =>
|
||||
LeadPathValidationTests.Rectangle(x1, y1, x2, y2);
|
||||
|
||||
private static Program Circle(double x, double y, double radius)
|
||||
{
|
||||
var p = new Program();
|
||||
p.MoveTo(x + radius, y);
|
||||
p.ArcTo(x + radius, y, x, y, RotationType.CCW);
|
||||
return p;
|
||||
}
|
||||
|
||||
private static Program ExternalLead()
|
||||
{
|
||||
var p = new Program();
|
||||
|
||||
@@ -3,6 +3,7 @@ using OpenNest.CNC.CuttingPlanning;
|
||||
using OpenNest.Diagnostics;
|
||||
using OpenNest.Engine.CuttingPlanning;
|
||||
using OpenNest.Geometry;
|
||||
using static OpenNest.Tests.TestHelpers;
|
||||
|
||||
namespace OpenNest.Tests.CuttingPlanning;
|
||||
|
||||
@@ -111,7 +112,8 @@ public class SafestRouteFallbackTests
|
||||
{
|
||||
var plate = new Nest().CreatePlate();
|
||||
plate.Size = new Size(60, 60);
|
||||
plate.Parts.Add(new Part(new Drawing("bar", Rectangle(1, 50)), new Vector(25, 2)));
|
||||
plate.Parts.Add(new Part(
|
||||
new Drawing("bar", MakeClockwiseRectProgram(1, 50)), new Vector(25, 2)));
|
||||
plate.Parts.Add(new Part(new Drawing("left", ExplicitContourTests.Square(false)), new Vector(5, 20)));
|
||||
var right = new Part(new Drawing("right", ExplicitContourTests.Square(false)), new Vector(36, 20));
|
||||
if (lockRight)
|
||||
@@ -119,15 +121,4 @@ public class SafestRouteFallbackTests
|
||||
plate.Parts.Add(right);
|
||||
return plate;
|
||||
}
|
||||
|
||||
private static Program Rectangle(double width, double height)
|
||||
{
|
||||
var program = new Program();
|
||||
program.MoveTo(0, 0);
|
||||
program.LineTo(0, height);
|
||||
program.LineTo(width, height);
|
||||
program.LineTo(width, 0);
|
||||
program.LineTo(0, 0);
|
||||
return program;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using OpenNest.CNC;
|
||||
using OpenNest.Diagnostics;
|
||||
using OpenNest.Geometry;
|
||||
using static OpenNest.Tests.TestHelpers;
|
||||
|
||||
namespace OpenNest.Tests.Diagnostics;
|
||||
|
||||
@@ -96,19 +97,8 @@ public class OverlapAutoCheckSchedulerTests
|
||||
private static Plate PlateWithParts()
|
||||
{
|
||||
var plate = new Plate();
|
||||
plate.Parts.Add(Rectangle());
|
||||
plate.Parts.Add(Rectangle());
|
||||
plate.Parts.Add(MakeSquarePart("same name"));
|
||||
plate.Parts.Add(MakeSquarePart("same name"));
|
||||
return plate;
|
||||
}
|
||||
|
||||
private static Part Rectangle()
|
||||
{
|
||||
var program = new Program(Mode.Absolute);
|
||||
program.Codes.Add(new RapidMove(0, 0));
|
||||
program.Codes.Add(new LinearMove(4, 0));
|
||||
program.Codes.Add(new LinearMove(4, 4));
|
||||
program.Codes.Add(new LinearMove(0, 4));
|
||||
program.Codes.Add(new LinearMove(0, 0));
|
||||
return new Part(new Drawing("same name", program));
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ using System.Globalization;
|
||||
using OpenNest.CNC;
|
||||
using OpenNest.Diagnostics;
|
||||
using OpenNest.Geometry;
|
||||
using static OpenNest.Tests.TestHelpers;
|
||||
|
||||
namespace OpenNest.Tests.Diagnostics;
|
||||
|
||||
@@ -10,7 +11,7 @@ public class OverlapHoverPagesTests
|
||||
[Fact]
|
||||
public void CrowdedCoincidentPairsRemainAccessibleInOrderWithinEveryPageBudget()
|
||||
{
|
||||
var parts = Enumerable.Range(1, 12).Select(i => Rectangle($"part-{i}")).ToArray();
|
||||
var parts = Enumerable.Range(1, 12).Select(i => MakeSquarePart($"part-{i}")).ToArray();
|
||||
var report = PlateOverlapAnalyzer.Analyze(parts);
|
||||
var pairs = OverlapPairPresentation.HitTest(report.Pairs.Reverse(), p => p, new Vector(2, 2), 96);
|
||||
var text = string.Join("\n\n", pairs.Select(p => OverlapPairPresentation.Details(p, Units.Millimeters, CultureInfo.InvariantCulture)));
|
||||
@@ -35,7 +36,8 @@ public class OverlapHoverPagesTests
|
||||
public void OneDetailWithLongUnbrokenNamesPagesWithoutDroppingText(int rows, int width)
|
||||
{
|
||||
var name = string.Concat(Enumerable.Repeat("veryLongName😀e\u0301", 30));
|
||||
var pair = Assert.Single(PlateOverlapAnalyzer.Analyze(new[] { Rectangle(name), Rectangle("tail") }).Pairs);
|
||||
var pair = Assert.Single(PlateOverlapAnalyzer.Analyze(
|
||||
new[] { MakeSquarePart(name), MakeSquarePart("tail") }).Pairs);
|
||||
var text = OverlapPairPresentation.Details(pair, Units.Inches, CultureInfo.InvariantCulture);
|
||||
var pages = OverlapHoverPages.Create(text, 1, rows, width, s => new StringInfo(s).LengthInTextElements);
|
||||
|
||||
@@ -116,15 +118,4 @@ public class OverlapHoverPagesTests
|
||||
pages.MovePage(1);
|
||||
}
|
||||
}
|
||||
|
||||
private static Part Rectangle(string name)
|
||||
{
|
||||
var program = new Program(Mode.Absolute);
|
||||
program.Codes.Add(new RapidMove(0, 0));
|
||||
program.Codes.Add(new LinearMove(4, 0));
|
||||
program.Codes.Add(new LinearMove(4, 4));
|
||||
program.Codes.Add(new LinearMove(0, 4));
|
||||
program.Codes.Add(new LinearMove(0, 0));
|
||||
return new Part(new Drawing(name, program));
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ using System.Globalization;
|
||||
using OpenNest.CNC;
|
||||
using OpenNest.Diagnostics;
|
||||
using OpenNest.Geometry;
|
||||
using static OpenNest.Tests.TestHelpers;
|
||||
|
||||
namespace OpenNest.Tests.Diagnostics;
|
||||
|
||||
@@ -12,7 +13,10 @@ public class OverlapPairPresentationTests
|
||||
[InlineData(Units.Millimeters, "mm")]
|
||||
public void DetailsUseCapturedNamesSequenceIdsAndSuppliedUnits(Units units, string symbol)
|
||||
{
|
||||
var parts = new[] { Rectangle("cutoff"), Rectangle("left"), Rectangle("right") };
|
||||
var parts = new[]
|
||||
{
|
||||
MakeSquarePart("cutoff"), MakeSquarePart("left"), MakeSquarePart("right"),
|
||||
};
|
||||
parts[0].BaseDrawing.IsCutOff = true;
|
||||
var snapshot = PlateOverlapAnalyzer.Capture(parts);
|
||||
parts[1].BaseDrawing.Name = "changed";
|
||||
@@ -41,7 +45,8 @@ public class OverlapPairPresentationTests
|
||||
{
|
||||
Assert.Equal(6 * factor, OverlapPairPresentation.MarkerHalfSize(dpi));
|
||||
Assert.Equal(10 * factor, OverlapPairPresentation.HitRadius(dpi));
|
||||
var pairs = PlateOverlapAnalyzer.Analyze(new[] { Rectangle("a"), Rectangle("b") }).Pairs;
|
||||
var pairs = PlateOverlapAnalyzer.Analyze(
|
||||
new[] { MakeSquarePart("a"), MakeSquarePart("b") }).Pairs;
|
||||
foreach (var zoom in new[] { 0.5, 20.0, 500.0 })
|
||||
{
|
||||
Vector ToScreen(Vector world) => new(100 + world.X * zoom, 200 - world.Y * zoom);
|
||||
@@ -59,20 +64,10 @@ public class OverlapPairPresentationTests
|
||||
[Fact]
|
||||
public void CoincidentMarkersReturnEveryPairInIdOrderNotFragmentOrder()
|
||||
{
|
||||
var pairs = PlateOverlapAnalyzer.Analyze(new[] { Rectangle("a"), Rectangle("b"), Rectangle("c") }).Pairs;
|
||||
var pairs = PlateOverlapAnalyzer.Analyze(
|
||||
new[] { MakeSquarePart("a"), MakeSquarePart("b"), MakeSquarePart("c") }).Pairs;
|
||||
var hits = OverlapPairPresentation.HitTest(pairs.Reverse(), p => p, new Vector(2, 2), 96);
|
||||
Assert.Equal(new[] { "1/2", "1/3", "2/3" }, hits.Select(OverlapPairPresentation.Label));
|
||||
Assert.All(hits, p => Assert.Single(p.Regions)); // One island per coincident pair.
|
||||
}
|
||||
|
||||
private static Part Rectangle(string name)
|
||||
{
|
||||
var program = new Program(Mode.Absolute);
|
||||
program.Codes.Add(new RapidMove(0, 0));
|
||||
program.Codes.Add(new LinearMove(4, 0));
|
||||
program.Codes.Add(new LinearMove(4, 4));
|
||||
program.Codes.Add(new LinearMove(0, 4));
|
||||
program.Codes.Add(new LinearMove(0, 0));
|
||||
return new Part(new Drawing(name, program));
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using OpenNest.CNC;
|
||||
using OpenNest.Diagnostics;
|
||||
using OpenNest.Geometry;
|
||||
using static OpenNest.Tests.TestHelpers;
|
||||
|
||||
namespace OpenNest.Tests.Diagnostics;
|
||||
|
||||
@@ -51,10 +52,10 @@ public class OverlapReportStateTests
|
||||
case "rotate": part.Rotate(0.01); break;
|
||||
case "reorder": (plate.Parts[0], plate.Parts[1]) = (plate.Parts[1], plate.Parts[0]); break;
|
||||
case "replace-part": plate.Parts[0] = part.CloneAtOffset(new Vector()); break;
|
||||
case "replace-drawing": plate.Parts[0] = Rectangle(); break;
|
||||
case "replace-drawing": plate.Parts[0] = MakeSquarePart("same name"); break;
|
||||
case "placed-program": part.Update(); break;
|
||||
case "clean-program": part.BaseDrawing.Program = (Program)part.BaseDrawing.Program.Clone(); break;
|
||||
case "add": plate.Parts.Add(Rectangle()); break;
|
||||
case "add": plate.Parts.Add(MakeSquarePart("same name")); break;
|
||||
case "remove": plate.Parts.RemoveAt(0); break;
|
||||
case "plate":
|
||||
var replacement = new Plate();
|
||||
@@ -178,7 +179,7 @@ public class OverlapReportStateTests
|
||||
public void CutoffSlotsRemainPartOfTheOrderedIdentityStamp()
|
||||
{
|
||||
var plate = PlateWithParts();
|
||||
var cutoff = Rectangle();
|
||||
var cutoff = MakeSquarePart("same name");
|
||||
cutoff.BaseDrawing.IsCutOff = true;
|
||||
plate.Parts.Insert(0, cutoff);
|
||||
var state = new OverlapReportState();
|
||||
@@ -269,7 +270,7 @@ public class OverlapReportStateTests
|
||||
case "placed-program": part.Update(); break;
|
||||
case "clean-program": part.BaseDrawing.Program = (Program)part.BaseDrawing.Program.Clone(); break;
|
||||
case "cutoff": part.BaseDrawing.IsCutOff = true; break;
|
||||
case "replace": plate.Parts[0] = Rectangle(); break;
|
||||
case "replace": plate.Parts[0] = MakeSquarePart("same name"); break;
|
||||
case "remove": plate.Parts.RemoveAt(0); break;
|
||||
case "reorder": (plate.Parts[0], plate.Parts[1]) = (plate.Parts[1], plate.Parts[0]); break;
|
||||
}
|
||||
@@ -332,7 +333,7 @@ public class OverlapReportStateTests
|
||||
var state = new OverlapReportState();
|
||||
Assert.True(state.TryPublish(state.Begin(plate), plate, Analyze(plate)));
|
||||
var display = state.DisplayPairs;
|
||||
plate.Parts.Add(Rectangle());
|
||||
plate.Parts.Add(MakeSquarePart("same name"));
|
||||
state.Invalidate(plate); // Same entry point as the controller's collection event.
|
||||
Assert.Null(state.Report);
|
||||
Assert.Equal(OverlapCheckStatus.Stale, state.Status);
|
||||
@@ -345,8 +346,8 @@ public class OverlapReportStateTests
|
||||
private static Plate PlateWithTwoPairs()
|
||||
{
|
||||
var plate = PlateWithParts();
|
||||
var a = Rectangle();
|
||||
var b = Rectangle();
|
||||
var a = MakeSquarePart("same name");
|
||||
var b = MakeSquarePart("same name");
|
||||
a.Offset(20, 0);
|
||||
b.Offset(20, 0);
|
||||
plate.Parts.Add(a);
|
||||
@@ -359,19 +360,8 @@ public class OverlapReportStateTests
|
||||
private static Plate PlateWithParts()
|
||||
{
|
||||
var plate = new Plate();
|
||||
plate.Parts.Add(Rectangle());
|
||||
plate.Parts.Add(Rectangle());
|
||||
plate.Parts.Add(MakeSquarePart("same name"));
|
||||
plate.Parts.Add(MakeSquarePart("same name"));
|
||||
return plate;
|
||||
}
|
||||
|
||||
private static Part Rectangle()
|
||||
{
|
||||
var program = new Program(Mode.Absolute);
|
||||
program.Codes.Add(new RapidMove(0, 0));
|
||||
program.Codes.Add(new LinearMove(4, 0));
|
||||
program.Codes.Add(new LinearMove(4, 4));
|
||||
program.Codes.Add(new LinearMove(0, 4));
|
||||
program.Codes.Add(new LinearMove(0, 0));
|
||||
return new Part(new Drawing("same name", program));
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ using OpenNest.CNC;
|
||||
using OpenNest.Diagnostics;
|
||||
using OpenNest.Engine.Jobs;
|
||||
using OpenNest.Geometry;
|
||||
using static OpenNest.Tests.TestHelpers;
|
||||
|
||||
namespace OpenNest.Tests.Diagnostics;
|
||||
|
||||
@@ -66,8 +67,8 @@ public class PartSpacingReviewTests
|
||||
public void PartInsideCutout_IsHeldToSpacingFromTheHoleEdge(double insert, bool warns)
|
||||
{
|
||||
var frame = new Program();
|
||||
frame.Codes.AddRange(SquareProgram(0, 0, 10).Codes);
|
||||
frame.Codes.AddRange(SquareProgram(2, 2, 6).Codes);
|
||||
frame.Codes.AddRange(MakeSquareProgram(0, 0, 10).Codes);
|
||||
frame.Codes.AddRange(MakeSquareProgram(2, 2, 6).Codes);
|
||||
var report = Analyze(Spacing, new Part(new Drawing("frame", frame)), Square(insert, insert, 2));
|
||||
|
||||
Assert.Equal(warns, Find(report, PostVerificationKind.PartSpacing).Length == 1);
|
||||
@@ -189,18 +190,7 @@ public class PartSpacingReviewTests
|
||||
}
|
||||
|
||||
private static Part Square(double x, double y, double size, string name = "square") =>
|
||||
new(new Drawing(name, SquareProgram(0, 0, size)), new Vector(x, y));
|
||||
|
||||
private static Program SquareProgram(double x, double y, double size)
|
||||
{
|
||||
var program = new Program();
|
||||
program.MoveTo(x, y);
|
||||
program.LineTo(x + size, y);
|
||||
program.LineTo(x + size, y + size);
|
||||
program.LineTo(x, y + size);
|
||||
program.LineTo(x, y);
|
||||
return program;
|
||||
}
|
||||
new(new Drawing(name, MakeSquareProgram(0, 0, size)), new Vector(x, y));
|
||||
|
||||
private static Program Rectangle(double width, double height)
|
||||
{
|
||||
|
||||
@@ -4,6 +4,7 @@ using OpenNest.CNC.CuttingStrategy;
|
||||
using OpenNest.Diagnostics;
|
||||
using OpenNest.Geometry;
|
||||
using OpenNest.IO;
|
||||
using static OpenNest.Tests.TestHelpers;
|
||||
|
||||
namespace OpenNest.Tests.Diagnostics;
|
||||
|
||||
@@ -50,8 +51,8 @@ public class PostVerificationAnalyzerTests
|
||||
[Fact]
|
||||
public void Overlap_SubtractsHoles()
|
||||
{
|
||||
var program = Square(0, 0, 10);
|
||||
program.Codes.AddRange(Square(2, 2, 6).Codes);
|
||||
var program = MakeSquareProgram(0, 0, 10);
|
||||
program.Codes.AddRange(MakeSquareProgram(2, 2, 6).Codes);
|
||||
var frame = Part(program);
|
||||
var report = Analyze(frame, Rectangle(3, 3, 1));
|
||||
Assert.Empty(Find(report, PostVerificationKind.Overlap));
|
||||
@@ -91,8 +92,8 @@ public class PostVerificationAnalyzerTests
|
||||
[Fact]
|
||||
public void MissingLeadIn_PartialApplySingleLeavesOtherContourUncovered()
|
||||
{
|
||||
var program = Square(0, 0, 10);
|
||||
program.Codes.AddRange(Square(2, 2, 2).Codes);
|
||||
var program = MakeSquareProgram(0, 0, 10);
|
||||
program.Codes.AddRange(MakeSquareProgram(2, 2, 2).Codes);
|
||||
var part = Part(program);
|
||||
part.ApplySingleLeadIn(Leads(), Vector.Zero,
|
||||
new Line(Vector.Zero, new Vector(10, 0)), ContourType.External);
|
||||
@@ -377,7 +378,7 @@ public class PostVerificationAnalyzerTests
|
||||
public void Rapid_NonzeroInitialLeadRapidIsNotAppliedTwice(Mode mode)
|
||||
{
|
||||
var part = Rectangle(100, 50, 4);
|
||||
var placed = Square(0, 0, 4);
|
||||
var placed = MakeSquareProgram(0, 0, 4);
|
||||
placed.Codes.Insert(0, new RapidMove(-2, 0));
|
||||
placed.Codes[1] = new LinearMove(0, 0) { Layer = LayerType.Leadin };
|
||||
placed.MoveTo(-2, 2);
|
||||
@@ -408,7 +409,7 @@ public class PostVerificationAnalyzerTests
|
||||
var placed = new Program();
|
||||
placed.MoveTo(-2, 2);
|
||||
placed.MoveTo(6, 2);
|
||||
placed.Codes.AddRange(Square(0, 0, 4).Codes);
|
||||
placed.Codes.AddRange(MakeSquareProgram(0, 0, 4).Codes);
|
||||
part.RestoreLeadInProgram(placed, false);
|
||||
Assert.Empty(Find(Analyze(part), PostVerificationKind.RapidCrossing));
|
||||
}
|
||||
@@ -472,7 +473,7 @@ public class PostVerificationAnalyzerTests
|
||||
public void Rapid_ResumedContourBecomesObstacleOnlyAfterItsLastCut()
|
||||
{
|
||||
var part = Rectangle(0, 0, 4);
|
||||
var placed = Square(0, 0, 4);
|
||||
var placed = MakeSquareProgram(0, 0, 4);
|
||||
placed.Codes.Insert(3, new RapidMove(4, 4)); // Pause, with no uncut gap.
|
||||
placed.MoveTo(-2, 2);
|
||||
placed.MoveTo(6, 2);
|
||||
@@ -486,7 +487,7 @@ public class PostVerificationAnalyzerTests
|
||||
public void Rapid_InternalGapDoesNotBecomeAnObstacle(bool staleFlag)
|
||||
{
|
||||
var part = Rectangle(0, 0, 4);
|
||||
var placed = Square(0, 0, 4);
|
||||
var placed = MakeSquareProgram(0, 0, 4);
|
||||
placed.Codes.Insert(3, new RapidMove(3.75, 4)); // Leave a gap on the top edge.
|
||||
placed.MoveTo(-2, 2);
|
||||
placed.MoveTo(6, 2);
|
||||
@@ -568,7 +569,7 @@ public class PostVerificationAnalyzerTests
|
||||
public void Rapid_OpenCutsAcrossAClosedContourAreIncomplete()
|
||||
{
|
||||
var part = Rectangle(0, 0, 10);
|
||||
var placed = Square(0, 0, 10);
|
||||
var placed = MakeSquareProgram(0, 0, 10);
|
||||
// Two cuts from edge to edge release the strip between them.
|
||||
placed.MoveTo(0, 3);
|
||||
placed.LineTo(10, 3);
|
||||
@@ -597,7 +598,7 @@ public class PostVerificationAnalyzerTests
|
||||
{
|
||||
var part = Rectangle(100, 50, 20);
|
||||
var sub = CircleWithLead();
|
||||
var program = Square(0, 0, 20);
|
||||
var program = MakeSquareProgram(0, 0, 20);
|
||||
program.Codes.Insert(0, new SubProgramCall { Program = sub, Offset = new Vector(5, 5) });
|
||||
program.Codes.Insert(1, new SubProgramCall { Program = sub, Offset = new Vector(15, 5) });
|
||||
program.Mode = Mode.Incremental;
|
||||
@@ -647,25 +648,14 @@ public class PostVerificationAnalyzerTests
|
||||
private static Part Part(Program program) => new(new Drawing("fixture", program));
|
||||
|
||||
private static Part Rectangle(double x, double y, double size) =>
|
||||
new(new Drawing("rectangle", Square(0, 0, size)), new Vector(x, y));
|
||||
|
||||
private static Program Square(double x, double y, double size)
|
||||
{
|
||||
var program = new Program();
|
||||
program.MoveTo(x, y);
|
||||
program.LineTo(x + size, y);
|
||||
program.LineTo(x + size, y + size);
|
||||
program.LineTo(x, y + size);
|
||||
program.LineTo(x, y);
|
||||
return program;
|
||||
}
|
||||
new(new Drawing("rectangle", MakeSquareProgram(0, 0, size)), new Vector(x, y));
|
||||
|
||||
// A 10x10 part whose 4x4 cutout is cut in two pieces, leaving 0.2 tabs on its left and
|
||||
// right edges; leads enter and leave each piece from inside the slug.
|
||||
private static Part TabbedCutout(bool leads)
|
||||
{
|
||||
var clean = Square(3, 3, 4);
|
||||
clean.Codes.AddRange(Square(0, 0, 10).Codes);
|
||||
var clean = MakeSquareProgram(3, 3, 4);
|
||||
clean.Codes.AddRange(MakeSquareProgram(0, 0, 10).Codes);
|
||||
var part = new Part(new Drawing("tabbed", clean));
|
||||
var program = new Program();
|
||||
if (leads)
|
||||
@@ -691,7 +681,7 @@ public class PostVerificationAnalyzerTests
|
||||
program.LineTo(3, 4.9);
|
||||
if (leads)
|
||||
program.Codes.Add(new LinearMove(3.2, 4.7) { Layer = LayerType.Leadout });
|
||||
program.Codes.AddRange(Square(0, 0, 10).Codes);
|
||||
program.Codes.AddRange(MakeSquareProgram(0, 0, 10).Codes);
|
||||
part.RestoreLeadInProgram(program, false);
|
||||
return part;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ using OpenNest.Tests.BestFit;
|
||||
using Xunit.Abstractions;
|
||||
using OpenNest.Engine.Fill;
|
||||
using OpenNest.Geometry;
|
||||
using static OpenNest.Tests.TestHelpers;
|
||||
|
||||
namespace OpenNest.Tests.Fill;
|
||||
|
||||
@@ -223,7 +224,7 @@ public class FillExtentsTests
|
||||
[InlineData(15, 10)] // One rectangle fits, the pair does not.
|
||||
public void Fill_NoFit_MatchesFrozenLegacy(double length, double width)
|
||||
{
|
||||
var drawing = MakeRect(10, 8);
|
||||
var drawing = MakeRectDrawing(10, 8);
|
||||
var before = Snapshot(drawing);
|
||||
var area = new Box(3, 5, length, width);
|
||||
var expected = new LegacyFillExtents(area, 0.5).Fill(drawing);
|
||||
@@ -433,7 +434,7 @@ public class FillExtentsTests
|
||||
internal static Drawing MakeFixture(string shape)
|
||||
{
|
||||
if (shape == "rectangle")
|
||||
return MakeRect(10, 8);
|
||||
return MakeRectDrawing(10, 8);
|
||||
if (shape == "triangle")
|
||||
return MakeRightTriangle(10, 8);
|
||||
var program = new Program();
|
||||
@@ -525,17 +526,6 @@ public class FillExtentsTests
|
||||
return new Drawing("triangle", pgm);
|
||||
}
|
||||
|
||||
private static Drawing MakeRect(double w, double h)
|
||||
{
|
||||
var pgm = new Program();
|
||||
pgm.Codes.Add(new RapidMove(new Vector(0, 0)));
|
||||
pgm.Codes.Add(new LinearMove(new Vector(w, 0)));
|
||||
pgm.Codes.Add(new LinearMove(new Vector(w, h)));
|
||||
pgm.Codes.Add(new LinearMove(new Vector(0, h)));
|
||||
pgm.Codes.Add(new LinearMove(new Vector(0, 0)));
|
||||
return new Drawing("rect", pgm);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Fill_Triangle_ReturnsPartsWithinWorkArea()
|
||||
{
|
||||
@@ -566,7 +556,7 @@ public class FillExtentsTests
|
||||
{
|
||||
var workArea = new Box(0, 0, 5, 5);
|
||||
var filler = new FillExtents(workArea, 0.5);
|
||||
var drawing = MakeRect(10, 10);
|
||||
var drawing = MakeRectDrawing(10, 10);
|
||||
|
||||
var parts = filler.Fill(drawing);
|
||||
|
||||
@@ -631,7 +621,7 @@ public class FillExtentsTests
|
||||
{
|
||||
var workArea = new Box(0, 0, 120, 60);
|
||||
var filler = new FillExtents(workArea, 0.5);
|
||||
var drawing = MakeRect(15, 10);
|
||||
var drawing = MakeRectDrawing(15, 10);
|
||||
|
||||
var parts = filler.Fill(drawing);
|
||||
|
||||
|
||||
@@ -17,35 +17,11 @@ public class PairOverlapDiagnosticTests
|
||||
/// Creates a 5x3.31 rectangle with rounded corners on the top-right and bottom-right
|
||||
/// (radius 0.5), similar to "4526 A14 PT13".
|
||||
/// </summary>
|
||||
private static Drawing MakeRoundedRect(double w = 5.0, double h = 3.31, double r = 0.5)
|
||||
{
|
||||
var pgm = new Program();
|
||||
pgm.Codes.Add(new RapidMove(new Vector(0, 0)));
|
||||
// Bottom edge
|
||||
pgm.Codes.Add(new LinearMove(new Vector(w - r, 0)));
|
||||
// Bottom-right rounded corner
|
||||
pgm.Codes.Add(new ArcMove(new Vector(w, r), new Vector(w - r, r), RotationType.CW));
|
||||
// Right edge
|
||||
pgm.Codes.Add(new LinearMove(new Vector(w, h - r)));
|
||||
// Top-right rounded corner
|
||||
pgm.Codes.Add(new ArcMove(new Vector(w - r, h), new Vector(w - r, h - r), RotationType.CW));
|
||||
// Top edge
|
||||
pgm.Codes.Add(new LinearMove(new Vector(0, h)));
|
||||
// Left edge back to start
|
||||
pgm.Codes.Add(new LinearMove(new Vector(0, 0)));
|
||||
return new Drawing("rounded-rect", pgm);
|
||||
}
|
||||
private static Drawing MakeRoundedRect(double w = 5.0, double h = 3.31, double r = 0.5) =>
|
||||
TestHelpers.MakeRoundedRectDrawing(w, h, r);
|
||||
|
||||
private static Drawing MakeSimpleRect(double w = 5.0, double h = 3.31)
|
||||
{
|
||||
var pgm = new Program();
|
||||
pgm.Codes.Add(new RapidMove(new Vector(0, 0)));
|
||||
pgm.Codes.Add(new LinearMove(new Vector(w, 0)));
|
||||
pgm.Codes.Add(new LinearMove(new Vector(w, h)));
|
||||
pgm.Codes.Add(new LinearMove(new Vector(0, h)));
|
||||
pgm.Codes.Add(new LinearMove(new Vector(0, 0)));
|
||||
return new Drawing("rect", pgm);
|
||||
}
|
||||
private static Drawing MakeSimpleRect(double w = 5.0, double h = 3.31) =>
|
||||
TestHelpers.MakeRectDrawing(w, h);
|
||||
|
||||
[Theory]
|
||||
[InlineData(0)] // 0 degrees
|
||||
|
||||
@@ -98,6 +98,88 @@ internal static class TestHelpers
|
||||
return new Drawing("square", pgm);
|
||||
}
|
||||
|
||||
/// <summary>A full circle of the given radius around (x, y), starting at its right.</summary>
|
||||
public static Program MakeCircleProgram(double x, double y, double radius)
|
||||
{
|
||||
var p = new Program();
|
||||
p.MoveTo(x + radius, y);
|
||||
p.ArcTo(x + radius, y, x, y, RotationType.CCW);
|
||||
return p;
|
||||
}
|
||||
|
||||
/// <summary>A counterclockwise size x size square, lower-left corner at (x, y).</summary>
|
||||
public static Program MakeSquareProgram(double x, double y, double size)
|
||||
{
|
||||
var program = new Program();
|
||||
program.MoveTo(x, y);
|
||||
program.LineTo(x + size, y);
|
||||
program.LineTo(x + size, y + size);
|
||||
program.LineTo(x, y + size);
|
||||
program.LineTo(x, y);
|
||||
return program;
|
||||
}
|
||||
|
||||
/// <summary>A width x height rectangle from the origin, clockwise: up first, then +X.</summary>
|
||||
public static Program MakeClockwiseRectProgram(double width, double height)
|
||||
{
|
||||
var program = new Program();
|
||||
program.MoveTo(0, 0);
|
||||
program.LineTo(0, height);
|
||||
program.LineTo(width, height);
|
||||
program.LineTo(width, 0);
|
||||
program.LineTo(0, 0);
|
||||
return program;
|
||||
}
|
||||
|
||||
/// <summary>A closed contour through the vertices in order, back to the first one.</summary>
|
||||
public static Program MakeClosedProgram(IEnumerable<Vector> vertices)
|
||||
{
|
||||
var p = new Program();
|
||||
p.MoveTo(vertices.First().X, vertices.First().Y);
|
||||
foreach (var v in vertices.Skip(1).Append(vertices.First()))
|
||||
p.LineTo(v.X, v.Y);
|
||||
return p;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Appends a clockwise width x height rectangle that starts and ends at the midpoint of its
|
||||
/// right edge, where <paramref name="program"/> must already be.
|
||||
/// </summary>
|
||||
public static void TraceRectangleFromRightMidpoint(Program program, double width, double height)
|
||||
{
|
||||
program.LineTo(width, 0);
|
||||
program.LineTo(0, 0);
|
||||
program.LineTo(0, height);
|
||||
program.LineTo(width, height);
|
||||
program.LineTo(width, height / 2);
|
||||
}
|
||||
|
||||
/// <summary>A part of a 4 x 4 absolute-mode square drawing with the given name.</summary>
|
||||
public static Part MakeSquarePart(string name)
|
||||
{
|
||||
var program = new Program(Mode.Absolute);
|
||||
program.Codes.Add(new RapidMove(0, 0));
|
||||
program.Codes.Add(new LinearMove(4, 0));
|
||||
program.Codes.Add(new LinearMove(4, 4));
|
||||
program.Codes.Add(new LinearMove(0, 4));
|
||||
program.Codes.Add(new LinearMove(0, 0));
|
||||
return new Part(new Drawing(name, program));
|
||||
}
|
||||
|
||||
/// <summary>A w x h outline whose two right-hand corners are arcs of radius r.</summary>
|
||||
public static Drawing MakeRoundedRectDrawing(double w, double h, double r)
|
||||
{
|
||||
var pgm = new Program();
|
||||
pgm.Codes.Add(new RapidMove(new Vector(0, 0)));
|
||||
pgm.Codes.Add(new LinearMove(new Vector(w - r, 0)));
|
||||
pgm.Codes.Add(new ArcMove(new Vector(w, r), new Vector(w - r, r), RotationType.CW));
|
||||
pgm.Codes.Add(new LinearMove(new Vector(w, h - r)));
|
||||
pgm.Codes.Add(new ArcMove(new Vector(w - r, h), new Vector(w - r, h - r), RotationType.CW));
|
||||
pgm.Codes.Add(new LinearMove(new Vector(0, h)));
|
||||
pgm.Codes.Add(new LinearMove(new Vector(0, 0)));
|
||||
return new Drawing("rounded-rect", pgm);
|
||||
}
|
||||
|
||||
public static Drawing MakeFrameDrawing(double w = 20, double h = 6, double t = 0.5)
|
||||
{
|
||||
// Thin-walled hollow frame: outer CW, inner cutout CCW (CNC convention).
|
||||
|
||||
Reference in new issue
Block a user