Replace polygon boolean clipping with direct entity splitting using bounding box filtering and exact intersection math. Eliminates Clipper2 precision drift that caused contour gaps (0.0035") breaking area calculation and ShapeBuilder chaining. Also fixes SpikeGrooveSplit: spike depth is now grooveDepth + weldGap (spike protrudes past groove), both V-shapes use same angle formula, and weldGap no longer double-subtracted from tip depth. SplitDrawingForm: fix parameter mapping (GrooveDepth direct from nud, not inflated), remove redundant Spike Depth display, add feature contour preview and trimmed split lines at feature positions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
128 lines
4.1 KiB
C#
128 lines
4.1 KiB
C#
using OpenNest.Converters;
|
|
using OpenNest.Geometry;
|
|
|
|
namespace OpenNest.Tests.Splitting;
|
|
|
|
public class SplitIntegrationTest
|
|
{
|
|
[Fact]
|
|
public void Split_SpikeGroove_NoContinuityGaps()
|
|
{
|
|
// Create a rectangle
|
|
var entities = new List<Entity>
|
|
{
|
|
new Line(new Vector(0, 0), new Vector(100, 0)),
|
|
new Line(new Vector(100, 0), new Vector(100, 50)),
|
|
new Line(new Vector(100, 50), new Vector(0, 50)),
|
|
new Line(new Vector(0, 50), new Vector(0, 0))
|
|
};
|
|
var pgm = ConvertGeometry.ToProgram(entities);
|
|
var drawing = new Drawing("TEST", pgm);
|
|
|
|
var sl = new SplitLine(50.0, CutOffAxis.Vertical);
|
|
sl.FeaturePositions.Add(12.5);
|
|
sl.FeaturePositions.Add(37.5);
|
|
|
|
var parameters = new SplitParameters
|
|
{
|
|
Type = SplitType.SpikeGroove,
|
|
GrooveDepth = 0.625,
|
|
SpikeDepth = 0.75,
|
|
SpikeWeldGap = 0.125,
|
|
SpikeAngle = 45,
|
|
SpikePairCount = 2
|
|
};
|
|
|
|
var results = DrawingSplitter.Split(drawing, new List<SplitLine> { sl }, parameters);
|
|
Assert.Equal(2, results.Count);
|
|
|
|
foreach (var piece in results)
|
|
{
|
|
// Get cut entities only (no rapids)
|
|
var pieceEntities = ConvertProgram.ToGeometry(piece.Program)
|
|
.Where(e => e.Layer != SpecialLayers.Rapid).ToList();
|
|
|
|
// Check that consecutive entity endpoints connect (no gaps)
|
|
for (var i = 0; i < pieceEntities.Count - 1; i++)
|
|
{
|
|
var end = GetEndPoint(pieceEntities[i]);
|
|
var start = GetStartPoint(pieceEntities[i + 1]);
|
|
var gap = end.DistanceTo(start);
|
|
Assert.True(gap < 0.01,
|
|
$"Gap of {gap:F6} between entities {i} and {i + 1} in {piece.Name}");
|
|
}
|
|
|
|
// Area should be non-zero
|
|
Assert.True(piece.Area > 0, $"{piece.Name} has zero area");
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void Split_SpikeGroove_Horizontal_NoContinuityGaps()
|
|
{
|
|
var entities = new List<Entity>
|
|
{
|
|
new Line(new Vector(0, 0), new Vector(100, 0)),
|
|
new Line(new Vector(100, 0), new Vector(100, 50)),
|
|
new Line(new Vector(100, 50), new Vector(0, 50)),
|
|
new Line(new Vector(0, 50), new Vector(0, 0))
|
|
};
|
|
var pgm = ConvertGeometry.ToProgram(entities);
|
|
var drawing = new Drawing("TEST", pgm);
|
|
|
|
var sl = new SplitLine(25.0, CutOffAxis.Horizontal);
|
|
sl.FeaturePositions.Add(25.0);
|
|
sl.FeaturePositions.Add(75.0);
|
|
|
|
var parameters = new SplitParameters
|
|
{
|
|
Type = SplitType.SpikeGroove,
|
|
GrooveDepth = 0.625,
|
|
SpikeDepth = 0.75,
|
|
SpikeWeldGap = 0.125,
|
|
SpikeAngle = 45,
|
|
SpikePairCount = 2
|
|
};
|
|
|
|
var results = DrawingSplitter.Split(drawing, new List<SplitLine> { sl }, parameters);
|
|
Assert.Equal(2, results.Count);
|
|
|
|
foreach (var piece in results)
|
|
{
|
|
var pieceEntities = ConvertProgram.ToGeometry(piece.Program)
|
|
.Where(e => e.Layer != SpecialLayers.Rapid).ToList();
|
|
|
|
for (var i = 0; i < pieceEntities.Count - 1; i++)
|
|
{
|
|
var end = GetEndPoint(pieceEntities[i]);
|
|
var start = GetStartPoint(pieceEntities[i + 1]);
|
|
var gap = end.DistanceTo(start);
|
|
Assert.True(gap < 0.01,
|
|
$"Gap of {gap:F6} between entities {i} and {i + 1} in {piece.Name}");
|
|
}
|
|
|
|
Assert.True(piece.Area > 0, $"{piece.Name} has zero area");
|
|
}
|
|
}
|
|
|
|
private static Vector GetStartPoint(Entity entity)
|
|
{
|
|
return entity switch
|
|
{
|
|
Line l => l.StartPoint,
|
|
Arc a => a.StartPoint(),
|
|
_ => new Vector(0, 0)
|
|
};
|
|
}
|
|
|
|
private static Vector GetEndPoint(Entity entity)
|
|
{
|
|
return entity switch
|
|
{
|
|
Line l => l.EndPoint,
|
|
Arc a => a.EndPoint(),
|
|
_ => new Vector(0, 0)
|
|
};
|
|
}
|
|
}
|