Files
OpenNest/OpenNest.Reporting/NestReportSnapshot.cs
T
aj 74d2b6aa68 feat(reporting): add one-plate nest report PDF library slice
Add OpenNest.Reporting (net8.0, PDFsharp-MigraDoc 6.2.4) with a detached
report snapshot and a PDF writer for an empty/demand-only job or a single
plate layout: Letter portrait summary with vector part thumbnails and a
landscape plate page with a vector sheet diagram, both with Page X of Y.

- Snapshot uses reference identity for document-local R### IDs, recounts
  nested quantities from placements x copies with checked wide integers,
  excludes cutoffs from accounting, and retains no live domain objects.
- Geometry keeps Cut/Display material paths, drops rapid/scribe/lead paths,
  preserves native arcs, holes and intentional tab gaps, and rejects
  malformed programs with plate/part identification.
- Open contours and cutoffs are stroked per contour so PDFsharp cannot join
  a tab gap to the next contour.
- Bundled DejaVu Sans via a custom resolver (including MigraDoc's error
  font); unsupported text fails with the field and code point.
- Layouts beyond this slice (multiple plates, page overflow, labels that
  do not fit) fail with NotSupportedException before the destination is
  replaced; output is rendered to a temporary sibling and moved atomically.
2026-09-29 13:13:32 -04:00

39 lines
1.7 KiB
C#

using System.Collections.Immutable;
namespace OpenNest.Reporting;
public sealed record ReportPoint(double X, double Y);
public sealed record ReportBounds(double Left, double Bottom, double Right, double Top)
{
public double Length => Right - Left;
public double Width => Top - Bottom;
}
/// <summary>Native line or arc; angles are radians and a null center denotes a line.</summary>
public sealed record ReportSegment(ReportPoint Start, ReportPoint End, ReportPoint? Center,
double Radius, double StartAngle, double SweepAngle);
public sealed record ReportContour(ImmutableArray<ReportSegment> Segments, bool Closed);
public sealed record ReportGeometry(ImmutableArray<ReportContour> Contours, ReportBounds Bounds)
{
public bool StrokeOnly => Contours.Any(contour => !contour.Closed);
}
public sealed record ReportDrawing(string Id, string Name, long Required, long Nested,
long Shortage, long Extra, ImmutableArray<int> Plates, ReportGeometry Geometry);
public sealed record ReportPlacement(string ReportId, ReportGeometry Geometry);
public sealed record ReportPlate(int Number, ReportBounds Bounds, int Copies, double PartSpacing,
double Utilization, ImmutableArray<ReportPlacement> Parts, ImmutableArray<ReportGeometry> Cutoffs);
/// <summary>A detached value snapshot. No domain objects or mutable geometry are retained.</summary>
public sealed record NestReportSnapshot(string Name, string Customer, string Notes, string Material,
string Grade, double Thickness, string Units, DateTimeOffset GeneratedAt,
ImmutableArray<ReportDrawing> Drawings, ImmutableArray<ReportPlate> Plates)
{
public long TotalSheets => Plates.Aggregate(0L, (total, plate) => checked(total + plate.Copies));
}