diff --git a/OpenNest.Reporting/NestPdfWriter.cs b/OpenNest.Reporting/NestPdfWriter.cs index d0b31c0a..3782f7e4 100644 --- a/OpenNest.Reporting/NestPdfWriter.cs +++ b/OpenNest.Reporting/NestPdfWriter.cs @@ -103,6 +103,7 @@ public static class NestPdfWriter ReportFonts.ValidateText(snapshot.Notes, "Notes"); ReportFonts.ValidateText(snapshot.Material, "Material"); ReportFonts.ValidateText(snapshot.Grade, "Material grade"); + ReportFonts.ValidateText(snapshot.ThicknessName, "Thickness name"); foreach (var drawing in snapshot.Drawings) ReportFonts.ValidateText(drawing.Name, $"Drawing {drawing.Id} name"); } diff --git a/OpenNest.Reporting/PartPrintoutBuilder.cs b/OpenNest.Reporting/PartPrintoutBuilder.cs index 3258c1b0..3295b9a5 100644 --- a/OpenNest.Reporting/PartPrintoutBuilder.cs +++ b/OpenNest.Reporting/PartPrintoutBuilder.cs @@ -20,6 +20,12 @@ public sealed record PrintoutPart(string Name, string Customer, double Width, do public sealed record PartPrintoutSnapshot(string Title, string Material, double Thickness, string Units, DateTimeOffset GeneratedAt, ImmutableArray Parts) { + /// + /// The name the nest machine's config gives this thickness ("16 ga"), shown before the + /// decimal; empty when it has none. The caller supplies it, as for the nest report. + /// + public string ThicknessName { get; init; } = ""; + /// "lb" for inch jobs, "kg" for millimeter jobs. public string WeightUnits => Units == "mm" ? "kg" : "lb"; } diff --git a/OpenNest.Reporting/PartPrintoutWriter.cs b/OpenNest.Reporting/PartPrintoutWriter.cs index b28bc794..16b86409 100644 --- a/OpenNest.Reporting/PartPrintoutWriter.cs +++ b/OpenNest.Reporting/PartPrintoutWriter.cs @@ -62,6 +62,7 @@ public static class PartPrintoutWriter { ReportFonts.ValidateText(snapshot.Title, "Printout title"); ReportFonts.ValidateText(snapshot.Material, "Material"); + ReportFonts.ValidateText(snapshot.ThicknessName, "Thickness name"); foreach (var part in snapshot.Parts) { ReportFonts.ValidateText(part.Name, $"Drawing '{part.Name}' name"); @@ -99,7 +100,7 @@ public static class PartPrintoutWriter var rows = new (string, string, string, string)[] { ("DIMENSIONS", $"{Length(part.Width, units)} x {Length(part.Length, units)}", "MATERIAL", Dash(snapshot.Material)), - ("THICKNESS", snapshot.Thickness > 0 ? Length(snapshot.Thickness, units, "0.####") : "-", + ("THICKNESS", Thickness(snapshot), "CUT LENGTH", Length(part.CutLength, units)), ("PIERCE COUNT", part.PierceCount.ToString(Invariant), "WEIGHT", part.Weight is { } weight ? $"{Number(weight)} {snapshot.WeightUnits}" : "-"), @@ -177,6 +178,15 @@ public static class PartPrintoutWriter private static string Length(double value, string units, string format = "0.00") => units == "mm" ? $"{value.ToString(format, Invariant)} mm" : $"{value.ToString(format, Invariant)}\""; + /// The machine config's name for the thickness leads: 16 ga - 0.0598". + private static string Thickness(PartPrintoutSnapshot snapshot) + { + if (snapshot.Thickness <= 0) + return "-"; + var value = Length(snapshot.Thickness, snapshot.Units, "0.####"); + return snapshot.ThicknessName.Length > 0 ? $"{snapshot.ThicknessName} - {value}" : value; + } + private static string Number(double value) => value.ToString("0.00", Invariant); private static string Dash(string text) => string.IsNullOrWhiteSpace(text) ? "-" : text.Trim(); diff --git a/OpenNest.Tests/Reporting/PartPrintoutTests.cs b/OpenNest.Tests/Reporting/PartPrintoutTests.cs index 32147512..3799ae3d 100644 --- a/OpenNest.Tests/Reporting/PartPrintoutTests.cs +++ b/OpenNest.Tests/Reporting/PartPrintoutTests.cs @@ -122,6 +122,40 @@ public sealed class PartPrintoutTests : IDisposable Assert.Contains("Generated 9/29/2026 12:00 PM", text); } + [SkippableFact] + public void Write_NamedThicknessLeadsTheDecimal() + { + var nest = NestReportTestData.CreateNest(); + var bracket = nest.Drawings.Single(drawing => drawing.Name == "Bracket"); + var path = Path.Combine(directory, "named.pdf"); + + PartPrintoutWriter.Write(PartPrintoutBuilder.Capture(nest, [bracket], NestReportTestData.GeneratedAt) + with + { ThicknessName = "11 ga" }, path); + + Assert.Matches(@"THICKNESS\s+11 ga - 0\.125""\s+CUT LENGTH", ReportPdf.Text(path, 1)); + } + + [Fact] + public void Write_RejectsAThicknessNameTheBundledFontCannotShow() + { + var nest = NestReportTestData.CreateNest(); + var printout = Path.Combine(directory, "bad-printout.pdf"); + var report = Path.Combine(directory, "bad-report.pdf"); + + var printoutError = Assert.Throws(() => PartPrintoutWriter.Write( + PartPrintoutBuilder.Capture(nest, null, NestReportTestData.GeneratedAt) with { ThicknessName = "1/4—plate" }, + printout)); + var reportError = Assert.Throws(() => NestPdfWriter.Write( + NestReportBuilder.Capture(nest, NestReportTestData.GeneratedAt) with { ThicknessName = "1/4—plate" }, + report)); + + Assert.Contains("Thickness name", printoutError.Message); + Assert.Contains("Thickness name", reportError.Message); + Assert.False(File.Exists(printout)); + Assert.False(File.Exists(report)); + } + [Fact] public void Write_RejectsTextTheBundledFontCannotShow() { diff --git a/OpenNest/Forms/EditNestForm.cs b/OpenNest/Forms/EditNestForm.cs index b9f5ebb8..754bbae4 100644 --- a/OpenNest/Forms/EditNestForm.cs +++ b/OpenNest/Forms/EditNestForm.cs @@ -862,12 +862,15 @@ namespace OpenNest.Forms catalogDensity ) with { - // The name the machine config gives the nest's thickness ("16 ga"), if any. - ThicknessName = Data.MachineCuttingDefaults.Find(machine, targets.Nest)?.Name?.Trim() ?? "", + ThicknessName = MachineThicknessName(machine, targets.Nest), }; NestPdfWriter.Write(snapshot, destination); } + /// The name the machine config gives the nest's thickness ("16 ga"), or empty. + private static string MachineThicknessName(Data.MachineConfig machine, Nest nest) => + Data.MachineCuttingDefaults.Find(machine, nest)?.Name?.Trim() ?? ""; + /// /// Writes one printout page per drawing, in order. Capture is synchronous on the UI thread /// and reads only drawings, so it has no accounting or dirty-state side effects. @@ -877,13 +880,17 @@ namespace OpenNest.Forms ArgumentNullException.ThrowIfNull(drawings); ArgumentException.ThrowIfNullOrWhiteSpace(destination); var material = Nest.Material; + var machine = MachineSettings.FindMachine(Nest.Machine); var catalogDensity = Data.MaterialDensityLookup.Resolve( material?.Name, material?.Grade, - MachineSettings.FindMachine(Nest.Machine), + machine, MachineSettings.LoadMaterials() ); - var snapshot = PartPrintoutBuilder.Capture(Nest, drawings, DateTimeOffset.Now, catalogDensity); + var snapshot = PartPrintoutBuilder.Capture(Nest, drawings, DateTimeOffset.Now, catalogDensity) with + { + ThicknessName = MachineThicknessName(machine, Nest), + }; PartPrintoutWriter.Write(snapshot, destination); } diff --git a/docs/nest-reports.md b/docs/nest-reports.md index 83e9d84a..8f8d2428 100644 --- a/docs/nest-reports.md +++ b/docs/nest-reports.md @@ -38,7 +38,8 @@ suggesting `.printout.pdf` or `.printout.pdf`. Each par gets one Letter portrait page, bookmarked by name, with the name and customer (the drawing's, else the nest's) as the header, the part's material contours stroked to fit (leads, rapids and marks omitted), a details table and the generated time. The table shows the -bounding box (Y extent x X extent), nest material and thickness, cut length and +bounding box (Y extent x X extent), nest material and thickness (led by the +machine config's thickness name, as in the nest report), cut length and pierce count, net area, weight (the nest report's density rules) and required quantity. Cut length sums the moves the head cuts (cut, break, common-line and lead layers); each run of them is one pierce, so scribe marks, tab spans, display moves and a display