using System.Collections.Generic; using OpenNest.CNC; using OpenNest.Geometry; namespace OpenNest.Posts.Cincinnati; /// /// Shared utilities for splitting CNC programs into features and classifying them. /// public static class FeatureUtils { /// /// Splits a flat list of codes into feature groups, breaking on rapid moves. /// Each feature starts with a rapid move followed by cutting/etching moves. /// public static List> SplitByRapids(List codes) { var features = new List>(); List current = null; foreach (var code in codes) { if (code is RapidMove) { if (current != null) features.Add(current); current = new List { code }; } else { current ??= new List(); current.Add(code); } } if (current != null && current.Count > 0) features.Add(current); return features; } /// /// Classifies features as etch or cut and orders etch features before cut features. /// public static List<(List codes, bool isEtch)> ClassifyAndOrder(List> features) { var result = new List<(List, bool)>(); var etch = new List>(); var cut = new List>(); foreach (var f in features) { if (IsEtch(f)) etch.Add(f); else cut.Add(f); } foreach (var f in etch) result.Add((f, true)); foreach (var f in cut) result.Add((f, false)); return result; } /// /// Splits a part's program into features by rapids, classifies each as etch or cut, /// and orders etch features before cut features. /// public static List<(List codes, bool isEtch)> SplitAndClassify(Part part) => ClassifyAndOrder(SplitByRapids(part.Program.Codes)); /// /// Returns true if any non-rapid move in the feature has LayerType.Scribe. /// public static bool IsEtch(List codes) { foreach (var code in codes) { if (code is LinearMove linear && linear.Layer == LayerType.Scribe) return true; if (code is ArcMove arc && arc.Layer == LayerType.Scribe) return true; } return false; } /// /// Computes the total cut distance of a feature by summing segment lengths. /// public static double ComputeCutDistance(List codes) { var distance = 0.0; var currentPos = Vector.Zero; foreach (var code in codes) { if (code is RapidMove rapid) currentPos = rapid.EndPoint; else if (code is LinearMove linear) { distance += currentPos.DistanceTo(linear.EndPoint); currentPos = linear.EndPoint; } else if (code is ArcMove arc) { distance += currentPos.DistanceTo(arc.EndPoint); currentPos = arc.EndPoint; } } return distance; } }