using System;
using OpenNest;
namespace OpenNest.Engine.Jobs;
/// Immutable per-job options; selection never changes the legacy global registry.
public sealed class NestJobOptions
{
public NestJobOptions(
string placementStrategy = "Lattice",
int? maxPlates = null,
double salvageRate = 0,
double minimumSalvageDimension = 0,
bool searchUntilCancelled = false,
CutOffAxis? offcutCut = null
)
{
if (offcutCut is { } axis && !Enum.IsDefined(axis))
throw new ArgumentOutOfRangeException(nameof(offcutCut));
ArgumentException.ThrowIfNullOrWhiteSpace(placementStrategy);
if (maxPlates <= 0)
throw new ArgumentOutOfRangeException(nameof(maxPlates));
if (!double.IsFinite(salvageRate) || salvageRate < 0 || salvageRate > 1)
throw new ArgumentOutOfRangeException(nameof(salvageRate));
if (!double.IsFinite(minimumSalvageDimension) || minimumSalvageDimension < 0)
throw new ArgumentOutOfRangeException(nameof(minimumSalvageDimension));
PlacementStrategy = placementStrategy;
MaxPlates = maxPlates;
SalvageRate = salvageRate;
MinimumSalvageDimension = minimumSalvageDimension;
SearchUntilCancelled = searchUntilCancelled;
OffcutCut = offcutCut;
}
///
/// When set, only the edge strips one cut along this axis frees count as a sheet's offcut, for
/// salvage credit and for the compactness tie-break between equal-cost plans:
/// keeps full-width strips at the bottom or top (the drop
/// off the end of a sheet), full-length ones at the sides.
/// Null (the default) counts the largest of all four.
///
public CutOffAxis? OffcutCut { get; }
///
/// Asks engines that support it to keep searching after their normal search, reporting
/// each better plan () until the solve's token is
/// cancelled, which then ends the solve with .
/// Meant for a caller that keeps the best reported plan at a time limit, such as
/// . Ignored when the token cannot be
/// cancelled. Results then depend on how long the search ran.
///
public bool SearchUntilCancelled { get; }
/// A copy of these options with set.
public NestJobOptions WithSearchUntilCancelled(bool value) =>
new(PlacementStrategy, MaxPlates, SalvageRate, MinimumSalvageDimension, value, OffcutCut)
{
CommonLineUsage = CommonLineUsage,
CommonLineKerf = CommonLineKerf,
};
///
/// When the pipeline may place parts that allow it on common lines, one
/// apart (see docs/common-line-cutting.md). Off by default;
/// without a positive kerf it acts as off.
///
public CommonLineUsage CommonLineUsage
{
get => commonLineUsage;
init => commonLineUsage = Enum.IsDefined(value) ? value : throw new ArgumentOutOfRangeException(nameof(CommonLineUsage));
}
private readonly CommonLineUsage commonLineUsage;
private readonly double commonLineKerf;
/// The cutting kerf for the job's material and thickness, in job units; zero when unknown.
public double CommonLineKerf
{
get => commonLineKerf;
init => commonLineKerf = double.IsFinite(value) && value >= 0 ? value : throw new ArgumentOutOfRangeException(nameof(CommonLineKerf));
}
/// True when common lines may be made: a level is on and the kerf is positive.
public bool UsesCommonLines => CommonLineUsage != CommonLineUsage.Off && CommonLineKerf > 0;
/// Fraction of eligible edge-offcut area credited by (0..1).
public double SalvageRate { get; }
/// Both offcut dimensions must meet this caller-supplied minimum in job units.
/// Zero disables credit; scraps and holes are never credited.
public double MinimumSalvageDimension { get; }
public string PlacementStrategy { get; }
/// Maximum physical sheets to commit, or null for no explicit cap.
public int? MaxPlates { get; }
}