diff --git a/OpenNest.Engine/NestingEngines/Irregular/PartCatalog.cs b/OpenNest.Engine/NestingEngines/Irregular/PartCatalog.cs
index 0e42ceb..445150e 100644
--- a/OpenNest.Engine/NestingEngines/Irregular/PartCatalog.cs
+++ b/OpenNest.Engine/NestingEngines/Irregular/PartCatalog.cs
@@ -25,7 +25,11 @@ internal sealed class Orientation
/// Chord deviation used for arcs; footprints are grown by it to stay conservative.
public required double Tolerance { get; init; }
- /// Outline bounds grown by the tolerance, so they contain the true perimeter.
+ ///
+ /// Outline bounds grown by the outline's actual flattening error, so they contain the true
+ /// perimeter: by when arcs were flattened, not at all for line-only
+ /// outlines, whose vertices are exact. Footprints and NFPs still use .
+ ///
public required double MinX { get; init; }
public required double MinY { get; init; }
public required double MaxX { get; init; }
@@ -96,7 +100,7 @@ internal static class PartCatalog
var outline = Polygonize(perimeter, angle, tolerance);
if (outline.Count < 3)
continue;
- orientations.Add(MakeOrientation(index, orientations.Count, angle, outline, tolerance));
+ orientations.Add(MakeOrientation(index, orientations.Count, angle, outline, tolerance, perimeter));
}
var area = orientations.Count == 0 ? 0 : System.Math.Abs(Clipper.Area(orientations[0].Outline));
@@ -123,7 +127,7 @@ internal static class PartCatalog
return null;
var tolerance = type.Orientations[0].Tolerance;
var outline = Polygonize(type.Perimeter, angle, tolerance);
- return outline.Count < 3 ? null : MakeOrientation(type.Index, index, angle, outline, tolerance);
+ return outline.Count < 3 ? null : MakeOrientation(type.Index, index, angle, outline, tolerance, type.Perimeter);
}
private static Shape? ReadPerimeter(PartGeometrySnapshot geometry) =>
@@ -163,9 +167,13 @@ internal static class PartCatalog
return path;
}
- private static Orientation MakeOrientation(int typeIndex, int index, double angle, PathD outline, double tolerance)
+ private static Orientation MakeOrientation(int typeIndex, int index, double angle, PathD outline,
+ double tolerance, Shape perimeter)
{
var bounds = Clipper.GetBounds(outline);
+ // Only flattened arcs deviate from the true perimeter; line vertices are exact, so a
+ // line-only part keeps its nominal material bounds and can fill its work area exactly.
+ var padding = perimeter.Entities.All(e => e.Type == EntityType.Line) ? 0 : tolerance;
return new Orientation
{
TypeIndex = typeIndex,
@@ -173,10 +181,10 @@ internal static class PartCatalog
Rotation = angle,
Outline = outline,
Tolerance = tolerance,
- MinX = bounds.left - tolerance,
- MinY = bounds.top - tolerance, // Clipper RectD: top is the minimum Y.
- MaxX = bounds.right + tolerance,
- MaxY = bounds.bottom + tolerance,
+ MinX = bounds.left - padding,
+ MinY = bounds.top - padding, // Clipper RectD: top is the minimum Y.
+ MaxX = bounds.right + padding,
+ MaxY = bounds.bottom + padding,
};
}