using OpenNest.Engine; using OpenNest.Converters; using OpenNest.Geometry; using OpenNest.Math; using System.Collections.Generic; using System.Linq; namespace OpenNest.Engine.BestFit { public class BestFitResult { public PairCandidate Candidate { get; set; } public double RotatedArea { get; set; } public double BoundingWidth { get; set; } public double BoundingHeight { get; set; } public double OptimalRotation { get; set; } public bool Keep { get; set; } public string Reason { get; set; } public double TrueArea { get; set; } public List HullAngles { get; set; } public double Utilization { get { return RotatedArea > 0 ? TrueArea / RotatedArea : 0; } } public double LongestSide { get { return System.Math.Max(BoundingWidth, BoundingHeight); } } public double ShortestSide { get { return System.Math.Min(BoundingWidth, BoundingHeight); } } public List BuildParts(Drawing drawing) { var part1 = Part.CreateAtOrigin(drawing); var part2 = Part.CreateAtOrigin(drawing, Candidate.Part2Rotation); part2.Location = Candidate.Part2Offset; part2.UpdateBounds(); if (!OptimalRotation.IsEqualTo(0)) { var pairBounds = ((IEnumerable)new IBoundable[] { part1, part2 }).GetBoundingBox(); var center = pairBounds.Center; part1.Rotate(-OptimalRotation, center); part2.Rotate(-OptimalRotation, center); } var finalBounds = ((IEnumerable)new IBoundable[] { part1, part2 }).GetBoundingBox(); var offset = new Vector(-finalBounds.Left, -finalBounds.Bottom); part1.Offset(offset); part2.Offset(offset); return new List { part1, part2 }; } public List BuildCanonicalParts() { return NormalizeToCutOrigin(BuildParts(Candidate.Drawing)); } public List BuildSourceParts(Drawing drawing) { var parts = BuildCanonicalParts(); var sourceAngle = drawing?.Source?.Angle ?? 0.0; for (var i = 0; i < parts.Count; i++) { var p = parts[i]; var rebound = Part.CreateAtOrigin(drawing, p.Rotation); var delta = p.BoundingBox.Location - rebound.BoundingBox.Location; rebound.Offset(delta); rebound.UpdateBounds(); parts[i] = rebound; } return NormalizeToCutOrigin(CanonicalFrame.FromCanonical(parts, sourceAngle)); } public Box GetCutBounds(List parts) { return GetCutBoundingBox(parts); } private static List NormalizeToCutOrigin(List parts) { if (parts == null || parts.Count == 0) return parts; var bounds = GetCutBoundingBox(parts); var offset = new Vector(-bounds.Left, -bounds.Bottom); foreach (var part in parts) part.Offset(offset); return parts; } private static Box GetCutBoundingBox(List parts) { var entities = new List(); foreach (var part in parts) { var partEntities = ConvertProgram.ToGeometry(part.Program) .Where(e => e.Layer != SpecialLayers.Rapid) .ToList(); foreach (var entity in partEntities) { entity.Offset(part.Location); entities.Add(entity); } } return entities.GetBoundingBox(); } } public enum BestFitSortField { Area, LongestSide, ShortestSide, Type, OriginalSequence, Keep, WhyKeepDrop } }