From 4053f1f989ef5b39b5cf0c80af0123fccaa038c7 Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Thu, 23 Apr 2026 08:20:39 -0400 Subject: [PATCH] fix(core): arc bounding box inflated for near-zero sweep arcs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Arcs with sweep angles smaller than Tolerance.Epsilon were treated as full circles by IsBetweenRad's shortcut check, causing UpdateBounds to expand the bounding box to Center ± Radius. This made zoom-to-fit zoom out far beyond the actual part extents. Skip cardinal angle expansion when sweep is near-zero so the bounding box uses only the arc's start/end points. Co-Authored-By: Claude Opus 4.6 --- OpenNest.Core/Geometry/Arc.cs | 31 ++++++++++--------- .../Geometry/EllipseConverterTests.cs | 24 ++++++++++++++ 2 files changed, 41 insertions(+), 14 deletions(-) diff --git a/OpenNest.Core/Geometry/Arc.cs b/OpenNest.Core/Geometry/Arc.cs index d51a4c9..8a677ec 100644 --- a/OpenNest.Core/Geometry/Arc.cs +++ b/OpenNest.Core/Geometry/Arc.cs @@ -404,26 +404,29 @@ namespace OpenNest.Geometry maxY = startpt.Y; } - var angle1 = StartAngle; - var angle2 = EndAngle; + var sweep = SweepAngle(); + if (sweep > Tolerance.Epsilon) + { + var angle1 = StartAngle; + var angle2 = EndAngle; - // switch the angle to counter clockwise. - if (IsReversed) - Generic.Swap(ref angle1, ref angle2); + if (IsReversed) + Generic.Swap(ref angle1, ref angle2); - if (Angle.IsBetweenRad(Angle.HalfPI, angle1, angle2)) - maxY = Center.Y + Radius; + if (Angle.IsBetweenRad(Angle.HalfPI, angle1, angle2)) + maxY = Center.Y + Radius; - if (Angle.IsBetweenRad(System.Math.PI, angle1, angle2)) - minX = Center.X - Radius; + if (Angle.IsBetweenRad(System.Math.PI, angle1, angle2)) + minX = Center.X - Radius; - const double oneHalfPI = System.Math.PI * 1.5; + const double oneHalfPI = System.Math.PI * 1.5; - if (Angle.IsBetweenRad(oneHalfPI, angle1, angle2)) - minY = Center.Y - Radius; + if (Angle.IsBetweenRad(oneHalfPI, angle1, angle2)) + minY = Center.Y - Radius; - if (Angle.IsBetweenRad(Angle.TwoPI, angle1, angle2)) - maxX = Center.X + Radius; + if (Angle.IsBetweenRad(Angle.TwoPI, angle1, angle2)) + maxX = Center.X + Radius; + } boundingBox.X = minX; boundingBox.Y = minY; diff --git a/OpenNest.Tests/Geometry/EllipseConverterTests.cs b/OpenNest.Tests/Geometry/EllipseConverterTests.cs index 7115260..89a20e2 100644 --- a/OpenNest.Tests/Geometry/EllipseConverterTests.cs +++ b/OpenNest.Tests/Geometry/EllipseConverterTests.cs @@ -2,14 +2,18 @@ using OpenNest.Geometry; using OpenNest.IO; using OpenNest.Math; using Xunit; +using Xunit.Abstractions; using System.Linq; namespace OpenNest.Tests.Geometry; public class EllipseConverterTests { + private readonly ITestOutputHelper _output; private const double Tol = 1e-10; + public EllipseConverterTests(ITestOutputHelper output) => _output = output; + [Fact] public void EvaluatePoint_AtZero_ReturnsMajorAxisEnd() { @@ -245,6 +249,26 @@ public class EllipseConverterTests } } + [Fact] + public void DxfImport_ArcBoundingBoxes_Diagnostic() + { + var path = @"C:\Users\aisaacs\Desktop\11ga tab.dxf"; + if (!System.IO.File.Exists(path)) return; + + var result = Dxf.Import(path); + var all = (System.Collections.Generic.IEnumerable)result.Entities; + var bbox = all.GetBoundingBox(); + _output.WriteLine($"Overall: X={bbox.X:F4} Y={bbox.Y:F4} W={bbox.Length:F4} H={bbox.Width:F4}"); + + for (var i = 0; i < result.Entities.Count; i++) + { + var e = result.Entities[i]; + var b = e.BoundingBox; + var flag = (b.Length > 1 || b.Width > 1) ? " ***" : ""; + _output.WriteLine($"{i + 1,3}. {e.GetType().Name,-8} X={b.X:F4} Y={b.Y:F4} W={b.Length:F4} H={b.Width:F4}{flag}"); + } + } + [Fact] public void ToOpenNest_FlippedNormalZ_ProducesCorrectArcs() {