using System; using System.Collections.Generic; namespace OpenNest.Engine.Jobs; public enum NestJobStatus { Complete, Incomplete, } public enum NestJobStopReason { Completed, StockExhausted, NoPlacementFound, PlateLimitReached, } /// /// Rotate about the snapshot origin, then translate by X/Y into the selected plate quadrant frame. /// Rotation is in radians. InstanceIndex is zero-based and unique within a part requirement across the job. /// The runner assigns final instance indices when committing a candidate. /// public sealed record NestJobPlacement( string PartId, int InstanceIndex, double X, double Y, double Rotation ); /// Requested = Placed + Unplaced for a requirement ID. public sealed record PartFulfillment(string PartId, int Requested, int Placed, int Unplaced); /// Used counts physical sheets; Remaining is null only for unlimited stock. public sealed record StockUsage(string StockId, int Used, int? Remaining); /// /// One common-line group on a result sheet: the zero-based positions of its members in the /// sheet's and the kerf they were placed at. The /// validation exempts members from the part spacing only through this explicit list, never by /// guessing from proximity (see docs/common-line-cutting.md). /// public sealed record NestJobCommonLine { public NestJobCommonLine(double kerf, IEnumerable placements) { Kerf = kerf; Placements = NestJob.Own(placements ?? throw new ArgumentNullException(nameof(placements))); } public double Kerf { get; } public IReadOnlyList Placements { get; } public bool Equals(NestJobCommonLine other) => other is not null && BitConverter.DoubleToInt64Bits(Kerf) == BitConverter.DoubleToInt64Bits(other.Kerf) && System.Linq.Enumerable.SequenceEqual(Placements, other.Placements); public override int GetHashCode() { var hash = new HashCode(); hash.Add(BitConverter.DoubleToInt64Bits(Kerf)); foreach (var index in Placements) hash.Add(index); return hash.ToHashCode(); } } /// One physical sheet, with owned ordered placements and immutable stock/settings snapshot. public sealed class NestJobPlateResult { public NestJobPlateResult( int plateIndex, NestPlateStock stock, IEnumerable placements, IEnumerable commonLines = null ) { ArgumentNullException.ThrowIfNull(stock); PlateIndex = plateIndex; Stock = stock; Placements = NestJob.Own(placements); CommonLines = commonLines == null ? Array.Empty() : NestJob.Own(commonLines); } public int PlateIndex { get; } public string StockId => Stock.Id; public NestPlateStock Stock { get; } public IReadOnlyList Placements { get; } /// Common-line groups among ; empty when there are none. [System.Text.Json.Serialization.JsonIgnore] public IReadOnlyList CommonLines { get; } /// when serialized: omitted when empty, so a sheet without /// groups serializes exactly as before they existed. [System.Text.Json.Serialization.JsonPropertyName("CommonLines")] [System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)] public IReadOnlyList SerializedCommonLines => CommonLines.Count == 0 ? null : CommonLines; } /// Detached result values in commit/input order; no mutable Drawing, Plate, or NestItem escapes. public sealed class NestJobResult { public NestJobResult( NestJobStatus status, NestJobStopReason stopReason, IEnumerable plates, IEnumerable fulfillment, IEnumerable stockUsage ) { Status = status; StopReason = stopReason; Plates = NestJob.Own(plates); Fulfillment = NestJob.Own(fulfillment); StockUsage = NestJob.Own(stockUsage); } public NestJobStatus Status { get; } public NestJobStopReason StopReason { get; } public IReadOnlyList Plates { get; } public IReadOnlyList Fulfillment { get; } public IReadOnlyList StockUsage { get; } }