using System;
using System.Linq;
namespace OpenNest.CNC
{
///
/// How a planned program cuts a common line once (see docs/common-line-cutting.md). A shared cut
/// is a run of moves: an open centerline cut made without
/// kerf compensation, with straight leads on its own axis. A remainder (a member's perimeter
/// between its shared spans) is an open contour whose rapid is followed directly by one of the
/// comments, naming the winding of the perimeter it
/// belongs to, so a post compensates it on the perimeter's side even though it encloses nothing.
///
public static class CommonLineCuts
{
/// The comment after the rapid into a remainder of a clockwise perimeter.
public const string ClockwiseRemainder = "COMMON LINE REMAINDER CW";
/// The comment after the rapid into a remainder of a counterclockwise perimeter.
public const string CounterclockwiseRemainder = "COMMON LINE REMAINDER CCW";
/// The remainder comment for a perimeter wound .
public static string RemainderMarker(RotationType winding) =>
winding == RotationType.CW ? ClockwiseRemainder : CounterclockwiseRemainder;
/// The perimeter winding a remainder comment names, or null for any other code.
public static RotationType? RemainderWinding(ICode code) => code switch
{
Comment { Value: ClockwiseRemainder } => RotationType.CW,
Comment { Value: CounterclockwiseRemainder } => RotationType.CCW,
_ => null,
};
/// Whether 's own codes cut a common line once.
public static bool IsSinglePass(Program program) =>
program?.Codes != null && program.Codes.Any(code =>
code is LinearMove { Layer: LayerType.CommonLine } or ArcMove { Layer: LayerType.CommonLine }
|| RemainderWinding(code) != null);
///
/// Refuses, before any output, a nest with a common line cut once when
/// cannot write shared cuts and remainders: it would cut the
/// shared cut with compensation (into a part) or a remainder on the wrong side. Posts never
/// fall back to cutting the line twice; set the plate to cut its common lines twice instead.
///
/// The post cannot write the nest.
public static void EnsurePostable(Nest nest, IPostProcessor postProcessor)
{
ArgumentNullException.ThrowIfNull(nest);
if (postProcessor is ICommonLinePostProcessor)
return;
for (var p = 0; p < nest.Plates.Count; p++)
{
var plate = nest.Plates[p];
for (var i = 0; i < plate.Parts.Count; i++)
if (IsSinglePass(plate.Parts[i]?.Program))
throw new InvalidOperationException(
$"Plate {p + 1}, part {i + 1} cuts a common line once, but the "
+ $"{postProcessor?.Name ?? "selected"} post cannot write single-pass common lines. "
+ "Set the plate to cut its common lines twice (Plate > Arrange > Cut Common Lines Twice), "
+ "plan cutting again, then post.");
}
}
}
///
/// A post that writes common lines cut once (): each shared cut
/// without kerf compensation and each remainder compensated on its perimeter's side. Posts
/// without it refuse such a nest ().
///
public interface ICommonLinePostProcessor : IPostProcessor
{
}
}