FillExtents vertical copy distance was not clamped, allowing rows to be placed overlapping each other when slide calculations returned large values. Clamp to pairHeight + partSpacing minimum, matching FillLinear. Also add FillStrategyRegistry.SetEnabled() to restrict which strategies run — useful for isolating individual strategies during troubleshooting. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
95 lines
3.3 KiB
C#
95 lines
3.3 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
|
|
namespace OpenNest.Engine.Strategies
|
|
{
|
|
public static class FillStrategyRegistry
|
|
{
|
|
private static readonly List<IFillStrategy> strategies = new();
|
|
private static List<IFillStrategy> sorted;
|
|
private static HashSet<string> enabledFilter;
|
|
|
|
static FillStrategyRegistry()
|
|
{
|
|
LoadFrom(typeof(FillStrategyRegistry).Assembly);
|
|
}
|
|
|
|
public static IReadOnlyList<IFillStrategy> Strategies =>
|
|
sorted ??= (enabledFilter != null
|
|
? strategies.Where(s => enabledFilter.Contains(s.Name)).OrderBy(s => s.Order).ToList()
|
|
: strategies.OrderBy(s => s.Order).ToList());
|
|
|
|
/// <summary>
|
|
/// Restricts the active strategies to only those whose names are listed.
|
|
/// Pass null to restore all strategies.
|
|
/// </summary>
|
|
public static void SetEnabled(params string[] names)
|
|
{
|
|
enabledFilter = names != null && names.Length > 0
|
|
? new HashSet<string>(names, StringComparer.OrdinalIgnoreCase)
|
|
: null;
|
|
sorted = null;
|
|
}
|
|
|
|
public static void LoadFrom(Assembly assembly)
|
|
{
|
|
foreach (var type in assembly.GetTypes())
|
|
{
|
|
if (type.IsAbstract || type.IsInterface || !typeof(IFillStrategy).IsAssignableFrom(type))
|
|
continue;
|
|
|
|
var ctor = type.GetConstructor(Type.EmptyTypes);
|
|
if (ctor == null)
|
|
{
|
|
Debug.WriteLine($"[FillStrategyRegistry] Skipping {type.Name}: no parameterless constructor");
|
|
continue;
|
|
}
|
|
|
|
try
|
|
{
|
|
var instance = (IFillStrategy)ctor.Invoke(null);
|
|
|
|
if (strategies.Any(s => s.Name.Equals(instance.Name, StringComparison.OrdinalIgnoreCase)))
|
|
{
|
|
Debug.WriteLine($"[FillStrategyRegistry] Duplicate strategy '{instance.Name}' skipped");
|
|
continue;
|
|
}
|
|
|
|
strategies.Add(instance);
|
|
Debug.WriteLine($"[FillStrategyRegistry] Registered: {instance.Name} (Order={instance.Order})");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.WriteLine($"[FillStrategyRegistry] Failed to instantiate {type.Name}: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
sorted = null;
|
|
}
|
|
|
|
public static void LoadPlugins(string directory)
|
|
{
|
|
if (!Directory.Exists(directory))
|
|
return;
|
|
|
|
foreach (var dll in Directory.GetFiles(directory, "*.dll"))
|
|
{
|
|
try
|
|
{
|
|
var assembly = Assembly.LoadFrom(dll);
|
|
LoadFrom(assembly);
|
|
Debug.WriteLine($"[FillStrategyRegistry] Loaded plugin assembly: {Path.GetFileName(dll)}");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.WriteLine($"[FillStrategyRegistry] Failed to load {Path.GetFileName(dll)}: {ex.Message}");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|