using System.Collections.Generic; using System.Linq; using System.Threading; using OpenNest.Geometry; 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 or LayerType.DisplayPerimeter && m.Curve != null) .Select(m => m.Curve.ToEntity().BoundingBox).ToList().GetBoundingBox(); }