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:
@@ -63,9 +63,72 @@ namespace OpenNest.IO.Bending
|
||||
bends.Add(bend);
|
||||
}
|
||||
|
||||
PropagateCollinearBendNotes(bends);
|
||||
|
||||
return bends;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For bends without a note (e.g. split by a cutout), copy angle/radius/direction
|
||||
/// from a collinear bend that does have a note.
|
||||
/// </summary>
|
||||
private static void PropagateCollinearBendNotes(List<Bend> bends)
|
||||
{
|
||||
const double angleTolerance = 0.01; // radians
|
||||
const double distanceTolerance = 0.01;
|
||||
|
||||
foreach (var bend in bends)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(bend.NoteText))
|
||||
continue;
|
||||
|
||||
foreach (var other in bends)
|
||||
{
|
||||
if (string.IsNullOrEmpty(other.NoteText))
|
||||
continue;
|
||||
|
||||
if (!AreCollinear(bend, other, angleTolerance, distanceTolerance))
|
||||
continue;
|
||||
|
||||
bend.Direction = other.Direction;
|
||||
bend.Angle = other.Angle;
|
||||
bend.Radius = other.Radius;
|
||||
bend.NoteText = other.NoteText;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool AreCollinear(Bend a, Bend b, double angleTolerance, double distanceTolerance)
|
||||
{
|
||||
var angleA = a.StartPoint.AngleTo(a.EndPoint);
|
||||
var angleB = b.StartPoint.AngleTo(b.EndPoint);
|
||||
|
||||
// Normalize angle difference to [0, PI) since opposite directions are still collinear
|
||||
var diff = System.Math.Abs(angleA - angleB) % System.Math.PI;
|
||||
if (diff > angleTolerance && System.Math.PI - diff > angleTolerance)
|
||||
return false;
|
||||
|
||||
// Perpendicular distance from midpoint of A to the infinite line through B
|
||||
var midA = new Vector(
|
||||
(a.StartPoint.X + a.EndPoint.X) / 2.0,
|
||||
(a.StartPoint.Y + a.EndPoint.Y) / 2.0);
|
||||
|
||||
var dx = b.EndPoint.X - b.StartPoint.X;
|
||||
var dy = b.EndPoint.Y - b.StartPoint.Y;
|
||||
var len = System.Math.Sqrt(dx * dx + dy * dy);
|
||||
|
||||
if (len < 1e-9)
|
||||
return false;
|
||||
|
||||
// 2D cross product gives signed perpendicular distance * length
|
||||
var vx = midA.X - b.StartPoint.X;
|
||||
var vy = midA.Y - b.StartPoint.Y;
|
||||
var perp = System.Math.Abs(vx * dy - vy * dx) / len;
|
||||
|
||||
return perp <= distanceTolerance;
|
||||
}
|
||||
|
||||
private List<ACadSharp.Entities.Line> FindBendLines(CadDocument document)
|
||||
{
|
||||
return document.Entities
|
||||
|
||||
@@ -221,8 +221,9 @@ namespace OpenNest.IO
|
||||
});
|
||||
}
|
||||
|
||||
// Close the ellipse if it's a full ellipse
|
||||
if (lines.Count >= 2)
|
||||
// Close only if it's a full ellipse (sweep ≈ 2π)
|
||||
var sweep = endParam - startParam;
|
||||
if (lines.Count >= 2 && System.Math.Abs(sweep - System.Math.PI * 2.0) < 0.01)
|
||||
{
|
||||
var first = lines.First();
|
||||
var last = lines.Last();
|
||||
|
||||
Reference in New Issue
Block a user