diff --git a/OpenNest.Core/Splitting/WeldGapTabSplit.cs b/OpenNest.Core/Splitting/WeldGapTabSplit.cs
new file mode 100644
index 0000000..afd5f0c
--- /dev/null
+++ b/OpenNest.Core/Splitting/WeldGapTabSplit.cs
@@ -0,0 +1,82 @@
+using System.Collections.Generic;
+using OpenNest.Geometry;
+
+namespace OpenNest;
+
+///
+/// Generates rectangular tabs on one side of the split edge (negative side).
+/// The positive side remains a straight line. Tabs act as weld-gap spacers.
+///
+public class WeldGapTabSplit : ISplitFeature
+{
+ public string Name => "Weld-Gap Tabs";
+
+ public SplitFeatureResult GenerateFeatures(SplitLine line, double extentStart, double extentEnd, SplitParameters parameters)
+ {
+ var extent = extentEnd - extentStart;
+ var tabCount = parameters.TabCount;
+ var tabWidth = parameters.TabWidth;
+ var tabHeight = parameters.TabHeight;
+
+ // Evenly space tabs along the split line
+ var spacing = extent / (tabCount + 1);
+
+ var negEntities = new List();
+ var isVertical = line.Axis == CutOffAxis.Vertical;
+ var pos = line.Position;
+
+ // Tabs protrude toward the negative side (lower coordinate on the split axis)
+ var tabDir = -1.0;
+
+ var cursor = extentStart;
+
+ for (var i = 0; i < tabCount; i++)
+ {
+ var tabCenter = extentStart + spacing * (i + 1);
+ var tabStart = tabCenter - tabWidth / 2;
+ var tabEnd = tabCenter + tabWidth / 2;
+
+ if (isVertical)
+ {
+ if (tabStart > cursor + OpenNest.Math.Tolerance.Epsilon)
+ negEntities.Add(new Line(new Vector(pos, cursor), new Vector(pos, tabStart)));
+
+ negEntities.Add(new Line(new Vector(pos, tabStart), new Vector(pos + tabDir * tabHeight, tabStart)));
+ negEntities.Add(new Line(new Vector(pos + tabDir * tabHeight, tabStart), new Vector(pos + tabDir * tabHeight, tabEnd)));
+ negEntities.Add(new Line(new Vector(pos + tabDir * tabHeight, tabEnd), new Vector(pos, tabEnd)));
+ }
+ else
+ {
+ if (tabStart > cursor + OpenNest.Math.Tolerance.Epsilon)
+ negEntities.Add(new Line(new Vector(cursor, pos), new Vector(tabStart, pos)));
+
+ negEntities.Add(new Line(new Vector(tabStart, pos), new Vector(tabStart, pos + tabDir * tabHeight)));
+ negEntities.Add(new Line(new Vector(tabStart, pos + tabDir * tabHeight), new Vector(tabEnd, pos + tabDir * tabHeight)));
+ negEntities.Add(new Line(new Vector(tabEnd, pos + tabDir * tabHeight), new Vector(tabEnd, pos)));
+ }
+
+ cursor = tabEnd;
+ }
+
+ // Final segment from last tab to extent end
+ if (isVertical)
+ {
+ if (extentEnd > cursor + OpenNest.Math.Tolerance.Epsilon)
+ negEntities.Add(new Line(new Vector(pos, cursor), new Vector(pos, extentEnd)));
+ }
+ else
+ {
+ if (extentEnd > cursor + OpenNest.Math.Tolerance.Epsilon)
+ negEntities.Add(new Line(new Vector(cursor, pos), new Vector(extentEnd, pos)));
+ }
+
+ // Positive side: plain straight line (reversed direction)
+ var posEntities = new List();
+ if (isVertical)
+ posEntities.Add(new Line(new Vector(pos, extentEnd), new Vector(pos, extentStart)));
+ else
+ posEntities.Add(new Line(new Vector(extentEnd, pos), new Vector(extentStart, pos)));
+
+ return new SplitFeatureResult(negEntities, posEntities);
+ }
+}
diff --git a/OpenNest.Tests/Splitting/SplitFeatureTests.cs b/OpenNest.Tests/Splitting/SplitFeatureTests.cs
index 5818744..eef2c5c 100644
--- a/OpenNest.Tests/Splitting/SplitFeatureTests.cs
+++ b/OpenNest.Tests/Splitting/SplitFeatureTests.cs
@@ -1,9 +1,53 @@
+using System.Linq;
using OpenNest.Geometry;
namespace OpenNest.Tests.Splitting;
public class SplitFeatureTests
{
+ [Fact]
+ public void WeldGapTabSplit_Vertical_TabsOnNegativeSide()
+ {
+ var feature = new WeldGapTabSplit();
+ var line = new SplitLine(50.0, CutOffAxis.Vertical);
+ var parameters = new SplitParameters
+ {
+ Type = SplitType.WeldGapTabs,
+ TabWidth = 2.0,
+ TabHeight = 0.25,
+ TabCount = 2
+ };
+
+ var result = feature.GenerateFeatures(line, 0.0, 100.0, parameters);
+
+ // Positive side (right): single straight line (no tabs)
+ Assert.Single(result.PositiveSideEdge);
+ Assert.IsType(result.PositiveSideEdge[0]);
+
+ // Negative side (left): has tab protrusions — more than 1 entity
+ Assert.True(result.NegativeSideEdge.Count > 1);
+
+ // All entities should be lines
+ Assert.All(result.NegativeSideEdge, e => Assert.IsType(e));
+
+ // First entity starts at extent start, last ends at extent end
+ var first = (Line)result.NegativeSideEdge[0];
+ var last = (Line)result.NegativeSideEdge[^1];
+ Assert.Equal(0.0, first.StartPoint.Y, 6);
+ Assert.Equal(100.0, last.EndPoint.Y, 6);
+
+ // Tabs protrude in the negative-X direction (left of split line)
+ var tabEntities = result.NegativeSideEdge.Cast().ToList();
+ var minX = tabEntities.Min(l => System.Math.Min(l.StartPoint.X, l.EndPoint.X));
+ Assert.Equal(50.0 - 0.25, minX, 6); // tabHeight = 0.25
+ }
+
+ [Fact]
+ public void WeldGapTabSplit_Name()
+ {
+ Assert.Equal("Weld-Gap Tabs", new WeldGapTabSplit().Name);
+ }
+
[Fact]
public void StraightSplit_Vertical_ProducesSingleLineEachSide()
{