diff --git a/OpenNest.Engine/Strategies/FillHelpers.cs b/OpenNest.Engine/Strategies/FillHelpers.cs
index 6533568..44eb252 100644
--- a/OpenNest.Engine/Strategies/FillHelpers.cs
+++ b/OpenNest.Engine/Strategies/FillHelpers.cs
@@ -1,6 +1,7 @@
using OpenNest.Engine.Fill;
using OpenNest.Geometry;
using OpenNest.Math;
+using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;
@@ -63,5 +64,42 @@ namespace OpenNest.Engine.Strategies
return best;
}
+
+ ///
+ /// Runs a fill function with direction preference logic.
+ /// If preferred is null, tries both directions and returns the better result.
+ /// If preferred is set, tries preferred first; only tries other if preferred yields zero.
+ ///
+ public static List FillWithDirectionPreference(
+ Func> fillFunc,
+ NestDirection? preferred,
+ IFillComparer comparer,
+ Box workArea)
+ {
+ if (preferred == null)
+ {
+ var h = fillFunc(NestDirection.Horizontal);
+ var v = fillFunc(NestDirection.Vertical);
+
+ if ((h == null || h.Count == 0) && (v == null || v.Count == 0))
+ return new List();
+
+ if (h == null || h.Count == 0) return v;
+ if (v == null || v.Count == 0) return h;
+
+ return comparer.IsBetter(h, v, workArea) ? h : v;
+ }
+
+ var other = preferred == NestDirection.Horizontal
+ ? NestDirection.Vertical
+ : NestDirection.Horizontal;
+
+ var pref = fillFunc(preferred.Value);
+ if (pref != null && pref.Count > 0)
+ return pref;
+
+ var fallback = fillFunc(other);
+ return fallback ?? new List();
+ }
}
}
diff --git a/OpenNest.Engine/Strategies/FillPolicy.cs b/OpenNest.Engine/Strategies/FillPolicy.cs
new file mode 100644
index 0000000..12c37a9
--- /dev/null
+++ b/OpenNest.Engine/Strategies/FillPolicy.cs
@@ -0,0 +1,8 @@
+namespace OpenNest.Engine.Strategies
+{
+ ///
+ /// Groups engine scoring and direction policy into a single object.
+ /// Set by the engine, consumed by strategies via FillContext.Policy.
+ ///
+ public record FillPolicy(IFillComparer Comparer, NestDirection? PreferredDirection = null);
+}
diff --git a/OpenNest.Tests/FillPolicyTests.cs b/OpenNest.Tests/FillPolicyTests.cs
new file mode 100644
index 0000000..8c49625
--- /dev/null
+++ b/OpenNest.Tests/FillPolicyTests.cs
@@ -0,0 +1,50 @@
+using OpenNest.Engine;
+using OpenNest.Engine.Fill;
+using OpenNest.Engine.Strategies;
+using OpenNest.Geometry;
+
+namespace OpenNest.Tests;
+
+public class FillWithDirectionPreferenceTests
+{
+ private readonly IFillComparer comparer = new DefaultFillComparer();
+ private readonly Box workArea = new(0, 0, 100, 100);
+
+ [Fact]
+ public void NullPreference_TriesBothDirections_ReturnsBetter()
+ {
+ var hParts = new List { TestHelpers.MakePartAt(0, 0, 10), TestHelpers.MakePartAt(12, 0, 10) };
+ var vParts = new List { TestHelpers.MakePartAt(0, 0, 10) };
+
+ var result = FillHelpers.FillWithDirectionPreference(
+ dir => dir == NestDirection.Horizontal ? hParts : vParts,
+ null, comparer, workArea);
+
+ Assert.Equal(2, result.Count);
+ }
+
+ [Fact]
+ public void PreferredDirection_UsedFirst_WhenProducesResults()
+ {
+ var hParts = new List { TestHelpers.MakePartAt(0, 0, 10), TestHelpers.MakePartAt(12, 0, 10) };
+ var vParts = new List { TestHelpers.MakePartAt(0, 0, 10), TestHelpers.MakePartAt(0, 12, 10), TestHelpers.MakePartAt(0, 24, 10) };
+
+ var result = FillHelpers.FillWithDirectionPreference(
+ dir => dir == NestDirection.Horizontal ? hParts : vParts,
+ NestDirection.Horizontal, comparer, workArea);
+
+ Assert.Equal(2, result.Count); // H has results, so H is returned (preferred)
+ }
+
+ [Fact]
+ public void PreferredDirection_FallsBack_WhenPreferredReturnsEmpty()
+ {
+ var vParts = new List { TestHelpers.MakePartAt(0, 0, 10) };
+
+ var result = FillHelpers.FillWithDirectionPreference(
+ dir => dir == NestDirection.Horizontal ? new List() : vParts,
+ NestDirection.Horizontal, comparer, workArea);
+
+ Assert.Equal(1, result.Count); // Falls back to V
+ }
+}