using System; using System.Collections.Generic; using System.Linq; using OpenNest.Engine; using OpenNest.Engine.Jobs; namespace OpenNest.Desktop { /// /// Auto Nest with the desktop dialog's defaults and no dialog: every drawing's remaining /// quantity, with its priority and rotation constraints, on sheets like a template plate, /// through . Only a valid, keepable result that places something /// is committed, onto the nest's empty plates first and then new plates from its defaults. /// public static class AutoNestJob { /// /// The job for 's remaining quantities on stock like /// ; null when nothing remains. The nest is not changed, so /// the caller can solve it off its thread. /// public static NestPipelineRequest ForRemaining(Nest nest, Plate template, string engineName) { ArgumentNullException.ThrowIfNull(nest); ArgumentNullException.ThrowIfNull(template); var items = nest.Drawings .Where(d => !d.IsCutOff && d.Quantity.Remaining > 0) .Select(d => new NestItem { Drawing = d, Quantity = d.Quantity.Remaining, Priority = d.Priority, RotationStart = d.Constraints.StartAngle, RotationEnd = d.Constraints.EndAngle, StepAngle = d.Constraints.StepAngle, }) .ToList(); if (items.Count == 0) return null; return new NestPipelineRequest(engineName, items, NestStockBuilder.FromTemplate(template, null), new NestJobOptions(maxPlates: NestRenest.MaxPlates, salvageRate: 0, minimumSalvageDimension: nest.MinimumRemnantDimension) { CommonLineUsage = nest.CommonLineUsage, CommonLineKerf = CommonLineKerf(template), }); } /// /// The template plate's saved cutting kerf, or zero (no common lines) when it has none. /// This toolkit-neutral path has no machine defaults to fall back on. /// private static double CommonLineKerf(Plate template) { var kerf = template.CuttingParameters?.Kerf ?? 0; return double.IsFinite(kerf) && kerf > 0 ? kerf : 0; } /// /// Commits to on the nest's own thread /// when it is valid, keepable and places something; otherwise leaves the nest unchanged /// and says why in the note. /// public static NestRenestResult Keep(Nest nest, NestPipelineResult result) { ArgumentNullException.ThrowIfNull(nest); return NestRenest.Keep(result, valid => NestPipelineCommit.ApplyToPlates(valid, () => EmptyOrNewPlate(nest))); } private static Plate EmptyOrNewPlate(Nest nest) => nest.Plates.FirstOrDefault(p => p.Parts.Count == 0) ?? nest.CreatePlate(); /// /// Why would commit nothing for , naming the /// first problem; null when it would commit. /// public static string RefusalReason(NestPipelineResult result) { ArgumentNullException.ThrowIfNull(result); if (!result.IsValid) return $"Auto Nest found {result.Violations.Count} layout problem" + $"{(result.Violations.Count == 1 ? "" : "s")} (first: {result.Violations[0]}); nothing was changed."; if (!result.CanKeep) return "Auto Nest's layout cannot be kept faithfully; nothing was changed."; if (result.Plates.All(p => p.Parts.Count == 0)) return "Auto Nest placed nothing; nothing was changed." + (result.Unplaced.Count > 0 ? " Not placed: " + string.Join("; ", result.Unplaced.Select(u => u.Describe())) + "." : ""); return null; } } }