using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using OpenNest.Geometry; namespace OpenNest.Engine.Jobs.Adapters; /// A detached mutable domain nest plus explicit requirement identity (never inferred from names). public sealed class MaterializedNestResult { internal MaterializedNestResult(Nest nest, Dictionary drawings) { Nest = nest; DrawingsByPartId = new ReadOnlyDictionary(drawings); } public Nest Nest { get; } public IReadOnlyDictionary DrawingsByPartId { get; } } /// Materializes a result from the same job. Geometry safety remains the solver's future validation boundary. public static class NestResultMaterializer { public static MaterializedNestResult Materialize(NestJob job, NestJobResult result) { ArgumentNullException.ThrowIfNull(job); ArgumentNullException.ThrowIfNull(result); var nest = new Nest(); var drawings = job.Parts.ToDictionary( p => p.Id, DrawingJobMapper.CreateDrawing, StringComparer.Ordinal ); foreach (var drawing in drawings.Values) nest.Drawings.Add(drawing); foreach (var sheet in result.Plates) { var plate = DrawingJobMapper.CreatePlate(sheet.Stock); foreach (var pose in sheet.Placements) { if (!drawings.TryGetValue(pose.PartId, out var drawing)) throw new ArgumentException( "Result contains a requirement not present in the job.", nameof(result) ); // Do not use CreateAtOrigin: it normalizes bounds and would change the snapshot frame. var part = new Part(drawing); part.Rotate(pose.Rotation); part.Location = new Vector(pose.X, pose.Y); part.UpdateBounds(); // Quantity=1 is set before the only attachment; Plate's event owns Nested accounting. plate.Parts.Add(part); } AddCommonLines(plate, plate.Parts.ToList(), sheet.CommonLines); nest.Plates.Add(plate); } return new MaterializedNestResult(nest, drawings); } /// /// Adds a sheet's explicit common-line groups to ; members are the /// sheet's placements by position in . A group naming a position /// outside the sheet, a part twice, a part of an earlier group, or fewer than two parts throws: /// such a result is malformed. /// internal static void AddCommonLines(Plate plate, IReadOnlyList parts, IReadOnlyList groups) { if (groups.Count == 0) return; var taken = new HashSet(); foreach (var group in groups) { if (group.Placements.Count < 2 || group.Placements.Any(i => i < 0 || i >= parts.Count || !taken.Add(i))) throw new ArgumentException("A common-line group names a placement that is missing, repeated or in another group."); plate.CommonLines.Add(new CommonLineGroup(group.Placements.Select(i => parts[i]), group.Kerf)); } } }