diff --git a/OpenNest/Actions/ActionFillArea.cs b/OpenNest/Actions/ActionFillArea.cs
index 6818e5e..b8e2485 100644
--- a/OpenNest/Actions/ActionFillArea.cs
+++ b/OpenNest/Actions/ActionFillArea.cs
@@ -6,6 +6,8 @@ using System.Threading.Tasks;
using System.Windows.Forms;
using OpenNest.Controls;
using OpenNest.Engine;
+using OpenNest.Engine.Jobs.Placement;
+using OpenNest.Forms;
namespace OpenNest.Actions
{
@@ -46,13 +48,15 @@ namespace OpenNest.Actions
private async void FillArea()
{
+ var strategy = EngineSelection.FillStrategy;
if (progress != null && cts != null)
{
try
{
- var engine = NestEngineRegistry.Create(plateView.Plate);
var parts = await Task.Run(() =>
- engine.Fill(
+ PlateFillService.FillItem(
+ strategy,
+ plateView.Plate,
new NestItem { Drawing = drawing },
SelectedArea,
progress,
@@ -69,8 +73,15 @@ namespace OpenNest.Actions
}
else
{
- var engine = NestEngineRegistry.Create(plateView.Plate);
- engine.Fill(new NestItem { Drawing = drawing }, SelectedArea);
+ var parts = PlateFillService.FillItem(
+ strategy,
+ plateView.Plate,
+ new NestItem { Drawing = drawing },
+ SelectedArea,
+ null,
+ CancellationToken.None
+ );
+ plateView.Plate.Parts.AddRange(parts);
plateView.Invalidate();
}
diff --git a/OpenNest/Controls/PlateView.cs b/OpenNest/Controls/PlateView.cs
index 6564711..3d94702 100644
--- a/OpenNest/Controls/PlateView.cs
+++ b/OpenNest/Controls/PlateView.cs
@@ -16,6 +16,7 @@ using OpenNest.Geometry;
using OpenNest.Math;
using Timer = System.Timers.Timer;
using OpenNest.Engine;
+using OpenNest.Engine.Jobs.Placement;
namespace OpenNest.Controls
{
@@ -602,11 +603,18 @@ namespace OpenNest.Controls
try
{
- var engine = NestEngineRegistry.Create(Plate);
+ var strategy = EngineSelection.FillStrategy;
var spacing = Plate.PartSpacing;
var parts = await Task.Run(() =>
{
- var result = engine.Fill(groupParts, workArea, progress, cts.Token);
+ var result = PlateFillService.FillGroup(
+ strategy,
+ Plate,
+ groupParts,
+ workArea,
+ progress,
+ cts.Token
+ );
Compactor.Settle(result, workArea, spacing);
return result;
});
diff --git a/OpenNest/Forms/AutoNestForm.cs b/OpenNest/Forms/AutoNestForm.cs
index 3067350..a932db2 100644
--- a/OpenNest/Forms/AutoNestForm.cs
+++ b/OpenNest/Forms/AutoNestForm.cs
@@ -87,9 +87,9 @@ namespace OpenNest.Forms
private void LoadEngines()
{
- foreach (var engine in NestEngineRegistry.AvailableEngines)
- engineComboBox.Items.Add(engine.Name);
- engineComboBox.SelectedItem = NestEngineRegistry.ActiveEngineName;
+ foreach (var name in EngineSelection.UiEngineNames)
+ engineComboBox.Items.Add(name);
+ engineComboBox.SelectedItem = EngineSelection.EngineName;
}
private void SetupPartsGrid()
diff --git a/OpenNest/Forms/EngineSelection.cs b/OpenNest/Forms/EngineSelection.cs
new file mode 100644
index 0000000..5ed8ac7
--- /dev/null
+++ b/OpenNest/Forms/EngineSelection.cs
@@ -0,0 +1,53 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using OpenNest.Engine.Jobs;
+using OpenNest.Engine.Jobs.Placement;
+
+namespace OpenNest.Forms
+{
+ ///
+ /// App-scoped nesting-engine selection — the desktop replacement for the process-global
+ /// NestEngineRegistry.ActiveEngineName. The selected name addresses a whole-job
+ /// INestingEngine resolved through NestingEngineRegistry at call time; single-plate
+ /// interactive fill uses FillStrategy, which maps a built-in engine to its placement
+ /// strategy and falls back to Default for jobs-only engines (StockLadder, plug-ins).
+ ///
+ public static class EngineSelection
+ {
+ public const string DefaultEngineName = "Default";
+
+ /// Registered jobs engine deliberately kept out of the desktop combo.
+ public const string HiddenEngineName = "StockLadder";
+
+ private static string engineName = DefaultEngineName;
+
+ public static string EngineName
+ {
+ get { return engineName; }
+ set
+ {
+ engineName = string.IsNullOrWhiteSpace(value)
+ ? DefaultEngineName
+ : value.Trim();
+ }
+ }
+
+ /// Desktop combo contents: registered jobs engines minus StockLadder.
+ public static IEnumerable UiEngineNames =>
+ NestingEngineRegistry.AvailableEngines
+ .Where(e => !e.Name.Equals(HiddenEngineName, StringComparison.OrdinalIgnoreCase))
+ .Select(e => e.Name)
+ .ToList();
+
+ /// Placement strategy for single-plate fill: the selection when it names a
+ /// built-in strategy, otherwise Default.
+ public static string FillStrategy => IsFillStrategy(engineName) ? engineName : DefaultEngineName;
+
+ public static bool IsFillStrategy(string name) =>
+ !string.IsNullOrWhiteSpace(name)
+ && PlateFillService.BuiltInStrategies.Any(
+ s => s.Equals(name.Trim(), StringComparison.OrdinalIgnoreCase)
+ );
+ }
+}
diff --git a/OpenNest/Forms/MainForm.cs b/OpenNest/Forms/MainForm.cs
index 0c4632b..5da13e2 100644
--- a/OpenNest/Forms/MainForm.cs
+++ b/OpenNest/Forms/MainForm.cs
@@ -17,6 +17,9 @@ using OpenNest.Gpu;
using OpenNest.IO;
using OpenNest.Properties;
using OpenNest.Engine;
+using OpenNest.Engine.Jobs;
+using OpenNest.Engine.Jobs.Adapters;
+using OpenNest.Engine.Jobs.Placement;
namespace OpenNest.Forms
{
@@ -68,16 +71,19 @@ namespace OpenNest.Forms
//if (GpuEvaluatorFactory.GpuAvailable)
// BestFitCache.CreateSlideComputer = () => GpuEvaluatorFactory.CreateSlideComputer();
+ // Jobs-side plug-in discovery: INestingEngine implementations are registered per
+ // assembly/type with the same per-DLL isolation as before. Binary plug-ins derived
+ // from the legacy NestEngineBase no longer load here after the Phase-4 removal.
var enginesDir = Path.Combine(Application.StartupPath, "Engines");
- NestEngineRegistry.LoadPlugins(enginesDir);
+ NestingEngineRegistry.LoadPlugins(enginesDir);
OptionsForm.ApplyDisabledStrategies();
ColorSchemeRegistry.ApplyActiveFromSettings();
- foreach (var engine in NestEngineRegistry.AvailableEngines)
- engineComboBox.Items.Add(engine.Name);
+ foreach (var name in EngineSelection.UiEngineNames)
+ engineComboBox.Items.Add(name);
- engineComboBox.SelectedItem = NestEngineRegistry.ActiveEngineName;
+ engineComboBox.SelectedItem = EngineSelection.EngineName;
engineComboBox.SelectedIndexChanged += EngineComboBox_SelectedIndexChanged;
}
@@ -334,7 +340,7 @@ namespace OpenNest.Forms
private void EngineComboBox_SelectedIndexChanged(object sender, EventArgs e)
{
if (engineComboBox.SelectedItem is string name)
- NestEngineRegistry.ActiveEngineName = name;
+ EngineSelection.EngineName = name;
}
private void UpdateLocationMode()
@@ -1009,7 +1015,7 @@ namespace OpenNest.Forms
if (form.EngineName != null)
{
- NestEngineRegistry.ActiveEngineName = form.EngineName;
+ EngineSelection.EngineName = form.EngineName;
engineComboBox.SelectedItem = form.EngineName;
}
@@ -1118,6 +1124,7 @@ namespace OpenNest.Forms
SortOrder = sortOrder,
MinRemnantSize = minRemnantSize,
AllowPlateCreation = allowPlateCreation,
+ Strategy = EngineSelection.FillStrategy,
};
var result = await Task.Run(() =>
@@ -1199,7 +1206,8 @@ namespace OpenNest.Forms
salvageRate,
plate,
progress,
- token
+ token,
+ EngineSelection.FillStrategy
)
);
@@ -1223,10 +1231,19 @@ namespace OpenNest.Forms
}
else
{
- var engine = NestEngineRegistry.Create(plate);
- engine.PlateNumber = plateIndex;
-
- nestParts = await Task.Run(() => engine.Nest(items, progress, token));
+ // Same preview flow as before: fill the current plate's remaining demand with the
+ // selected strategy, then commit the returned parts. App-scoped selection — the
+ // process-global engine registry is never consulted.
+ nestParts = await Task.Run(() =>
+ PlateFillService.Nest(
+ EngineSelection.FillStrategy,
+ plate,
+ items,
+ plateIndex,
+ progress,
+ token
+ )
+ );
}
activeForm.PlateView.ClearPreviewParts();