mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-10-13 04:51:55 -04:00
fix(cnc): show first internal rapid after cut-first programs
This commit is contained in:
1 parent
08acb092a7
commit
3d512c794c
2 files changed
+84
-8
No files matched your search
@@ -31,16 +31,16 @@ namespace OpenNest.CNC
|
||||
|
||||
private static Vector AppendProgram(Program pgm, Vector basePos, Vector startPos, List<Segment> results)
|
||||
{
|
||||
// Draw the rapid from the previous tool position to the program's first
|
||||
// pierce point. The walk then starts at the program origin (basePos), not
|
||||
// the pierce: the skipped first rapid still advances pos, so starting at
|
||||
// the pierce would apply a nonzero Incremental first delta twice (as in
|
||||
// lead-in programs) and shift every later rapid by it.
|
||||
// Draw the approach to the first rapid destination, or to the program
|
||||
// origin when it starts cutting without a rapid. Only an initial rapid
|
||||
// (or call arrival) is already represented by this approach; a later
|
||||
// first rapid must remain visible. Walk starts at the program origin,
|
||||
// so an incremental initial rapid is not applied twice.
|
||||
var firstPierce = FirstPiercePoint(pgm, basePos);
|
||||
results.Add(new Segment(startPos, firstPierce));
|
||||
|
||||
var pos = basePos;
|
||||
Walk(pgm, basePos, ref pos, skipFirst: true, results);
|
||||
Walk(pgm, basePos, ref pos, skipFirst: StartsWithRapidOrCall(pgm), results);
|
||||
// The last rapid ends at a pierce, not necessarily the final tool position.
|
||||
return pos;
|
||||
}
|
||||
@@ -53,11 +53,23 @@ namespace OpenNest.CNC
|
||||
return FirstPiercePoint(call.Program, basePos + call.Offset);
|
||||
|
||||
if (pgm[i] is Motion motion)
|
||||
return motion.EndPoint + basePos;
|
||||
return motion is RapidMove ? motion.EndPoint + basePos : basePos;
|
||||
}
|
||||
return basePos;
|
||||
}
|
||||
|
||||
private static bool StartsWithRapidOrCall(Program pgm)
|
||||
{
|
||||
for (var i = 0; i < pgm.Length; i++)
|
||||
{
|
||||
if (pgm[i] is SubProgramCall { Program: not null })
|
||||
return true;
|
||||
if (pgm[i] is Motion motion)
|
||||
return motion is RapidMove;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void Walk(
|
||||
Program pgm,
|
||||
Vector basePos,
|
||||
@@ -83,7 +95,7 @@ namespace OpenNest.CNC
|
||||
results.Add(new Segment(pos, firstPierce));
|
||||
|
||||
var subPos = holeBase;
|
||||
Walk(program, holeBase, ref subPos, skipFirst: true, results);
|
||||
Walk(program, holeBase, ref subPos, skipFirst: StartsWithRapidOrCall(program), results);
|
||||
pos = subPos;
|
||||
}
|
||||
else if (code is Motion motion)
|
||||
|
||||
@@ -76,6 +76,70 @@ namespace OpenNest.Tests.CNC
|
||||
Assert.Equal(new Vector(108, 204), segments[1].To);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(Mode.Absolute)]
|
||||
[InlineData(Mode.Incremental)]
|
||||
public void Enumerate_ProgramStartingWithCut_RetainsFirstInternalRapid(Mode mode)
|
||||
{
|
||||
// A saved placed program can start with a lead-in cut rather than a G00.
|
||||
// The first G00 then travels from that cutout to the next one.
|
||||
var pgm = new Program(mode);
|
||||
pgm.Codes.Add(new LinearMove(2, 0));
|
||||
pgm.Codes.Add(new RapidMove(4, 1));
|
||||
pgm.Codes.Add(new LinearMove(5, 1));
|
||||
pgm.Codes.Add(new RapidMove(6, 2));
|
||||
|
||||
var segments = RapidEnumerator.Enumerate(
|
||||
pgm,
|
||||
basePos: new Vector(100, 200),
|
||||
startPos: Vector.Zero
|
||||
);
|
||||
|
||||
Assert.Equal(3, segments.Count);
|
||||
Assert.Equal(new RapidEnumerator.Segment(Vector.Zero, new Vector(100, 200)), segments[0]);
|
||||
Assert.Equal(new Vector(102, 200), segments[1].From);
|
||||
Assert.Equal(mode == Mode.Absolute ? new Vector(104, 201) : new Vector(106, 201), segments[1].To);
|
||||
Assert.Equal(mode == Mode.Absolute ? new Vector(105, 201) : new Vector(111, 202), segments[2].From);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Enumerate_EveryPartStartingWithCut_RetainsItsFirstRapid()
|
||||
{
|
||||
var program = new Program(Mode.Incremental);
|
||||
program.Codes.Add(new LinearMove(1, 0));
|
||||
program.Codes.Add(new RapidMove(1, 1));
|
||||
program.Codes.Add(new LinearMove(0, 1));
|
||||
var drawing = new Drawing("cut-first", program);
|
||||
var parts = new[]
|
||||
{
|
||||
new Part(drawing, new Vector(10, 20)),
|
||||
new Part(drawing, new Vector(30, 40)),
|
||||
};
|
||||
|
||||
var segments = RapidEnumerator.Enumerate(parts);
|
||||
|
||||
Assert.Equal(4, segments.Count);
|
||||
Assert.Equal(new RapidEnumerator.Segment(new Vector(11, 20), new Vector(12, 21)), segments[1]);
|
||||
Assert.Equal(new RapidEnumerator.Segment(new Vector(31, 40), new Vector(32, 41)), segments[3]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Enumerate_SubProgramStartingWithCut_RetainsFirstInternalRapid()
|
||||
{
|
||||
var hole = new Program(Mode.Incremental);
|
||||
hole.Codes.Add(new LinearMove(1, 0));
|
||||
hole.Codes.Add(new RapidMove(2, 1));
|
||||
hole.Codes.Add(new LinearMove(0, 1));
|
||||
var pgm = new Program(Mode.Absolute);
|
||||
pgm.Codes.Add(new SubProgramCall { Id = 1, Program = hole, Offset = new Vector(5, 5) });
|
||||
|
||||
var segments = RapidEnumerator.Enumerate(pgm, new Vector(100, 200), Vector.Zero);
|
||||
|
||||
Assert.Equal(2, segments.Count);
|
||||
Assert.Equal(new RapidEnumerator.Segment(Vector.Zero, new Vector(105, 205)), segments[0]);
|
||||
Assert.Equal(new RapidEnumerator.Segment(new Vector(106, 205), new Vector(108, 206)), segments[1]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Enumerate_SubProgramCall_RapidEndsAtAbsoluteHolePierce()
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user