mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-10-27 06:01:06 -04:00
Converter edits rebuild a drawing's program around its first pierce, so Source.Offset can move (legacy drawings with no source entities, or a tabbed/hidden contour changing the first cut). Part.Update kept the old Location, shifting placed parts by the rotated origin delta - about 43 inches on a 270-degree part in the reported nest. Offset rebuilt parts by that delta so their geometry stays put on the plate. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
91 lines
3.5 KiB
C#
91 lines
3.5 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using OpenNest.CNC;
|
|
using OpenNest.Geometry;
|
|
|
|
namespace OpenNest;
|
|
|
|
/// <summary>
|
|
/// Captures drawing program text before an edit and rebuilds only the parts whose
|
|
/// drawing program changed. Drawing keys use reference identity because names are editable.
|
|
/// </summary>
|
|
public sealed class DrawingProgramSnapshot
|
|
{
|
|
private readonly Dictionary<Drawing, Captured> programs;
|
|
private readonly Func<Program, string> fingerprint;
|
|
|
|
private DrawingProgramSnapshot(
|
|
Dictionary<Drawing, Captured> programs,
|
|
Func<Program, string> fingerprint)
|
|
{
|
|
this.programs = programs;
|
|
this.fingerprint = fingerprint;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Capture before handing drawings to an editor, including its load operation:
|
|
/// programs can be edited in place. The callback must include both the main
|
|
/// program text and its hole sub-programs, and must not mutate the program.
|
|
/// </summary>
|
|
public static DrawingProgramSnapshot Capture(
|
|
IEnumerable<Drawing> drawings,
|
|
Func<Program, string> fingerprint)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(drawings);
|
|
ArgumentNullException.ThrowIfNull(fingerprint);
|
|
var programs = new Dictionary<Drawing, Captured>(ReferenceEqualityComparer.Instance);
|
|
|
|
foreach (var drawing in drawings)
|
|
{
|
|
if (!drawing.IsCutOff && !programs.ContainsKey(drawing))
|
|
programs.Add(drawing, new Captured(fingerprint(drawing.Program), SourceOffset(drawing)));
|
|
}
|
|
|
|
return new DrawingProgramSnapshot(programs, fingerprint);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Complete the captured edit by rebuilding changed drawings' parts across all plates.
|
|
/// Part.Update clears obsolete lead-ins, tabs and locks; the placement is anchored to the
|
|
/// source frame, so a rebuild that re-origins the program (Source.Offset moved, e.g. a new
|
|
/// first pierce) leaves the geometry where it was on the plate.
|
|
/// Unchanged and uncaptured drawings' parts retain their program instances and state.
|
|
/// Returns the rebuilt parts so a UI can invalidate just their graphics.
|
|
/// </summary>
|
|
public IReadOnlyList<Part> UpdateChangedParts(IEnumerable<Plate> plates)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(plates);
|
|
var originShifts = new Dictionary<Drawing, Vector>(ReferenceEqualityComparer.Instance);
|
|
|
|
foreach (var entry in programs)
|
|
{
|
|
if (!entry.Key.IsCutOff
|
|
&& !string.Equals(entry.Value.Fingerprint, fingerprint(entry.Key.Program), StringComparison.Ordinal))
|
|
originShifts.Add(entry.Key, SourceOffset(entry.Key) - entry.Value.SourceOffset);
|
|
}
|
|
|
|
var updated = new List<Part>();
|
|
foreach (var plate in plates)
|
|
{
|
|
foreach (var part in plate.Parts)
|
|
{
|
|
if (!originShifts.TryGetValue(part.BaseDrawing, out var shift))
|
|
continue;
|
|
|
|
// A source point P sat at Location + R(P - old); keep it there with the new origin.
|
|
var rotation = part.Rotation;
|
|
part.Update();
|
|
if (shift != Vector.Zero)
|
|
part.Offset(shift.Rotate(rotation));
|
|
updated.Add(part);
|
|
}
|
|
}
|
|
|
|
return updated;
|
|
}
|
|
|
|
private static Vector SourceOffset(Drawing drawing) => drawing.Source?.Offset ?? Vector.Zero;
|
|
|
|
private readonly record struct Captured(string Fingerprint, Vector SourceOffset);
|
|
}
|