using System.Collections.Generic; using System.Linq; using System.Threading; using OpenNest.Geometry; using OpenNest.Math; namespace OpenNest.CNC.CuttingPlanning; /// /// Whether one part's clean material lies strictly inside a cutout of another's. The cutting /// planner's prerequisites and the pre-post review share this proof, so a pair the planner orders /// inner-before-host is the pair posting checks. Bounds only select candidates; containment is /// proven on native material, and any boundary contact or incomplete material is ambiguous. /// internal static class NestedPartContainment { private const int ContactBudget = 1000000; /// /// True when 's perimeter lies strictly inside one cutout of /// , false when it lies in none; otherwise null, with /// saying why containment cannot be established. /// internal static bool? InsideCutout(LeadMaterialSnapshot inner, LeadMaterialSnapshot host, CancellationToken token, out string ambiguity) { ambiguity = null; if (!inner.IsComplete || !host.IsComplete) { ambiguity = inner.Reason ?? host.Reason; return null; } var budget = ContactBudget; foreach (var curve in inner.Rings[0]) foreach (var ring in host.Rings) foreach (var boundary in ring) { token.ThrowIfCancellationRequested(); if (--budget < 0) { ambiguity = "the containment check exceeds the native query limit"; return null; } if (curve.Contacts(boundary, out var overlap).Count != 0 || overlap) { ambiguity = "their boundaries touch or cross"; return null; } } var probe = inner.Rings[0][0].Start; return host.Rings.Skip(1).Any(hole => LeadMaterialSnapshot.Inside(probe, hole, token)); } /// Material extent of a clean program at a location: rapids and scribe marks excluded. internal static Box CleanBounds(Program clean, Vector location, CancellationToken token) => ExecutionMotionReader.ReadSupported(clean, location, null, token).Motions .Where(m => !m.Rapid && m.Layer is LayerType.Cut or LayerType.Display && m.Curve != null) .Select(m => m.Curve.ToEntity().BoundingBox).ToList().GetBoundingBox(); /// Whether a host's bounds admit an inner part's bounds as a containment candidate. internal static bool Within(Box inner, Box outer) => inner.Left >= outer.Left - Tolerance.Epsilon && inner.Right <= outer.Right + Tolerance.Epsilon && inner.Bottom >= outer.Bottom - Tolerance.Epsilon && inner.Top <= outer.Top + Tolerance.Epsilon; /// The union of two boxes. internal static Box Union(Box a, Box b) { var left = System.Math.Min(a.Left, b.Left); var bottom = System.Math.Min(a.Bottom, b.Bottom); return new(left, bottom, System.Math.Max(a.Right, b.Right) - left, System.Math.Max(a.Top, b.Top) - bottom); } }