mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-11-06 12:25:43 -05:00
Merge branch 'fix/report-group-ids'
This commit is contained in:
commit
54cea0c325
3 files changed
+51
-11
No files matched your search
@@ -46,15 +46,20 @@ internal static class NestReportDiagram
|
||||
var view = new XRect(0, 0, width, height);
|
||||
var fit = Fit.Create(union, new XRect(Inset, Inset, width - 2 * Inset, height - 2 * Inset));
|
||||
var labels = PlaceLabels(plate, shapes, obstacles, all, fit, view, font, out var failed);
|
||||
var groups = PlaceGroups(plate, failed, labels, fit, view, font);
|
||||
var groups = PlaceGroups(plate, obstacles, failed, labels, fit, view, font);
|
||||
return new DiagramView(canvas, fit, labels, groups);
|
||||
}
|
||||
|
||||
private static List<DiagramGroup> PlaceGroups(ReportPlate plate, List<int> failed,
|
||||
/// <summary>
|
||||
/// Groups unlabeled same-ID neighbors. Clearance against other parts and cutoffs is tested on
|
||||
/// their real contours, not their bounding boxes: a cluster nested in a large part's hole or
|
||||
/// concave notch lies inside that part's bounding box but touches none of its material.
|
||||
/// </summary>
|
||||
private static List<DiagramGroup> PlaceGroups(ReportPlate plate, List<Shape> obstacles, List<int> failed,
|
||||
List<DiagramLabel> labels, Fit fit, XRect view, XFont font)
|
||||
{
|
||||
// Obstacles are the parts, in plate order, followed by the cutoffs.
|
||||
var boxes = plate.Parts.Select(part => fit.Rect(part.Geometry.Bounds)).ToList();
|
||||
var cutoffs = plate.Cutoffs.Select(cutoff => fit.Rect(cutoff.Bounds)).ToList();
|
||||
var groups = new List<DiagramGroup>();
|
||||
// A small screen-space gap also accommodates tightly packed parts with unset spacing.
|
||||
var gap = System.Math.Max(4, plate.PartSpacing * fit.Scale * 2);
|
||||
@@ -89,8 +94,7 @@ internal static class NestReportDiagram
|
||||
var memberSet = members.ToHashSet();
|
||||
// A rectangular group must not imply that unrelated or ungrouped parts belong to it.
|
||||
if (!view.Contains(boundary)
|
||||
|| boxes.Where((_, index) => !memberSet.Contains(index)).Any(box => box.IntersectsWith(boundary))
|
||||
|| cutoffs.Any(box => box.IntersectsWith(boundary))
|
||||
|| obstacles.Where((_, index) => !memberSet.Contains(index)).Any(shape => shape.Crosses(ModelBox(fit, boundary)))
|
||||
|| labels.Any(label => label.Box.IntersectsWith(boundary))
|
||||
|| groups.Any(group => group.Boundary.IntersectsWith(boundary) || group.Label.Box.IntersectsWith(boundary)))
|
||||
continue;
|
||||
@@ -112,8 +116,7 @@ internal static class NestReportDiagram
|
||||
{
|
||||
var padded = box;
|
||||
padded.Inflate(Clearance, Clearance);
|
||||
if (!view.Contains(padded) || boxes.Any(other => other.IntersectsWith(padded))
|
||||
|| cutoffs.Any(other => other.IntersectsWith(padded))
|
||||
if (!view.Contains(padded) || obstacles.Any(shape => shape.Crosses(ModelBox(fit, padded)))
|
||||
|| labels.Any(label => label.Box.IntersectsWith(padded))
|
||||
|| groups.Any(group => group.Boundary.IntersectsWith(padded) || group.Label.Box.IntersectsWith(padded)))
|
||||
continue;
|
||||
@@ -143,9 +146,9 @@ internal static class NestReportDiagram
|
||||
var height = size.Height + 2 * LabelPadding;
|
||||
var pole = fit.Point(shapes[index].Pole.X, shapes[index].Pole.Y);
|
||||
var box = new XRect(pole.X - width / 2, pole.Y - height / 2, width, height);
|
||||
var topLeft = fit.Model(new XPoint(box.X - Clearance, box.Y - Clearance));
|
||||
var bottomRight = fit.Model(new XPoint(box.Right + Clearance, box.Bottom + Clearance));
|
||||
var test = new Box(topLeft.X, bottomRight.Y, bottomRight.X - topLeft.X, topLeft.Y - bottomRight.Y);
|
||||
var padded = box;
|
||||
padded.Inflate(Clearance, Clearance);
|
||||
var test = ModelBox(fit, padded);
|
||||
if (view.Contains(box) && !placed.Any(label => label.Box.IntersectsWith(box))
|
||||
&& !obstacles.Any(shape => shape.Crosses(test)))
|
||||
placed.Add(new DiagramLabel(index, id, box));
|
||||
@@ -155,6 +158,14 @@ internal static class NestReportDiagram
|
||||
return placed;
|
||||
}
|
||||
|
||||
/// <summary>The model-space rectangle under a canvas rectangle.</summary>
|
||||
private static Box ModelBox(Fit fit, XRect rect)
|
||||
{
|
||||
var topLeft = fit.Model(new XPoint(rect.Left, rect.Top));
|
||||
var bottomRight = fit.Model(new XPoint(rect.Right, rect.Bottom));
|
||||
return new Box(topLeft.X, bottomRight.Y, bottomRight.X - topLeft.X, topLeft.Y - bottomRight.Y);
|
||||
}
|
||||
|
||||
private static ReportBounds Union(ReportPlate plate, IEnumerable<Shape> shapes)
|
||||
{
|
||||
var left = plate.Bounds.Left;
|
||||
|
||||
@@ -209,6 +209,34 @@ public sealed class NestPdfLayoutTests : IDisposable
|
||||
Assert.Equal(2, plan.Labels.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Plan_GroupNestedInsideALargerPartsCutoutIsStillLabeled()
|
||||
{
|
||||
// A frame whose cutout holds the washer cluster: its bounding box covers the group, but
|
||||
// its material does not, so the group outline and ID are still clear.
|
||||
var nest = NestReportTestData.CreateDenseNest();
|
||||
var frame = new Program();
|
||||
frame.Codes.Add(new RapidMove(0, 0));
|
||||
frame.Codes.Add(new LinearMove(0, 30));
|
||||
frame.Codes.Add(new LinearMove(40, 30));
|
||||
frame.Codes.Add(new LinearMove(40, 0));
|
||||
frame.Codes.Add(new LinearMove(0, 0));
|
||||
frame.Codes.Add(new RapidMove(5, 5));
|
||||
frame.Codes.Add(new LinearMove(35, 5));
|
||||
frame.Codes.Add(new LinearMove(35, 25));
|
||||
frame.Codes.Add(new LinearMove(5, 25));
|
||||
frame.Codes.Add(new LinearMove(5, 5));
|
||||
nest.Plates[0].Parts.Add(new Part(new Drawing("Frame", frame), new Vector(50, 20)));
|
||||
var snapshot = NestReportBuilder.Capture(nest, NestReportTestData.GeneratedAt);
|
||||
var plan = NestReportDiagram.Plan(snapshot.Plates[0], 720, 320);
|
||||
var group = Assert.Single(plan.Groups);
|
||||
Assert.Equal("2", group.Label.Id);
|
||||
Assert.Equal(24, group.Parts.Count);
|
||||
var frameBox = plan.Fit.Rect(snapshot.Plates[0].Parts[^1].Geometry.Bounds);
|
||||
Assert.True(frameBox.Contains(group.Boundary));
|
||||
Assert.True(frameBox.Contains(group.Label.Box));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Plan_SeparatedClustersOfTheSamePartGetSeparateGroups()
|
||||
{
|
||||
|
||||
@@ -70,7 +70,8 @@ quantity tables even when its diagram label cannot fit.
|
||||
Nearby small parts with the same report ID may share a dotted rectangular outline
|
||||
and one ID beside it. Separate clusters get separate outlines. A group is omitted
|
||||
if its rectangle includes any other part or cutoff, or if there is no clear space
|
||||
for its label. Dotted group outlines are distinct from dashed scrap cutoffs.
|
||||
for its label. Clearance is tested against real contours, not bounding boxes, so a
|
||||
cluster nested in a larger part's cutout or notch is still grouped and labeled. Dotted group outlines are distinct from dashed scrap cutoffs.
|
||||
|
||||
The writer still rejects, with `NotSupportedException` and before touching
|
||||
the destination:
|
||||
|
||||
Reference in new issue
Block a user