From f0d84e041cb543f226317db5dd72029f0d493835 Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Sun, 1 Nov 2026 10:50:50 -0500 Subject: [PATCH] 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 --- OpenNest.Tests/BestFit/BestFitOverlapTests.cs | 14 +--- .../ContourEntryCandidateTests.cs | 22 ++--- .../ContourEntryRankingTests.cs | 35 ++++---- .../CuttingCaptureRotationTests.cs | 16 +--- .../CuttingPlanning/CuttingDependencyTests.cs | 14 +--- .../CuttingPlanning/CuttingPlanCommitTests.cs | 14 +--- .../CuttingPlanServiceTests.cs | 14 +--- .../CuttingPlanning/ErodedPierceTests.cs | 13 +-- .../LeadPathValidationTests.cs | 23 +++--- .../CuttingPlanning/PierceZoneTests.cs | 13 +-- .../SafestRouteFallbackTests.cs | 15 +--- .../OverlapAutoCheckSchedulerTests.cs | 16 +--- .../Diagnostics/OverlapHoverPagesTests.cs | 17 +--- .../OverlapPairPresentationTests.cs | 23 ++---- .../Diagnostics/OverlapReportStateTests.cs | 30 +++---- .../Diagnostics/PartSpacingReviewTests.cs | 18 +--- .../PostVerificationAnalyzerTests.cs | 40 ++++----- OpenNest.Tests/Fill/FillExtentsTests.cs | 20 ++--- .../Fill/PairOverlapDiagnosticTests.cs | 32 +------- OpenNest.Tests/TestHelpers.cs | 82 +++++++++++++++++++ 20 files changed, 193 insertions(+), 278 deletions(-) diff --git a/OpenNest.Tests/BestFit/BestFitOverlapTests.cs b/OpenNest.Tests/BestFit/BestFitOverlapTests.cs index b87e47ba..dc9136ba 100644 --- a/OpenNest.Tests/BestFit/BestFitOverlapTests.cs +++ b/OpenNest.Tests/BestFit/BestFitOverlapTests.cs @@ -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() { diff --git a/OpenNest.Tests/CuttingPlanning/ContourEntryCandidateTests.cs b/OpenNest.Tests/CuttingPlanning/ContourEntryCandidateTests.cs index b05e126a..a31200c9 100644 --- a/OpenNest.Tests/CuttingPlanning/ContourEntryCandidateTests.cs +++ b/OpenNest.Tests/CuttingPlanning/ContourEntryCandidateTests.cs @@ -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 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; - } - /// Square perimeter with an L-notch hole slug; its concave vertex (3,3.5) is reflex for the slug's own travel. 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), diff --git a/OpenNest.Tests/CuttingPlanning/ContourEntryRankingTests.cs b/OpenNest.Tests/CuttingPlanning/ContourEntryRankingTests.cs index 3a54af6c..105bce94 100644 --- a/OpenNest.Tests/CuttingPlanning/ContourEntryRankingTests.cs +++ b/OpenNest.Tests/CuttingPlanning/ContourEntryRankingTests.cs @@ -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 --------------------------------------------------------------- - /// Square 0..10 in travel order, optionally cyclically reindexed or reversed. - private static Program Square(IEnumerable 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(() => 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)); diff --git a/OpenNest.Tests/CuttingPlanning/CuttingCaptureRotationTests.cs b/OpenNest.Tests/CuttingPlanning/CuttingCaptureRotationTests.cs index 84266709..8a3efee2 100644 --- a/OpenNest.Tests/CuttingPlanning/CuttingCaptureRotationTests.cs +++ b/OpenNest.Tests/CuttingPlanning/CuttingCaptureRotationTests.cs @@ -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; - } } diff --git a/OpenNest.Tests/CuttingPlanning/CuttingDependencyTests.cs b/OpenNest.Tests/CuttingPlanning/CuttingDependencyTests.cs index 35832fa6..fbb67f0d 100644 --- a/OpenNest.Tests/CuttingPlanning/CuttingDependencyTests.cs +++ b/OpenNest.Tests/CuttingPlanning/CuttingDependencyTests.cs @@ -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)); } diff --git a/OpenNest.Tests/CuttingPlanning/CuttingPlanCommitTests.cs b/OpenNest.Tests/CuttingPlanning/CuttingPlanCommitTests.cs index 95f8b503..cef3744d 100644 --- a/OpenNest.Tests/CuttingPlanning/CuttingPlanCommitTests.cs +++ b/OpenNest.Tests/CuttingPlanning/CuttingPlanCommitTests.cs @@ -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); - } } diff --git a/OpenNest.Tests/CuttingPlanning/CuttingPlanServiceTests.cs b/OpenNest.Tests/CuttingPlanning/CuttingPlanServiceTests.cs index 58fb992c..f8e4a13b 100644 --- a/OpenNest.Tests/CuttingPlanning/CuttingPlanServiceTests.cs +++ b/OpenNest.Tests/CuttingPlanning/CuttingPlanServiceTests.cs @@ -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); diff --git a/OpenNest.Tests/CuttingPlanning/ErodedPierceTests.cs b/OpenNest.Tests/CuttingPlanning/ErodedPierceTests.cs index 262e4f7c..c5c76114 100644 --- a/OpenNest.Tests/CuttingPlanning/ErodedPierceTests.cs +++ b/OpenNest.Tests/CuttingPlanning/ErodedPierceTests.cs @@ -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)); } diff --git a/OpenNest.Tests/CuttingPlanning/LeadPathValidationTests.cs b/OpenNest.Tests/CuttingPlanning/LeadPathValidationTests.cs index 0b30a481..8794033f 100644 --- a/OpenNest.Tests/CuttingPlanning/LeadPathValidationTests.cs +++ b/OpenNest.Tests/CuttingPlanning/LeadPathValidationTests.cs @@ -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); diff --git a/OpenNest.Tests/CuttingPlanning/PierceZoneTests.cs b/OpenNest.Tests/CuttingPlanning/PierceZoneTests.cs index 4bae670d..441350d3 100644 --- a/OpenNest.Tests/CuttingPlanning/PierceZoneTests.cs +++ b/OpenNest.Tests/CuttingPlanning/PierceZoneTests.cs @@ -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(); diff --git a/OpenNest.Tests/CuttingPlanning/SafestRouteFallbackTests.cs b/OpenNest.Tests/CuttingPlanning/SafestRouteFallbackTests.cs index 3701a027..cbfe4911 100644 --- a/OpenNest.Tests/CuttingPlanning/SafestRouteFallbackTests.cs +++ b/OpenNest.Tests/CuttingPlanning/SafestRouteFallbackTests.cs @@ -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; - } } diff --git a/OpenNest.Tests/Diagnostics/OverlapAutoCheckSchedulerTests.cs b/OpenNest.Tests/Diagnostics/OverlapAutoCheckSchedulerTests.cs index d15e2860..716ca55f 100644 --- a/OpenNest.Tests/Diagnostics/OverlapAutoCheckSchedulerTests.cs +++ b/OpenNest.Tests/Diagnostics/OverlapAutoCheckSchedulerTests.cs @@ -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)); - } } diff --git a/OpenNest.Tests/Diagnostics/OverlapHoverPagesTests.cs b/OpenNest.Tests/Diagnostics/OverlapHoverPagesTests.cs index 0cf92b5f..d33ad60f 100644 --- a/OpenNest.Tests/Diagnostics/OverlapHoverPagesTests.cs +++ b/OpenNest.Tests/Diagnostics/OverlapHoverPagesTests.cs @@ -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)); - } } diff --git a/OpenNest.Tests/Diagnostics/OverlapPairPresentationTests.cs b/OpenNest.Tests/Diagnostics/OverlapPairPresentationTests.cs index 773f1eab..ba407dba 100644 --- a/OpenNest.Tests/Diagnostics/OverlapPairPresentationTests.cs +++ b/OpenNest.Tests/Diagnostics/OverlapPairPresentationTests.cs @@ -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)); - } } diff --git a/OpenNest.Tests/Diagnostics/OverlapReportStateTests.cs b/OpenNest.Tests/Diagnostics/OverlapReportStateTests.cs index 6cc12591..33ea5e4e 100644 --- a/OpenNest.Tests/Diagnostics/OverlapReportStateTests.cs +++ b/OpenNest.Tests/Diagnostics/OverlapReportStateTests.cs @@ -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)); - } } diff --git a/OpenNest.Tests/Diagnostics/PartSpacingReviewTests.cs b/OpenNest.Tests/Diagnostics/PartSpacingReviewTests.cs index d5ac2dcc..e2ddecd6 100644 --- a/OpenNest.Tests/Diagnostics/PartSpacingReviewTests.cs +++ b/OpenNest.Tests/Diagnostics/PartSpacingReviewTests.cs @@ -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) { diff --git a/OpenNest.Tests/Diagnostics/PostVerificationAnalyzerTests.cs b/OpenNest.Tests/Diagnostics/PostVerificationAnalyzerTests.cs index 63c19c36..20b1063c 100644 --- a/OpenNest.Tests/Diagnostics/PostVerificationAnalyzerTests.cs +++ b/OpenNest.Tests/Diagnostics/PostVerificationAnalyzerTests.cs @@ -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; } diff --git a/OpenNest.Tests/Fill/FillExtentsTests.cs b/OpenNest.Tests/Fill/FillExtentsTests.cs index 8df16577..94d6e840 100644 --- a/OpenNest.Tests/Fill/FillExtentsTests.cs +++ b/OpenNest.Tests/Fill/FillExtentsTests.cs @@ -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); diff --git a/OpenNest.Tests/Fill/PairOverlapDiagnosticTests.cs b/OpenNest.Tests/Fill/PairOverlapDiagnosticTests.cs index a7ecd295..5e31a1b3 100644 --- a/OpenNest.Tests/Fill/PairOverlapDiagnosticTests.cs +++ b/OpenNest.Tests/Fill/PairOverlapDiagnosticTests.cs @@ -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". /// - 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 diff --git a/OpenNest.Tests/TestHelpers.cs b/OpenNest.Tests/TestHelpers.cs index 10284997..f0750251 100644 --- a/OpenNest.Tests/TestHelpers.cs +++ b/OpenNest.Tests/TestHelpers.cs @@ -98,6 +98,88 @@ internal static class TestHelpers return new Drawing("square", pgm); } + /// A full circle of the given radius around (x, y), starting at its right. + 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; + } + + /// A counterclockwise size x size square, lower-left corner at (x, y). + 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; + } + + /// A width x height rectangle from the origin, clockwise: up first, then +X. + 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; + } + + /// A closed contour through the vertices in order, back to the first one. + public static Program MakeClosedProgram(IEnumerable 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; + } + + /// + /// Appends a clockwise width x height rectangle that starts and ends at the midpoint of its + /// right edge, where must already be. + /// + 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); + } + + /// A part of a 4 x 4 absolute-mode square drawing with the given name. + 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)); + } + + /// A w x h outline whose two right-hand corners are arcs of radius r. + 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).