feat(engine): add built-in Rectangles and Irregular nesting engines
Moves the two production plug-in engines into OpenNest.Engine under names that describe the jobs they suit: - Rectangles: plain and near-rectangular plates, maximal-rectangles box packing (was the RectanglesNestingEngine plug-in) - Irregular: irregular profiles, no-fit-polygon frontier packing (was the Opus55NestingEngine plug-in) Their tests and the shared engine contract/layout test kit move into OpenNest.Engine.Tests/NestingEngines. The registry maps the old plug-in names to the new engines, so saved desktop selections, scripts and API requests keep working, and a leftover plug-in DLL under an old name cannot shadow its replacement. Desktop startup passes the registry's lookup when restoring the saved Auto Nest engine.
This commit is contained in:
@@ -1,24 +1,49 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using OpenNest.Engine.NestingEngines.Irregular;
|
||||
using OpenNest.Engine.NestingEngines.Rectangles;
|
||||
|
||||
namespace OpenNest.Engine.Jobs;
|
||||
|
||||
/// <summary>
|
||||
/// Registry of whole-job <see cref="INestingEngine"/> implementations. The four production
|
||||
/// strategies are exposed through <see cref="FixedStrategyNestingEngine"/> so they compete on
|
||||
/// equal footing with model-submitted engines. Callers choose an engine explicitly from
|
||||
/// <see cref="AvailableEngines"/>; there is no process-global active selection.
|
||||
/// strategies are exposed through <see cref="FixedStrategyNestingEngine"/>. Callers choose an
|
||||
/// engine explicitly from <see cref="AvailableEngines"/>; there is no process-global active
|
||||
/// selection. Plug-ins loaded from an Engines/ folder register under their CLR type name.
|
||||
/// </summary>
|
||||
public static class NestingEngineRegistry
|
||||
{
|
||||
private static readonly List<NestingEngineInfo> engines = new();
|
||||
|
||||
/// <summary>
|
||||
/// Registry names used by earlier releases, mapped to the engine that replaced them, so saved
|
||||
/// selections and scripts keep working. Consulted only when no engine has the requested name.
|
||||
/// </summary>
|
||||
private static readonly Dictionary<string, string> RenamedEngines = new(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
["Opus55NestingEngine"] = "Irregular",
|
||||
["RectanglesNestingEngine"] = "Rectangles",
|
||||
};
|
||||
|
||||
static NestingEngineRegistry()
|
||||
{
|
||||
Register(
|
||||
"Rectangles",
|
||||
"Plain and near-rectangular parts: maximal-rectangles box packing",
|
||||
() => new RectanglesNestingEngine()
|
||||
);
|
||||
|
||||
Register(
|
||||
"Irregular",
|
||||
"Irregular parts: no-fit-polygon frontier packing with look-ahead stock selection",
|
||||
() => new IrregularNestingEngine()
|
||||
);
|
||||
|
||||
Register(
|
||||
"StockLadder",
|
||||
"Caller-stock constrained-first fill and equivalent-demand area repacking",
|
||||
@@ -53,13 +78,33 @@ public static class NestingEngineRegistry
|
||||
public static IReadOnlyList<NestingEngineInfo> AvailableEngines => engines;
|
||||
|
||||
/// <summary>
|
||||
/// Creates the engine registered under <paramref name="name"/> (case-insensitive). The caller's
|
||||
/// explicit choice is the whole selection mechanism; unknown names throw.
|
||||
/// Registered name for <paramref name="name"/>: an exact (case-insensitive) match, else the
|
||||
/// engine a renamed legacy name now maps to, else null. Hosts use this to restore a saved
|
||||
/// selection made under an old name.
|
||||
/// </summary>
|
||||
public static string? ResolveName(string? name)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
return null;
|
||||
var trimmed = name.Trim();
|
||||
var info = engines.FirstOrDefault(e => e.Name.Equals(trimmed, StringComparison.OrdinalIgnoreCase));
|
||||
if (info != null)
|
||||
return info.Name;
|
||||
return RenamedEngines.TryGetValue(trimmed, out var renamed)
|
||||
&& engines.FirstOrDefault(e => e.Name.Equals(renamed, StringComparison.OrdinalIgnoreCase)) is { } target
|
||||
? target.Name
|
||||
: null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the engine registered under <paramref name="name"/> (case-insensitive, renamed legacy
|
||||
/// names accepted). The caller's explicit choice is the whole selection mechanism; unknown names throw.
|
||||
/// </summary>
|
||||
public static INestingEngine Create(string name)
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(name);
|
||||
var info = engines.FirstOrDefault(e => e.Name.Equals(name, StringComparison.OrdinalIgnoreCase));
|
||||
var resolved = ResolveName(name);
|
||||
var info = resolved == null ? null : engines.First(e => e.Name == resolved);
|
||||
if (info == null)
|
||||
throw new NotSupportedException(
|
||||
$"Unknown nesting engine: {name}. Available: {string.Join(", ", engines.Select(e => e.Name))}."
|
||||
@@ -75,6 +120,13 @@ public static class NestingEngineRegistry
|
||||
return;
|
||||
}
|
||||
|
||||
// A leftover plug-in under a renamed engine's old name would shadow its built-in replacement.
|
||||
if (RenamedEngines.ContainsKey(name))
|
||||
{
|
||||
Debug.WriteLine($"[NestingEngineRegistry] '{name}' skipped: replaced by built-in '{RenamedEngines[name]}'");
|
||||
return;
|
||||
}
|
||||
|
||||
engines.Add(new NestingEngineInfo(name, description, factory));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user