using System.Collections.Generic; using OpenNest.Geometry; using OpenNest.Math; namespace OpenNest.Engine.CirclePacking { internal abstract class FillEngine { public FillEngine(Bin bin) { Bin = bin; } public Bin Bin { get; set; } public abstract void Fill(Item item); public abstract void Fill(Item item, int maxCount); /// /// Lays staggered stripes apart into , /// odd stripes shifted along their length by . The last stripes /// may instead stack straight: a diameter apart, each repeating the last staggered stripe's /// positions. Straight stacking climbs faster per stripe than staggering, so where the /// sheet leaves too little for one more staggered stripe, a few straight ones can fit more. /// Every switch point is tried, all staggered and all straight included, and the one placing /// the most is kept; a tie keeps the stripes staggered. /// protected static void AddStripes(Bin bin, Item item, Stripes stripes) { var staggered = new List(); for (var outer = stripes.OuterStart; outer <= stripes.OuterMax; outer += stripes.OuterStep) staggered.Add(outer); var counts = new int[staggered.Count]; var total = 0; for (var stripe = 0; stripe < counts.Length; stripe++) { for (var inner = InnerStart(stripes, stripe); inner <= stripes.InnerMax; inner += stripes.InnerStep) counts[stripe]++; total += counts[stripe]; } var switchAt = staggered.Count; var straightCount = 0; var best = total; var placed = 0; for (var stripe = 0; stripe < counts.Length; stripe++) { placed += counts[stripe]; var straight = 0; for (var outer = staggered[stripe] + item.Diameter; outer <= stripes.OuterMax; outer += item.Diameter) straight++; if (placed + straight * counts[stripe] > best) { best = placed + straight * counts[stripe]; switchAt = stripe + 1; straightCount = straight; } } for (var stripe = 0; stripe < switchAt; stripe++) AddStripe(bin, item, stripes, staggered[stripe], InnerStart(stripes, stripe)); var last = switchAt - 1; var straightOuter = last >= 0 ? staggered[last] : 0; for (var stripe = 0; stripe < straightCount; stripe++) { straightOuter += item.Diameter; AddStripe(bin, item, stripes, straightOuter, InnerStart(stripes, last)); } } private static double InnerStart(Stripes stripes, int stripe) => stripe.IsOdd() ? stripes.InnerStart + stripes.Shift : stripes.InnerStart; private static void AddStripe(Bin bin, Item item, Stripes stripes, double outer, double innerStart) { for (var inner = innerStart; inner <= stripes.InnerMax; inner += stripes.InnerStep) { var addedItem = item.Clone(); addedItem.Center = stripes.InnerAlongX ? new Vector(inner, outer) : new Vector(outer, inner); bin.Items.Add(addedItem); } } /// /// Stripes run along the inner axis, X when , and step along the /// other; starts and maxima are circle centers. /// protected readonly record struct Stripes( bool InnerAlongX, double OuterStart, double OuterMax, double OuterStep, double InnerStart, double InnerMax, double InnerStep, double Shift ); } }