mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-11-14 05:45:07 -05:00
88 lines
4.2 KiB
C#
88 lines
4.2 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
|
|
namespace OpenNest.CNC
|
|
{
|
|
/// <summary>
|
|
/// A rapid the head must make raised because it passes over parts already cut, which may have
|
|
/// tipped. Plan Cutting marks such a rapid only when no route avoids the crossing, and always
|
|
/// marks the rapid out of a cutout's slug after its lead-out (see docs/cutting-planner.md): a
|
|
/// <see cref="Marker"/> comment immediately before the <see cref="RapidMove"/>, or before the
|
|
/// <see cref="SubProgramCall"/> whose arrival rapid it is, since a sub-program may be shared and
|
|
/// is never marked itself. Rapid checks accept a crossing on a marked rapid. Posts write their machine's head raise before every marked rapid,
|
|
/// and refuse to post a marked program when they have none, since the rapid is unsafe head down.
|
|
/// See docs/cutting-planner.md.
|
|
/// </summary>
|
|
public static class HeadRaise
|
|
{
|
|
/// <summary>The comment immediately before a rapid made with the head raised.</summary>
|
|
public const string Marker = "HEAD RAISE";
|
|
|
|
public static bool IsMarker(ICode code) => code is Comment { Value: Marker };
|
|
|
|
/// <summary>Whether <paramref name="program"/>'s own codes hold a marker.</summary>
|
|
public static bool IsMarked(Program program)
|
|
{
|
|
if (program?.Codes == null)
|
|
return false;
|
|
foreach (var code in program.Codes)
|
|
if (IsMarker(code))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// For each of <paramref name="codes"/>, whether it is a rapid made with the head raised, or a
|
|
/// sub-program call whose arrival rapid is. A marker must be followed directly by a rapid or
|
|
/// a call; anything else is a malformed program (<see cref="ArgumentException"/>), since a
|
|
/// post could raise before the wrong move.
|
|
/// </summary>
|
|
public static bool[] RaisedMask(IReadOnlyList<ICode> codes)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(codes);
|
|
var mask = new bool[codes.Count];
|
|
for (var i = 0; i < codes.Count; i++)
|
|
{
|
|
if (!IsMarker(codes[i]))
|
|
continue;
|
|
if (i + 1 >= codes.Count || codes[i + 1] is not (RapidMove or SubProgramCall))
|
|
throw new ArgumentException("A head-raise marker is not followed by a rapid.");
|
|
mask[i + 1] = true;
|
|
}
|
|
return mask;
|
|
}
|
|
|
|
/// <summary>
|
|
/// A copy of <paramref name="program"/> with a marker before each of its own codes at
|
|
/// <paramref name="rapids"/>, which must be rapids or sub-program calls not marked already.
|
|
/// </summary>
|
|
public static Program Mark(Program program, IReadOnlyCollection<int> rapids)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(program);
|
|
ArgumentNullException.ThrowIfNull(rapids);
|
|
var marked = RaisedMask(program.Codes);
|
|
var copy = (Program)program.Clone();
|
|
for (var i = copy.Codes.Count - 1; i >= 0; i--)
|
|
{
|
|
if (!rapids.Contains(i))
|
|
continue;
|
|
if (copy.Codes[i] is not (RapidMove or SubProgramCall) || marked[i])
|
|
throw new ArgumentException("Only an unmarked rapid or sub-program call can be marked for a head raise.");
|
|
copy.Codes.Insert(i, new Comment(Marker));
|
|
}
|
|
return copy;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Refuses a nest a post cannot write, before any output is created: a marked rapid needs
|
|
/// the post's head-raise code, and its markers must be well formed.
|
|
/// </summary>
|
|
public static void EnsurePostable(Nest nest, bool hasCode, string postName) =>
|
|
PostMarkers.EnsurePostable(nest, hasCode, IsMarked, program => RaisedMask(program.Codes),
|
|
name => $"{name} has rapids that must raise the head over parts already cut, but "
|
|
+ $"the {postName} post has no head-raise code. Enter it in \"Head raise\" in "
|
|
+ $"the {postName} Settings dialog, which opens when you post.");
|
|
}
|
|
}
|