feat: add GeometrySimplifier.Apply to replace lines with arcs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using OpenNest.Math;
|
using OpenNest.Math;
|
||||||
|
|
||||||
namespace OpenNest.Geometry;
|
namespace OpenNest.Geometry;
|
||||||
@@ -51,7 +52,40 @@ public class GeometrySimplifier
|
|||||||
|
|
||||||
public Shape Apply(Shape shape, List<ArcCandidate> candidates)
|
public Shape Apply(Shape shape, List<ArcCandidate> candidates)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
var selected = candidates
|
||||||
|
.Where(c => c.IsSelected)
|
||||||
|
.OrderBy(c => c.StartIndex)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var newEntities = new List<Entity>();
|
||||||
|
var i = 0;
|
||||||
|
|
||||||
|
foreach (var candidate in selected)
|
||||||
|
{
|
||||||
|
// Copy entities before this candidate
|
||||||
|
while (i < candidate.StartIndex)
|
||||||
|
{
|
||||||
|
newEntities.Add(shape.Entities[i]);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert the fitted arc
|
||||||
|
newEntities.Add(candidate.FittedArc);
|
||||||
|
|
||||||
|
// Skip past the replaced lines
|
||||||
|
i = candidate.EndIndex + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy remaining entities
|
||||||
|
while (i < shape.Entities.Count)
|
||||||
|
{
|
||||||
|
newEntities.Add(shape.Entities[i]);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = new Shape();
|
||||||
|
result.Entities.AddRange(newEntities);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void FindCandidatesInRun(List<Entity> entities, int runStart, int runEnd, List<ArcCandidate> candidates)
|
private void FindCandidatesInRun(List<Entity> entities, int runStart, int runEnd, List<ArcCandidate> candidates)
|
||||||
|
|||||||
@@ -107,4 +107,59 @@ public class GeometrySimplifierTests
|
|||||||
Assert.Equal(6, candidates[1].StartIndex);
|
Assert.Equal(6, candidates[1].StartIndex);
|
||||||
Assert.Equal(9, candidates[1].EndIndex);
|
Assert.Equal(9, candidates[1].EndIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Apply_SingleCandidate_ReplacesLinesWithArc()
|
||||||
|
{
|
||||||
|
// 20 lines approximating a semicircle
|
||||||
|
var arc = new Arc(new Vector(0, 0), 10, 0, System.Math.PI, false);
|
||||||
|
var points = arc.ToPoints(20);
|
||||||
|
var shape = new Shape();
|
||||||
|
for (var i = 0; i < points.Count - 1; i++)
|
||||||
|
shape.Entities.Add(new Line(points[i], points[i + 1]));
|
||||||
|
|
||||||
|
var simplifier = new GeometrySimplifier { Tolerance = 0.1 };
|
||||||
|
var candidates = simplifier.Analyze(shape);
|
||||||
|
var result = simplifier.Apply(shape, candidates);
|
||||||
|
|
||||||
|
Assert.Single(result.Entities);
|
||||||
|
Assert.IsType<Arc>(result.Entities[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Apply_OnlySelectedCandidates_LeavesUnselectedAsLines()
|
||||||
|
{
|
||||||
|
// Two runs of lines with an arc between them
|
||||||
|
var shape = new Shape();
|
||||||
|
var arc1 = new Arc(new Vector(0, 0), 10, 0, System.Math.PI / 2, false);
|
||||||
|
var pts1 = arc1.ToPoints(5);
|
||||||
|
for (var i = 0; i < pts1.Count - 1; i++)
|
||||||
|
shape.Entities.Add(new Line(pts1[i], pts1[i + 1]));
|
||||||
|
|
||||||
|
shape.Entities.Add(new Arc(new Vector(20, 0), 5, 0, System.Math.PI, false));
|
||||||
|
|
||||||
|
var arc2 = new Arc(new Vector(30, 0), 8, 0, System.Math.PI / 3, false);
|
||||||
|
var pts2 = arc2.ToPoints(4);
|
||||||
|
for (var i = 0; i < pts2.Count - 1; i++)
|
||||||
|
shape.Entities.Add(new Line(pts2[i], pts2[i + 1]));
|
||||||
|
|
||||||
|
var simplifier = new GeometrySimplifier { Tolerance = 0.5, MinLines = 3 };
|
||||||
|
var candidates = simplifier.Analyze(shape);
|
||||||
|
|
||||||
|
// Deselect the first candidate
|
||||||
|
candidates[0].IsSelected = false;
|
||||||
|
|
||||||
|
var result = simplifier.Apply(shape, candidates);
|
||||||
|
|
||||||
|
// First run (5 lines) stays as lines + middle arc + second run replaced by arc
|
||||||
|
// 5 original lines + 1 original arc + 1 fitted arc = 7 entities
|
||||||
|
Assert.Equal(7, result.Entities.Count);
|
||||||
|
// First 5 should be lines
|
||||||
|
for (var i = 0; i < 5; i++)
|
||||||
|
Assert.IsType<Line>(result.Entities[i]);
|
||||||
|
// Index 5 is the original arc
|
||||||
|
Assert.IsType<Arc>(result.Entities[5]);
|
||||||
|
// Index 6 is the fitted arc replacing the second run
|
||||||
|
Assert.IsType<Arc>(result.Entities[6]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user