fix(core): stop Program.BoundingBox including the origin
Min/max were seeded at 0, so any geometry not touching the origin got an inflated box, and the first move only updated max (else-if). Rotated canonical drawings are the common trigger: their origin ends up outside the shape, which skewed Part bounds and bbox-based alignment. Track the real extents and keep returning a zero box for empty programs. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -305,22 +305,27 @@ namespace OpenNest.CNC
|
||||
return new Vector(0, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bounding box of the geometry the program visits. The tool's starting position is not
|
||||
/// part of the geometry, so the origin only contributes when the program reaches it.
|
||||
/// An empty program returns a zero-size box at the origin.
|
||||
/// </summary>
|
||||
public Box BoundingBox()
|
||||
{
|
||||
var origin = new Vector(0, 0);
|
||||
return BoundingBox(ref origin);
|
||||
return BoundingBox(ref origin, out var box) ? box : new Box(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
private Box BoundingBox(ref Vector pos)
|
||||
private bool BoundingBox(ref Vector pos, out Box result)
|
||||
{
|
||||
// Capture the frame origin at entry. Sub-program Offsets and
|
||||
// absolute-mode endpoints are relative to this fixed origin.
|
||||
var frameOrigin = pos;
|
||||
|
||||
double minX = 0.0;
|
||||
double minY = 0.0;
|
||||
double maxX = 0.0;
|
||||
double maxY = 0.0;
|
||||
var minX = double.PositiveInfinity;
|
||||
var minY = double.PositiveInfinity;
|
||||
var maxX = double.NegativeInfinity;
|
||||
var maxY = double.NegativeInfinity;
|
||||
|
||||
for (int i = 0; i < Codes.Count; ++i)
|
||||
{
|
||||
@@ -338,12 +343,12 @@ namespace OpenNest.CNC
|
||||
|
||||
if (pt.X > maxX)
|
||||
maxX = pt.X;
|
||||
else if (pt.X < minX)
|
||||
if (pt.X < minX)
|
||||
minX = pt.X;
|
||||
|
||||
if (pt.Y > maxY)
|
||||
maxY = pt.Y;
|
||||
else if (pt.Y < minY)
|
||||
if (pt.Y < minY)
|
||||
minY = pt.Y;
|
||||
|
||||
pos = pt;
|
||||
@@ -361,12 +366,12 @@ namespace OpenNest.CNC
|
||||
|
||||
if (pt.X > maxX)
|
||||
maxX = pt.X;
|
||||
else if (pt.X < minX)
|
||||
if (pt.X < minX)
|
||||
minX = pt.X;
|
||||
|
||||
if (pt.Y > maxY)
|
||||
maxY = pt.Y;
|
||||
else if (pt.Y < minY)
|
||||
if (pt.Y < minY)
|
||||
minY = pt.Y;
|
||||
|
||||
pos = pt;
|
||||
@@ -470,7 +475,8 @@ namespace OpenNest.CNC
|
||||
// Sub-program frame origin in this program's frame
|
||||
// is frameOrigin + Offset, regardless of current pos.
|
||||
pos = frameOrigin + subpgm.Offset;
|
||||
var box = subpgm.Program.BoundingBox(ref pos);
|
||||
if (!subpgm.Program.BoundingBox(ref pos, out var box))
|
||||
break;
|
||||
|
||||
if (box.Left < minX)
|
||||
minX = box.Left;
|
||||
@@ -489,7 +495,14 @@ namespace OpenNest.CNC
|
||||
}
|
||||
}
|
||||
|
||||
return new Box(minX, minY, maxX - minX, maxY - minY);
|
||||
if (minX > maxX || minY > maxY)
|
||||
{
|
||||
result = new Box(0, 0, 0, 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
result = new Box(minX, minY, maxX - minX, maxY - minY);
|
||||
return true;
|
||||
}
|
||||
|
||||
public object Clone()
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
using OpenNest.CNC;
|
||||
using OpenNest.Geometry;
|
||||
|
||||
namespace OpenNest.Tests.CNC;
|
||||
|
||||
public class ProgramBoundingBoxTests
|
||||
{
|
||||
[Fact]
|
||||
public void GeometryAwayFromOrigin_DoesNotIncludeOrigin()
|
||||
{
|
||||
var pgm = new OpenNest.CNC.Program();
|
||||
pgm.Codes.Add(new RapidMove(new Vector(10, 10)));
|
||||
pgm.Codes.Add(new LinearMove(new Vector(20, 10)));
|
||||
pgm.Codes.Add(new LinearMove(new Vector(20, 30)));
|
||||
pgm.Codes.Add(new LinearMove(new Vector(10, 30)));
|
||||
pgm.Codes.Add(new LinearMove(new Vector(10, 10)));
|
||||
|
||||
var box = pgm.BoundingBox();
|
||||
|
||||
Assert.Equal(10, box.Left, precision: 6);
|
||||
Assert.Equal(10, box.Bottom, precision: 6);
|
||||
Assert.Equal(20, box.Right, precision: 6);
|
||||
Assert.Equal(30, box.Top, precision: 6);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GeometryBelowAndLeftOfOrigin_IsTrackedExactly()
|
||||
{
|
||||
var pgm = new OpenNest.CNC.Program();
|
||||
pgm.Codes.Add(new RapidMove(new Vector(-30, -20)));
|
||||
pgm.Codes.Add(new LinearMove(new Vector(-10, -20)));
|
||||
pgm.Codes.Add(new LinearMove(new Vector(-10, -5)));
|
||||
|
||||
var box = pgm.BoundingBox();
|
||||
|
||||
Assert.Equal(-30, box.Left, precision: 6);
|
||||
Assert.Equal(-20, box.Bottom, precision: 6);
|
||||
Assert.Equal(-10, box.Right, precision: 6);
|
||||
Assert.Equal(-5, box.Top, precision: 6);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EmptyProgram_ReturnsZeroBox()
|
||||
{
|
||||
var box = new OpenNest.CNC.Program().BoundingBox();
|
||||
|
||||
Assert.Equal(0, box.Width, precision: 6);
|
||||
Assert.Equal(0, box.Length, precision: 6);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user