mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-10-17 20:52:07 -04:00
Report, dialog and diagnostic text across post verification, cutoff planning, alignment and cutting-plan review repeatedly told the user what checks weren't (safety certifications, proof, guarantees) instead of what to do. Reworded these to state the same caveats affirmatively (inspect X, confirm Y, review Z) without changing the underlying safety guidance, and updated the tests/docs that pinned the old wording.
494 lines
19 KiB
C#
494 lines
19 KiB
C#
using OpenNest.CNC;
|
|
using OpenNest.CNC.CuttingStrategy;
|
|
using OpenNest.Diagnostics;
|
|
using OpenNest.Geometry;
|
|
|
|
namespace OpenNest.Tests.Diagnostics;
|
|
|
|
public class PostVerificationAnalyzerTests
|
|
{
|
|
[Fact]
|
|
public void EmptyReport_ListsAllChecksAndLimits()
|
|
{
|
|
var report = PostVerificationAnalyzer.Analyze(new Nest());
|
|
Assert.Empty(report.Findings);
|
|
Assert.False(report.HasWarnings);
|
|
Assert.True(report.CanPost(false));
|
|
Assert.True(report.CanPost(true));
|
|
var text = report.ToDisplayText();
|
|
Assert.Contains("Overlap", text);
|
|
Assert.Contains("Missing lead-ins", text);
|
|
Assert.Contains("Rapid crossings", text);
|
|
Assert.Contains("Inspect the posted machine program", text);
|
|
Assert.Contains("retract", text);
|
|
Assert.Contains("routing", text);
|
|
}
|
|
|
|
[Fact]
|
|
public void Overlap_UsesCleanMaterialAndOriginalOneBasedIndices()
|
|
{
|
|
var a = Rectangle(0, 0, 4);
|
|
var b = Rectangle(1, 1, 1);
|
|
// Deliberately unrelated placed toolpaths: overlap must use clean drawings.
|
|
a.Program.Codes.Clear();
|
|
a.Program.MoveTo(100, 100);
|
|
a.Program.LineTo(101, 101);
|
|
var report = Analyze(Cutoff(30, 30), a, Scribe(40, 40), b);
|
|
var overlap = Assert.Single(Find(report, PostVerificationKind.Overlap));
|
|
Assert.Equal((1, 2, 4), (overlap.PlateNumber, overlap.PartNumber, overlap.OtherPartNumber));
|
|
Assert.NotNull(overlap.Overlap);
|
|
Assert.Equal(new Vector(1.5, 1.5), overlap.Location);
|
|
Assert.Equal(1, overlap.Overlap.Area, 6);
|
|
Assert.True(report.HasWarnings);
|
|
Assert.False(report.CanPost(false));
|
|
Assert.True(report.CanPost(true));
|
|
Assert.False(report.CanPost(false)); // Consent is supplied per invocation, never latched.
|
|
}
|
|
|
|
[Fact]
|
|
public void Overlap_SubtractsHoles()
|
|
{
|
|
var program = Square(0, 0, 10);
|
|
program.Codes.AddRange(Square(2, 2, 6).Codes);
|
|
var frame = Part(program);
|
|
var report = Analyze(frame, Rectangle(3, 3, 1));
|
|
Assert.Empty(Find(report, PostVerificationKind.Overlap));
|
|
Assert.Empty(Find(report, PostVerificationKind.Incomplete));
|
|
}
|
|
|
|
[Fact]
|
|
public void MissingLeadIn_ManualFlagDoesNotProveCoverage()
|
|
{
|
|
var part = Rectangle(0, 0, 4);
|
|
part.HasManualLeadIns = true;
|
|
var finding = Assert.Single(Find(Analyze(part), PostVerificationKind.MissingLeadIn));
|
|
Assert.Equal(Vector.Zero, finding.Location);
|
|
}
|
|
|
|
[Fact]
|
|
public void MissingLeadIn_ActualLeadDoesNotRequireManualFlag()
|
|
{
|
|
var part = Rectangle(0, 0, 4);
|
|
part.ApplyLeadIns(Leads(), new Vector(-2, -2));
|
|
part.HasManualLeadIns = false;
|
|
Assert.Empty(Find(Analyze(part), PostVerificationKind.MissingLeadIn));
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(false)]
|
|
[InlineData(true)]
|
|
public void MissingLeadIn_NoLeadOrZeroLengthWarns(bool zeroLength)
|
|
{
|
|
var part = Rectangle(0, 0, 4);
|
|
var parameters = Leads();
|
|
parameters.ExternalLeadIn = zeroLength ? new LineLeadIn { Length = 0 } : new NoLeadIn();
|
|
part.ApplyLeadIns(parameters, new Vector(-2, -2));
|
|
Assert.Single(Find(Analyze(part), PostVerificationKind.MissingLeadIn));
|
|
}
|
|
|
|
[Fact]
|
|
public void MissingLeadIn_PartialApplySingleLeavesOtherContourUncovered()
|
|
{
|
|
var program = Square(0, 0, 10);
|
|
program.Codes.AddRange(Square(2, 2, 2).Codes);
|
|
var part = Part(program);
|
|
part.ApplySingleLeadIn(Leads(), Vector.Zero,
|
|
new Line(Vector.Zero, new Vector(10, 0)), ContourType.External);
|
|
Assert.True(part.HasManualLeadIns);
|
|
Assert.Single(Find(Analyze(part), PostVerificationKind.MissingLeadIn));
|
|
}
|
|
|
|
[Fact]
|
|
public void ScribeAndCutoff_AreExemptFromOverlapAndLeadInChecks()
|
|
{
|
|
var report = Analyze(Scribe(0, 0), Cutoff(0, 0));
|
|
Assert.Empty(report.Findings);
|
|
}
|
|
|
|
[Fact]
|
|
public void Rapid_ThroughPreviouslyCutPartWarns()
|
|
{
|
|
var report = Analyze(Rectangle(0, 0, 4), Scribe(-2, 2), Scribe(6, 2));
|
|
var crossing = Assert.Single(Find(report, PostVerificationKind.RapidCrossing).Where(f =>
|
|
f.PartNumber == 3 && f.OtherPartNumber == 1));
|
|
Assert.NotNull(crossing.RapidStart);
|
|
Assert.NotNull(crossing.RapidEnd);
|
|
Assert.Equal(new[] { new Vector(0, 2), new Vector(4, 2) }, crossing.ContactPoints);
|
|
Assert.Equal((crossing.RapidStart.Value + crossing.RapidEnd.Value) * 0.5, crossing.Location);
|
|
}
|
|
|
|
[Fact]
|
|
public void Rapid_DoesNotUseFuturePartsAsObstacles()
|
|
{
|
|
var report = Analyze(Scribe(-2, 2), Scribe(6, 2), Rectangle(0, 0, 4));
|
|
Assert.Empty(Find(report, PostVerificationKind.RapidCrossing));
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(-2, -2, false)] // Start-only departure.
|
|
[InlineData(2, 2, true)] // Departure into the interior.
|
|
[InlineData(4, 0, true)] // Along edge / arrival on another endpoint.
|
|
public void Rapid_HandlesDepartureVersusInteriorAndBoundary(double x, double y, bool warning)
|
|
{
|
|
var report = Analyze(Rectangle(0, 0, 4), Scribe(x, y));
|
|
Assert.Equal(warning, Find(report, PostVerificationKind.RapidCrossing).Any());
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(-2, 4, 6, 4)] // Tangent along top edge.
|
|
[InlineData(-2, 2, 0, 2)] // Endpoint arrival.
|
|
[InlineData(1, 1, 2, 2)] // Entirely inside.
|
|
public void Rapid_ConservativelyFlagsContactAndInterior(double x1, double y1, double x2, double y2)
|
|
{
|
|
var report = Analyze(Rectangle(0, 0, 4), Scribe(x1, y1), Scribe(x2, y2));
|
|
Assert.Contains(Find(report, PostVerificationKind.RapidCrossing), f => f.PartNumber == 3);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(false)]
|
|
[InlineData(true)]
|
|
public void Rapid_ActualGapNotStaleTabSettingControlsObstacle(bool staleFlag)
|
|
{
|
|
var closed = Rectangle(0, 0, 4);
|
|
closed.CuttingParameters = new CuttingParameters { TabsEnabled = staleFlag };
|
|
Assert.Contains(Find(Analyze(closed, Scribe(-2, 2), Scribe(6, 2)),
|
|
PostVerificationKind.RapidCrossing), f => f.PartNumber == 3);
|
|
|
|
var gapped = Rectangle(0, 0, 4);
|
|
((LinearMove)gapped.Program.Codes[^1]).EndPoint = new Vector(0, 0.25);
|
|
gapped.CuttingParameters = new CuttingParameters { TabsEnabled = staleFlag };
|
|
Assert.Empty(Find(Analyze(gapped, Scribe(-2, 2), Scribe(6, 2)),
|
|
PostVerificationKind.RapidCrossing));
|
|
}
|
|
|
|
[Fact]
|
|
public void Rapid_RealGeneratedTabRemainsGapWithStaleDisabledParameters()
|
|
{
|
|
var part = Rectangle(0, 0, 4);
|
|
var parameters = Leads();
|
|
parameters.TabsEnabled = true;
|
|
parameters.TabConfig = new NormalTab { Size = 0.25 };
|
|
part.ApplyLeadIns(parameters, new Vector(-2, 0));
|
|
parameters.TabsEnabled = false;
|
|
Assert.Empty(Find(Analyze(part, Scribe(-2, 2), Scribe(6, 2)), PostVerificationKind.RapidCrossing));
|
|
}
|
|
|
|
[Fact]
|
|
public void Rapid_CutoffTravelStillCrossesEarlierRealCuts()
|
|
{
|
|
var report = Analyze(Rectangle(0, 0, 4), Scribe(-2, 2), Cutoff(6, 2));
|
|
var crossing = Assert.Single(Find(report, PostVerificationKind.RapidCrossing).Where(f =>
|
|
f.PartNumber == 3 && f.OtherPartNumber == 1));
|
|
Assert.NotNull(crossing.RapidStart);
|
|
Assert.NotNull(crossing.RapidEnd);
|
|
Assert.Equal((crossing.RapidStart.Value + crossing.RapidEnd.Value) * 0.5, crossing.Location);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(Mode.Absolute)]
|
|
[InlineData(Mode.Incremental)]
|
|
public void Rapid_SamePartSharedHoleCallsUseOffsetsAndNonzeroLeadOrigins(Mode mode)
|
|
{
|
|
var part = Rectangle(100, 50, 20);
|
|
var hole = CircleWithLead();
|
|
hole.Mode = mode;
|
|
var placed = new Program();
|
|
placed.Codes.Add(new SubProgramCall { Program = hole, Offset = new Vector(5, 5), Id = 7 });
|
|
placed.MoveTo(2, 5);
|
|
placed.LineTo(2, 5); // Zero-length cut doesn't create an obstacle.
|
|
placed.MoveTo(8, 5); // Cross completed first hole.
|
|
placed.Codes.Add(new SubProgramCall { Program = hole, Offset = new Vector(15, 5), Id = 7 });
|
|
placed.Mode = mode;
|
|
Assert.True(part.RestoreLeadInProgram(placed, false));
|
|
var report = Analyze(part);
|
|
Assert.Contains(Find(report, PostVerificationKind.RapidCrossing), f =>
|
|
f.PartNumber == 1 && f.OtherPartNumber == 1);
|
|
Assert.Empty(Find(report, PostVerificationKind.Incomplete));
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(Mode.Absolute)]
|
|
[InlineData(Mode.Incremental)]
|
|
public void Rapid_NonzeroInitialLeadRapidIsNotAppliedTwice(Mode mode)
|
|
{
|
|
var part = Rectangle(100, 50, 4);
|
|
var placed = Square(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);
|
|
placed.MoveTo(6, 2);
|
|
placed.Mode = mode;
|
|
part.RestoreLeadInProgram(placed, false);
|
|
var crossings = Find(Analyze(part), PostVerificationKind.RapidCrossing);
|
|
Assert.Single(crossings);
|
|
Assert.Empty(Find(Analyze(part), PostVerificationKind.MissingLeadIn));
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(RotationType.CW)]
|
|
[InlineData(RotationType.CCW)]
|
|
public void Rapid_FullCircleArcTangentIsNotLostToChordApproximation(RotationType rotation)
|
|
{
|
|
var program = new Program();
|
|
program.MoveTo(1, 0);
|
|
program.ArcTo(1, 0, 0, 0, rotation);
|
|
var report = Analyze(Part(program), Scribe(-2, 1), Scribe(2, 1));
|
|
Assert.Contains(Find(report, PostVerificationKind.RapidCrossing), f => f.PartNumber == 3);
|
|
}
|
|
|
|
[Fact]
|
|
public void Rapid_SamePartFutureContourIsNotAnObstacle()
|
|
{
|
|
var part = Rectangle(0, 0, 10);
|
|
var placed = new Program();
|
|
placed.MoveTo(-2, 2);
|
|
placed.MoveTo(6, 2);
|
|
placed.Codes.AddRange(Square(0, 0, 4).Codes);
|
|
part.RestoreLeadInProgram(placed, false);
|
|
Assert.Empty(Find(Analyze(part), PostVerificationKind.RapidCrossing));
|
|
}
|
|
|
|
[Fact]
|
|
public void MultiplePlates_AreAllCheckedWithoutSharingObstacles()
|
|
{
|
|
var nest = NestWith(Rectangle(0, 0, 4));
|
|
var second = nest.CreatePlate();
|
|
second.Parts.Add(Scribe(-2, 2));
|
|
second.Parts.Add(Scribe(6, 2));
|
|
second.Parts.Add(Rectangle(0, 0, 4));
|
|
second.Parts.Add(Rectangle(1, 1, 1));
|
|
var report = PostVerificationAnalyzer.Analyze(nest);
|
|
Assert.Contains(Find(report, PostVerificationKind.MissingLeadIn), f => f.PlateNumber == 1);
|
|
Assert.Contains(Find(report, PostVerificationKind.MissingLeadIn), f => f.PlateNumber == 2);
|
|
Assert.All(Find(report, PostVerificationKind.Overlap), f => Assert.Equal(2, f.PlateNumber));
|
|
Assert.DoesNotContain(Find(report, PostVerificationKind.RapidCrossing), f =>
|
|
f.PlateNumber == 2 && f.PartNumber <= 3);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("recursive")]
|
|
[InlineData("missing-call")]
|
|
[InlineData("null-code")]
|
|
[InlineData("nonfinite")]
|
|
[InlineData("bad-arc")]
|
|
[InlineData("empty")]
|
|
[InlineData("null-codes")]
|
|
public void MalformedPlacedPrograms_WarnInsteadOfClearing(string fault)
|
|
{
|
|
var part = Rectangle(0, 0, 4);
|
|
var program = part.Program;
|
|
switch (fault)
|
|
{
|
|
case "recursive": program.Codes.Add(new SubProgramCall { Program = program }); break;
|
|
case "missing-call": program.Codes.Add(new SubProgramCall()); break;
|
|
case "null-code": program.Codes.Add(null); break;
|
|
case "nonfinite": program.MoveTo(double.NaN, 0); break;
|
|
case "bad-arc": program.ArcTo(1, 1, 0, 0, RotationType.CW); break;
|
|
case "empty": program.Codes.Clear(); break;
|
|
case "null-codes": program.Codes = null; break;
|
|
}
|
|
var report = Analyze(part);
|
|
Assert.NotEmpty(Find(report, PostVerificationKind.Incomplete));
|
|
Assert.True(report.HasWarnings);
|
|
Assert.False(report.CanPost(false));
|
|
Assert.DoesNotContain("No warnings", report.ToDisplayText());
|
|
}
|
|
|
|
[Fact]
|
|
public void MalformedCleanProgram_OverlapIssuePreservesInputIndex()
|
|
{
|
|
var part = Rectangle(0, 0, 4);
|
|
part.BaseDrawing.Program.Codes.RemoveAt(4);
|
|
var report = Analyze(Cutoff(30, 30), part);
|
|
Assert.Contains(Find(report, PostVerificationKind.Incomplete), f => f.PartNumber == 2);
|
|
}
|
|
|
|
[Fact]
|
|
public void Rapid_ResumedContourBecomesObstacleOnlyAfterItsLastCut()
|
|
{
|
|
var part = Rectangle(0, 0, 4);
|
|
var placed = Square(0, 0, 4);
|
|
placed.Codes.Insert(3, new RapidMove(4, 4)); // Pause, with no uncut gap.
|
|
placed.MoveTo(-2, 2);
|
|
placed.MoveTo(6, 2);
|
|
part.RestoreLeadInProgram(placed, false);
|
|
Assert.Single(Find(Analyze(part), PostVerificationKind.RapidCrossing));
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(false)]
|
|
[InlineData(true)]
|
|
public void Rapid_InternalGapDoesNotBecomeAnObstacle(bool staleFlag)
|
|
{
|
|
var part = Rectangle(0, 0, 4);
|
|
var placed = Square(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);
|
|
part.RestoreLeadInProgram(placed, false);
|
|
part.CuttingParameters = new CuttingParameters { TabsEnabled = staleFlag };
|
|
Assert.Empty(Find(Analyze(part), PostVerificationKind.RapidCrossing));
|
|
}
|
|
|
|
[Fact]
|
|
public void MissingPlacedCuts_CannotPassForACleanMaterialPart()
|
|
{
|
|
var part = Rectangle(0, 0, 4);
|
|
part.Program.Codes.Clear();
|
|
part.Program.MoveTo(0, 0);
|
|
var report = Analyze(part);
|
|
Assert.NotEmpty(Find(report, PostVerificationKind.Incomplete));
|
|
Assert.False(report.CanPost(false));
|
|
}
|
|
|
|
[Fact]
|
|
public void InvalidArcDirection_IsIncomplete()
|
|
{
|
|
var part = Part(CircleWithLead());
|
|
((ArcMove)part.Program.Codes[^1]).Rotation = (RotationType)99;
|
|
Assert.NotEmpty(Find(Analyze(part), PostVerificationKind.Incomplete));
|
|
}
|
|
|
|
[Fact]
|
|
public void Rapid_LeadOutThatCompletesTabGapIsNotTreatedAsRetained()
|
|
{
|
|
var part = Rectangle(0, 0, 4);
|
|
((LinearMove)part.Program.Codes[^1]).EndPoint = new Vector(0, 0.25);
|
|
part.Program.Codes.Add(new LinearMove(0, 0) { Layer = LayerType.Leadout });
|
|
var report = Analyze(part, Scribe(-2, 2), Scribe(6, 2));
|
|
Assert.True(Find(report, PostVerificationKind.RapidCrossing).Any()
|
|
|| Find(report, PostVerificationKind.Incomplete).Any());
|
|
}
|
|
|
|
[Fact]
|
|
public void Rapid_OutOfOrderFragmentsCannotSilentlyPassAsTabbed()
|
|
{
|
|
var part = Rectangle(0, 0, 4);
|
|
var program = new Program();
|
|
program.MoveTo(0, 0);
|
|
program.LineTo(4, 0);
|
|
program.MoveTo(4, 4);
|
|
program.LineTo(0, 4);
|
|
program.MoveTo(4, 0);
|
|
program.LineTo(4, 4);
|
|
program.MoveTo(0, 4);
|
|
program.LineTo(0, 0);
|
|
part.RestoreLeadInProgram(program, false);
|
|
var report = Analyze(part, Scribe(-2, 2), Scribe(6, 2));
|
|
Assert.True(Find(report, PostVerificationKind.RapidCrossing).Any()
|
|
|| Find(report, PostVerificationKind.Incomplete).Any());
|
|
}
|
|
|
|
[Fact]
|
|
public void Cancellation_ThrowsWithoutChangingInputs()
|
|
{
|
|
var part = Rectangle(0, 0, 4);
|
|
var before = part.Program.ToString();
|
|
using var cancellation = new CancellationTokenSource();
|
|
cancellation.Cancel();
|
|
Assert.Throws<OperationCanceledException>(() =>
|
|
PostVerificationAnalyzer.Analyze(NestWith(part), cancellation.Token));
|
|
Assert.Equal(before, part.Program.ToString());
|
|
}
|
|
|
|
[Fact]
|
|
public void Analyze_PreservesProgramsPosesSharedCallsAndCuttingState()
|
|
{
|
|
var part = Rectangle(100, 50, 20);
|
|
var sub = CircleWithLead();
|
|
var program = Square(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;
|
|
part.RestoreLeadInProgram(program, true);
|
|
part.CuttingParameters = Leads();
|
|
var nest = NestWith(part);
|
|
var codes = program.Codes.ToArray();
|
|
var before = program.ToString();
|
|
var beforeSub = sub.ToString();
|
|
var parameters = part.CuttingParameters;
|
|
var bounds = part.BoundingBox;
|
|
var quantity = part.BaseDrawing.Quantity.Nested;
|
|
var report = PostVerificationAnalyzer.Analyze(nest);
|
|
Assert.Same(program, part.Program);
|
|
Assert.Equal(codes, program.Codes);
|
|
Assert.Equal(before, program.ToString());
|
|
Assert.Equal(beforeSub, sub.ToString());
|
|
Assert.Same(sub, ((SubProgramCall)program[0]).Program);
|
|
Assert.Same(sub, ((SubProgramCall)program[1]).Program);
|
|
Assert.Equal(new Vector(100, 50), part.Location);
|
|
Assert.Same(bounds, part.BoundingBox);
|
|
Assert.True(part.HasManualLeadIns);
|
|
Assert.True(part.LeadInsLocked);
|
|
Assert.Same(parameters, part.CuttingParameters);
|
|
Assert.Equal(quantity, part.BaseDrawing.Quantity.Nested);
|
|
Assert.True(((ICollection<PostVerificationFinding>)report.Findings).IsReadOnly);
|
|
var display = report.ToDisplayText();
|
|
part.Program.Codes.Clear();
|
|
Assert.Equal(display, report.ToDisplayText());
|
|
}
|
|
|
|
private static PostVerificationFinding[] Find(PostVerificationReport report, PostVerificationKind kind) =>
|
|
report.Findings.Where(f => f.Kind == kind).ToArray();
|
|
|
|
private static PostVerificationReport Analyze(params Part[] parts) =>
|
|
PostVerificationAnalyzer.Analyze(NestWith(parts));
|
|
|
|
private static Nest NestWith(params Part[] parts)
|
|
{
|
|
var nest = new Nest();
|
|
var plate = nest.CreatePlate();
|
|
foreach (var part in parts)
|
|
plate.Parts.Add(part);
|
|
return nest;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
private static Part Scribe(double x, double y)
|
|
{
|
|
var program = new Program();
|
|
program.MoveTo(x, y);
|
|
program.Codes.Add(new LinearMove(x, y) { Layer = LayerType.Scribe });
|
|
return Part(program);
|
|
}
|
|
|
|
private static Part Cutoff(double x, double y)
|
|
{
|
|
var program = new Program();
|
|
program.MoveTo(x, y);
|
|
program.LineTo(x + 1, y);
|
|
return new Part(new Drawing("cutoff", program) { IsCutOff = true });
|
|
}
|
|
|
|
private static Program CircleWithLead()
|
|
{
|
|
var program = new Program();
|
|
program.MoveTo(0.5, 0);
|
|
program.Codes.Add(new LinearMove(1, 0) { Layer = LayerType.Leadin });
|
|
program.Codes.Add(new ArcMove(1, 0, 0, 0) { Layer = LayerType.Display });
|
|
return program;
|
|
}
|
|
|
|
private static CuttingParameters Leads() => new()
|
|
{
|
|
ExternalLeadIn = new LineLeadIn { Length = 0.5 },
|
|
InternalLeadIn = new LineLeadIn { Length = 0.5 },
|
|
ArcCircleLeadIn = new LineLeadIn { Length = 0.5 }
|
|
};
|
|
}
|