using System; using System.Collections.Generic; using System.Linq; namespace OpenNest.Engine.Strategies { /// /// The built-in fill strategies in pipeline order, with one process-wide disabled set that the /// desktop Options form sets. Every fill that runs the strategy pipeline reads it. /// public static class FillStrategyRegistry { // Pipeline order, ascending by each strategy's Order value. private static readonly IReadOnlyList builtIn = Array.AsReadOnly( new IFillStrategy[] { new PairsFillStrategy(), new RowFillStrategy(), new ColumnFillStrategy(), new CircleFillStrategy(), new RectBestFitStrategy(), new ExtentsFillStrategy(), new LinearFillStrategy(), new TiltedRemnantStrategy(), } ); private static IReadOnlyList strategies = builtIn; private static List sorted; private static HashSet enabledFilter; private static readonly HashSet disabled = new(StringComparer.OrdinalIgnoreCase); public static IReadOnlyList Strategies => sorted ??= FilterStrategies(); /// /// Returns all strategies regardless of enabled/disabled state. /// public static IReadOnlyList AllStrategies => strategies; /// /// Returns the names of all permanently disabled strategies. /// public static IReadOnlyCollection DisabledNames => disabled; private static List FilterStrategies() { var source = enabledFilter != null ? strategies.Where(s => enabledFilter.Contains(s.Name)) : strategies.Where(s => !disabled.Contains(s.Name)); return source.ToList(); } /// /// Permanently disables strategies by name. They remain listed /// but are excluded from the default pipeline. /// public static void Disable(params string[] names) { foreach (var name in names) disabled.Add(name); sorted = null; } /// /// Re-enables a previously disabled strategy. /// public static void Enable(params string[] names) { foreach (var name in names) disabled.Remove(name); sorted = null; } /// /// Restricts the active strategies to only those whose names are listed. /// Pass null to restore all strategies. /// public static void SetEnabled(params string[] names) { enabledFilter = names != null && names.Length > 0 ? new HashSet(names, StringComparer.OrdinalIgnoreCase) : null; sorted = null; } /// /// Replaces the built-in list, in the given order, until the result is disposed. For tests /// that drive fills through a host that builds its own filler, such as the plate view. /// internal static IDisposable UseForTesting(IReadOnlyList replacement) { var prior = strategies; strategies = replacement; sorted = null; return new Restore(prior); } private sealed class Restore : IDisposable { private IReadOnlyList prior; public Restore(IReadOnlyList prior) => this.prior = prior; public void Dispose() { if (prior == null) return; strategies = prior; sorted = null; prior = null; } } } }