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; } }