using System; using System.Collections.Generic; using System.Linq; namespace OpenNest { /// /// Parts placed exactly one kerf apart along collinear straight edges, so one centerline cut /// makes both edges (see docs/common-line-cutting.md). Membership is by reference; the shared /// spans are derived from the parts' placed geometry (), never /// stored, so a moved member breaks the group rather than leaving a stale span behind. /// public sealed class CommonLineGroup { private readonly List members; /// The kerf is not finite and positive. /// Fewer than two distinct parts, a null part or a cut-off. public CommonLineGroup(IEnumerable members, double kerf) { ArgumentNullException.ThrowIfNull(members); if (!double.IsFinite(kerf) || kerf <= 0) throw new ArgumentOutOfRangeException(nameof(kerf), kerf, "A common line needs a finite, positive kerf."); var distinct = new List(); foreach (var part in members) { if (part == null) throw new ArgumentException("A common-line group cannot hold a null part.", nameof(members)); if (part.BaseDrawing?.IsCutOff == true) throw new ArgumentException("A cut-off cannot share a common line.", nameof(members)); if (!distinct.Any(existing => ReferenceEquals(existing, part))) distinct.Add(part); } if (distinct.Count < 2) throw new ArgumentException("A common-line group needs at least two parts.", nameof(members)); this.members = distinct; Kerf = kerf; } /// The parts in the group, in the order they were joined. public IReadOnlyList Members => members; /// The gap the members were placed at: the kerf width when the group was made. public double Kerf { get; } public bool Contains(Part part) => members.Any(member => ReferenceEquals(member, part)); /// Drops a member (a part removed from the plate); true when it was one. internal bool Remove(Part part) => members.RemoveAll(member => ReferenceEquals(member, part)) > 0; } }