feat: mirror axis simplifier, bend note propagation, ellipse fixes
Geometry Simplifier: - Replace least-squares circle fitting with mirror axis algorithm that constrains center to perpendicular bisector of chord, guaranteeing zero-gap endpoint connectivity by construction - Golden section search optimizes center position along the axis - Increase default tolerance from 0.005 to 0.5 for practical CNC use - Support existing arcs in simplification runs (sample arc points to find larger replacement arcs spanning lines + arcs together) - Add tolerance zone visualization (offset original geometry ±tolerance) - Show original geometry overlay with orange dashed lines in preview - Add "Original" checkbox to CadConverter for comparing old vs new - Store OriginalEntities on FileListItem to prevent tolerance creep when re-running simplifier with different settings Bend Detection: - Propagate bend notes to collinear bend lines split by cutouts using infinite-line perpendicular distance check - Add bend note text rendering in EntityView at bend line midpoints DXF Import: - Fix trimmed ellipse closing chord: only close when sweep ≈ 2π, preventing phantom lines through slot cutouts Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -29,6 +29,84 @@ public class SolidWorksBendDetectorTests
|
||||
Assert.Empty(bends);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Simplifier_EllipseSegments_FewLargeArcs()
|
||||
{
|
||||
var path = Path.Combine(AppContext.BaseDirectory, "Bending", "TestData", "4526 A14 PT11 Test.dxf");
|
||||
Assert.True(File.Exists(path), $"Test DXF not found: {path}");
|
||||
|
||||
var importer = new OpenNest.IO.DxfImporter { SplinePrecision = 200 };
|
||||
var result = importer.Import(path);
|
||||
|
||||
var shape = new OpenNest.Geometry.Shape();
|
||||
shape.Entities.AddRange(result.Entities);
|
||||
|
||||
// Default tolerance is 0.5 — should produce very few large arcs
|
||||
var simplifier = new OpenNest.Geometry.GeometrySimplifier();
|
||||
var candidates = simplifier.Analyze(shape);
|
||||
|
||||
// With 0.5 tolerance, 2 ellipses (~400 segments) should reduce to a handful of arcs
|
||||
// Dump for visibility then assert
|
||||
var info = string.Join(", ", candidates.Select(c => $"[{c.StartIndex}..{c.EndIndex}]={c.LineCount}lines R={c.FittedArc.Radius:F3}"));
|
||||
Assert.True(candidates.Count <= 10,
|
||||
$"Expected <=10 arcs but got {candidates.Count}: {info}");
|
||||
|
||||
// Each arc should cover many lines
|
||||
foreach (var c in candidates)
|
||||
Assert.True(c.LineCount >= 3, $"Arc [{c.StartIndex}..{c.EndIndex}] only covers {c.LineCount} lines");
|
||||
|
||||
// Arcs should connect to the original geometry within tolerance
|
||||
foreach (var c in candidates)
|
||||
{
|
||||
var firstLine = (OpenNest.Geometry.Line)shape.Entities[c.StartIndex];
|
||||
var lastLine = (OpenNest.Geometry.Line)shape.Entities[c.EndIndex];
|
||||
var arc = c.FittedArc;
|
||||
|
||||
var startGap = firstLine.StartPoint.DistanceTo(arc.StartPoint());
|
||||
var endGap = lastLine.EndPoint.DistanceTo(arc.EndPoint());
|
||||
|
||||
Assert.True(startGap < 1e-9, $"Start gap {startGap} at candidate [{c.StartIndex}..{c.EndIndex}]");
|
||||
Assert.True(endGap < 1e-9, $"End gap {endGap} at candidate [{c.StartIndex}..{c.EndIndex}]");
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Import_TrimmedEllipse_NoClosingChord()
|
||||
{
|
||||
var path = Path.Combine(AppContext.BaseDirectory, "Bending", "TestData", "4526 A14 PT11.dxf");
|
||||
Assert.True(File.Exists(path), $"Test DXF not found: {path}");
|
||||
|
||||
var importer = new OpenNest.IO.DxfImporter();
|
||||
var result = importer.Import(path);
|
||||
|
||||
// The DXF has 2 trimmed ellipses forming an oblong slot.
|
||||
// Trimmed ellipses must not generate a closing chord line.
|
||||
// 83 = 72 lines + 4 arcs + 7 circles + ellipse segments (heavily merged by optimizer)
|
||||
Assert.Equal(83, result.Entities.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DetectBends_SplitBendLine_PropagatesNote()
|
||||
{
|
||||
var path = Path.Combine(AppContext.BaseDirectory, "Bending", "TestData", "4526 A14 PT23.dxf");
|
||||
Assert.True(File.Exists(path), $"Test DXF not found: {path}");
|
||||
|
||||
using var reader = new DxfReader(path);
|
||||
var doc = reader.Read();
|
||||
|
||||
var detector = new SolidWorksBendDetector();
|
||||
var bends = detector.DetectBends(doc);
|
||||
|
||||
Assert.Equal(5, bends.Count);
|
||||
Assert.All(bends, b =>
|
||||
{
|
||||
Assert.NotNull(b.NoteText);
|
||||
Assert.Equal(BendDirection.Up, b.Direction);
|
||||
Assert.Equal(90.0, b.Angle);
|
||||
Assert.Equal(0.125, b.Radius);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DetectBends_RealDxf_ParsesNotesCorrectly()
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user