mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-10-26 21:01:05 -04:00
fix(desktop): draw planned cut-offs and issue markers on the plan preview
The proposal preview skipped the cut-off overlay: it carries cut-off parts but no cut-off definitions, so planner cut-offs looked like thin black part outlines instead of the orange-red dashes used everywhere else. The preview now draws each cut-off part's own cutting moves in that style. The same paint guard hid the validation overlay over a preview, so issue markers for unverified plans were set but never drawn. The overlay now draws on proposals too, with the marker's part numbers mapped to the preview's proposed order so the right parts stay highlighted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c3fa8e79e0
commit
405a867a77
5 files changed
+86
-4
No files matched your search
@@ -217,6 +217,41 @@ public class CuttingInspectionPanelTests
|
||||
Assert.Null(view.ValidationOverlay.Finding);
|
||||
});
|
||||
|
||||
[Fact]
|
||||
public void PlannedCutOffsKeepTheCutOffLineStyleOnTheProposal() => RunSta(() =>
|
||||
{
|
||||
using var editor = Editor();
|
||||
editor.Size = new System.Drawing.Size(1300, 800);
|
||||
editor.Show();
|
||||
editor.PlateManager.LoadAt(0);
|
||||
editor.OpenCuttingInspection();
|
||||
var panel = editor.CuttingInspection!;
|
||||
PumpUntil(() => panel.Proposal != null && !panel.IsPlanning);
|
||||
var view = editor.PlateView;
|
||||
var baseline = CutOffPixels(view);
|
||||
Field<CheckBox>(panel, "autoCutOffsCheckBox").Checked = true;
|
||||
PumpUntil(() => panel.Proposal != null && !panel.IsPlanning);
|
||||
Assert.True(view.HasProposalDisplay);
|
||||
Assert.Contains(view.DisplayPlate.Parts, part => part.BaseDrawing.IsCutOff);
|
||||
Assert.True(CutOffPixels(view) > baseline + 50, "Planned cut-offs should draw as orange-red dashes.");
|
||||
panel.RequestClose();
|
||||
|
||||
static int CutOffPixels(OpenNest.Controls.PlateView view)
|
||||
{
|
||||
using var bitmap = new System.Drawing.Bitmap(view.Width, view.Height);
|
||||
view.DrawToBitmap(bitmap, new System.Drawing.Rectangle(0, 0, view.Width, view.Height));
|
||||
var count = 0;
|
||||
for (var x = 0; x < bitmap.Width; x++)
|
||||
for (var y = 0; y < bitmap.Height; y++)
|
||||
{
|
||||
var pixel = bitmap.GetPixel(x, y);
|
||||
if (pixel.R > 220 && pixel.G is > 40 and < 110 && pixel.B < 40)
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
});
|
||||
|
||||
[Fact]
|
||||
public void NextAndPreviousShowTheSelectedReadyPlateNotTheOpeningPlate() => RunSta(() =>
|
||||
{
|
||||
|
||||
@@ -250,6 +250,11 @@ namespace OpenNest.Controls
|
||||
public void DrawCutOffs(Graphics g)
|
||||
{
|
||||
var plate = view.DisplayPlate;
|
||||
if (view.HasProposalDisplay)
|
||||
{
|
||||
DrawProposedCutOffs(g, plate);
|
||||
return;
|
||||
}
|
||||
if (plate?.CutOffs == null || plate.CutOffs.Count == 0)
|
||||
return;
|
||||
|
||||
@@ -277,6 +282,36 @@ namespace OpenNest.Controls
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A proposal preview carries cut-off parts but no cut-off definitions, so draw each
|
||||
/// cut-off part's own (absolute) cutting moves in the same dashed style.
|
||||
/// </summary>
|
||||
private void DrawProposedCutOffs(Graphics g, Plate plate)
|
||||
{
|
||||
if (plate == null)
|
||||
return;
|
||||
|
||||
using var pen = new Pen(CutOffLineColor, 2.0f) { DashStyle = DashStyle.Dash };
|
||||
|
||||
foreach (var part in plate.Parts)
|
||||
{
|
||||
var program = part.BaseDrawing.IsCutOff ? part.Program : null;
|
||||
if (program == null || program.Mode != Mode.Absolute)
|
||||
continue;
|
||||
|
||||
Vector? position = null;
|
||||
foreach (var code in program.Codes)
|
||||
{
|
||||
if (code is not Motion motion)
|
||||
continue;
|
||||
var end = motion.EndPoint + part.Location;
|
||||
if (motion is LinearMove && position is { } start)
|
||||
DrawLine(g, start, end, pen);
|
||||
position = end;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawActiveWorkArea(Graphics g)
|
||||
{
|
||||
var workArea = view.ActiveWorkArea;
|
||||
|
||||
@@ -728,14 +728,15 @@ namespace OpenNest.Controls
|
||||
renderer.DrawParts(e.Graphics);
|
||||
renderer.DrawClampZones(e.Graphics);
|
||||
renderer.DrawRemovedClamps(e.Graphics);
|
||||
renderer.DrawCutOffs(e.Graphics);
|
||||
if (!HasProposalDisplay)
|
||||
{
|
||||
renderer.DrawCutOffs(e.Graphics);
|
||||
renderer.DrawActiveWorkArea(e.Graphics);
|
||||
renderer.DrawDebugRemnants(e.Graphics);
|
||||
DrawOverlapOverlay(e.Graphics);
|
||||
validationOverlay.Draw(e.Graphics);
|
||||
}
|
||||
// Plan Cutting marks findings on its proposal as well as on the live plate.
|
||||
validationOverlay.Draw(e.Graphics);
|
||||
|
||||
base.OnPaint(e);
|
||||
|
||||
|
||||
@@ -805,6 +805,11 @@ internal sealed class CuttingInspectionPanel : UserControl
|
||||
view.ZoomToFit();
|
||||
}
|
||||
|
||||
/// <summary>A source part number as numbered on the canvas: proposed order on a preview, else unchanged.</summary>
|
||||
private int? ShownPartNumber(int plateIndex, int? sourceNumber) =>
|
||||
preview == null || sourceNumber is not int source ? sourceNumber
|
||||
: CuttingProposalInspection.ProposedPosition(Proposal, plateIndex, source) + 1;
|
||||
|
||||
private void NavigateFinding()
|
||||
{
|
||||
if (Mode == CuttingInspectionMode.Validate)
|
||||
@@ -817,7 +822,12 @@ internal sealed class CuttingInspectionPanel : UserControl
|
||||
return;
|
||||
ShowPlate(row.PlateIndex);
|
||||
// Witnesses are world coordinates, so they mark the proposal and a blocked plate's live view alike.
|
||||
if (row.Witness is { } witness && ShowMarker(witness))
|
||||
// Their part numbers drive which parts stay highlighted: use the shown plate's numbering.
|
||||
if (row.Witness is { } witness && ShowMarker(witness with
|
||||
{
|
||||
PartNumber = ShownPartNumber(row.PlateIndex, row.PartNumber),
|
||||
OtherPartNumber = ShownPartNumber(row.PlateIndex, row.OtherPartNumber),
|
||||
}))
|
||||
{
|
||||
SetDetail(MarkerHint(witness));
|
||||
return;
|
||||
|
||||
@@ -432,7 +432,8 @@ panel is wired in the editor. Non-editor callers still use the shared analyzer.
|
||||
*before* capture, so the cutting plan sequences the new lines like any
|
||||
other cut-off. These are the only live changes made before Apply: Apply keeps them,
|
||||
while unticking the box, Close or switching to Validate removes them and
|
||||
restores the earlier cut-offs and sequence. A blocked cut-off plan adds nothing and
|
||||
restores the earlier cut-offs and sequence. The proposal preview draws planned cut-offs
|
||||
with the same orange-red dashed line as the editor. A blocked cut-off plan adds nothing and
|
||||
plans nothing; the panel names the plate and reason.
|
||||
`Cut-off Settings...` opens the Automatic Scrap Cut-offs dialog in settings mode for
|
||||
the same scope: it previews on the active plate as usual, but `Use These Settings`
|
||||
|
||||
Reference in new issue
Block a user