mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-10-10 20:14:13 -04:00
Highlight validation contacts and mute unrelated parts
This commit is contained in:
1 parent
bb108e9341
commit
d59c03f5ab
12 files changed
+223
-30
No files matched your search
@@ -69,7 +69,12 @@ public sealed class ReleasedContourState
|
||||
findings.Add(new(PostVerificationKind.RapidCrossing, plate, part, obstacle.Part,
|
||||
$"Direct XY rapid crosses or touches completed untabbed contour {obstacle.Contour} " +
|
||||
$"of part {obstacle.Part}.")
|
||||
{ Location = (start + move.End) * 0.5, RapidStart = start, RapidEnd = move.End });
|
||||
{
|
||||
Location = (start + move.End) * 0.5,
|
||||
RapidStart = start,
|
||||
RapidEnd = move.End,
|
||||
ContactPoints = PostVerificationGeometry.ContactPoints(start, move.End, obstacle.Curves, token)
|
||||
});
|
||||
}
|
||||
}
|
||||
else if (move.Layer == LayerType.Leadin)
|
||||
|
||||
@@ -89,6 +89,30 @@ internal static class PostVerificationGeometry
|
||||
return inside;
|
||||
}
|
||||
|
||||
internal static IReadOnlyList<Vector> ContactPoints(Vector start, Vector end,
|
||||
IReadOnlyList<Curve> curves, CancellationToken token)
|
||||
{
|
||||
var length = start.DistanceTo(end);
|
||||
if (length <= Epsilon)
|
||||
return Array.Empty<Vector>();
|
||||
var direction = (end - start) * (1 / length);
|
||||
var distances = new List<double>();
|
||||
foreach (var curve in curves)
|
||||
{
|
||||
token.ThrowIfCancellationRequested();
|
||||
curve.ContactAfterStart(start, direction, length, distances);
|
||||
}
|
||||
distances.Sort();
|
||||
var points = new List<Vector>();
|
||||
foreach (var distance in distances)
|
||||
{
|
||||
var point = start + direction * distance;
|
||||
if (points.Count == 0 || points[^1].DistanceTo(point) > Epsilon)
|
||||
points.Add(point);
|
||||
}
|
||||
return points.AsReadOnly();
|
||||
}
|
||||
|
||||
private static double Dot(Vector a, Vector b) => a.X * b.X + a.Y * b.Y;
|
||||
private static double Cross(Vector a, Vector b) => a.X * b.Y - a.Y * b.X;
|
||||
private static double Normalize(double angle)
|
||||
@@ -286,7 +310,8 @@ internal static class PostVerificationGeometry
|
||||
// The largest distance from the centre that ContactAfterStart can count as contact.
|
||||
private double ContactReach => System.Math.Sqrt(Radius * Radius + ContactSlack);
|
||||
|
||||
internal bool ContactAfterStart(Vector origin, Vector direction, double length)
|
||||
internal bool ContactAfterStart(Vector origin, Vector direction, double length,
|
||||
List<double> contacts = null)
|
||||
{
|
||||
if (Center is { } center)
|
||||
{
|
||||
@@ -299,10 +324,13 @@ internal static class PostVerificationGeometry
|
||||
if (square < -ContactSlack)
|
||||
return false;
|
||||
var offset = System.Math.Sqrt(System.Math.Max(0, square));
|
||||
return Hit(projection - offset) || Hit(projection + offset);
|
||||
return contacts == null
|
||||
? Hit(projection - offset) || Hit(projection + offset)
|
||||
: Hit(projection - offset) | Hit(projection + offset);
|
||||
|
||||
bool Hit(double distance) => distance > Epsilon && distance <= length + Epsilon
|
||||
&& OnArc(origin + direction * System.Math.Clamp(distance, 0, length));
|
||||
&& OnArc(origin + direction * System.Math.Clamp(distance, 0, length))
|
||||
&& Record(distance);
|
||||
}
|
||||
var edge = End - Start;
|
||||
var relativeStart = Start - origin;
|
||||
@@ -315,13 +343,20 @@ internal static class PostVerificationGeometry
|
||||
var b = Dot(End - origin, direction);
|
||||
var low = System.Math.Max(0, System.Math.Min(a, b));
|
||||
var high = System.Math.Min(length, System.Math.Max(a, b));
|
||||
return high > Epsilon && low <= high + Epsilon;
|
||||
return high > Epsilon && low <= high + Epsilon && Record(low) && Record(high);
|
||||
}
|
||||
var distanceAlongRapid = Cross(relativeStart, edge) / denominator;
|
||||
var fractionAlongEdge = Cross(relativeStart, direction) / denominator;
|
||||
return distanceAlongRapid > Epsilon && distanceAlongRapid <= length + Epsilon
|
||||
&& fractionAlongEdge >= -Epsilon / System.Math.Max(Length, Epsilon)
|
||||
&& fractionAlongEdge <= 1 + Epsilon / System.Math.Max(Length, Epsilon);
|
||||
&& fractionAlongEdge <= 1 + Epsilon / System.Math.Max(Length, Epsilon)
|
||||
&& Record(distanceAlongRapid);
|
||||
|
||||
bool Record(double distance)
|
||||
{
|
||||
contacts?.Add(System.Math.Clamp(distance, 0, length));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
internal bool CrossesRay(Vector point)
|
||||
|
||||
@@ -21,6 +21,8 @@ public sealed record PostVerificationFinding(PostVerificationKind Kind, int Plat
|
||||
public Vector? Location { get; init; }
|
||||
public Vector? RapidStart { get; init; }
|
||||
public Vector? RapidEnd { get; init; }
|
||||
/// <summary>Rapid boundary contacts in travel order; collinear contact spans use their endpoints.</summary>
|
||||
public IReadOnlyList<Vector> ContactPoints { get; init; } = Array.Empty<Vector>();
|
||||
public PlateOverlapPair Overlap { get; init; }
|
||||
}
|
||||
|
||||
|
||||
@@ -113,6 +113,7 @@ public class PostVerificationAnalyzerTests
|
||||
f.PartNumber == 3 && f.OtherPartNumber == 1));
|
||||
Assert.NotNull(crossing.RapidStart);
|
||||
Assert.NotNull(crossing.RapidEnd);
|
||||
Assert.Equal(new[] { new Vector(0, 2), new Vector(4, 2) }, crossing.ContactPoints);
|
||||
Assert.Equal((crossing.RapidStart.Value + crossing.RapidEnd.Value) * 0.5, crossing.Location);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
using OpenNest.Diagnostics;
|
||||
using OpenNest.Geometry;
|
||||
|
||||
namespace OpenNest.Tests.Diagnostics;
|
||||
|
||||
public class RapidContactPointTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(-2, 2, 6, 2, 2)]
|
||||
[InlineData(-2, -2, 6, 6, 2)] // Shared corners are deduplicated.
|
||||
[InlineData(-2, 4, 6, 4, 2)] // Collinear contact span.
|
||||
[InlineData(-2, 2, 0, 2, 1)] // Endpoint arrival.
|
||||
[InlineData(0, 0, -2, -2, 0)] // Start-only departure.
|
||||
[InlineData(1, 1, 2, 2, 0)] // Entirely inside.
|
||||
public void RectangleContacts(double x1, double y1, double x2, double y2, int count)
|
||||
{
|
||||
var vertices = new[] { new Vector(0, 0), new Vector(4, 0), new Vector(4, 4), new Vector(0, 4) };
|
||||
var curves = Enumerable.Range(0, 4).Select(i =>
|
||||
PostVerificationGeometry.Curve.Create(vertices[i], vertices[(i + 1) % 4], null, false)).ToArray();
|
||||
var start = new Vector(x1, y1);
|
||||
var end = new Vector(x2, y2);
|
||||
var points = PostVerificationGeometry.ContactPoints(start, end, curves, default);
|
||||
Assert.Equal(count, points.Count);
|
||||
Assert.True(((ICollection<Vector>)points).IsReadOnly);
|
||||
Assert.All(points, point =>
|
||||
{
|
||||
Assert.Contains(curves, curve => curve.Contains(point));
|
||||
Assert.True(new Line(start, end).ClosestPointTo(point).DistanceTo(point) < 1e-8);
|
||||
});
|
||||
Assert.Equal(points.OrderBy(point => point.DistanceTo(start)), points);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0, 2)]
|
||||
[InlineData(1, 1)] // Tangency has a single marker.
|
||||
public void CircleContactsUseNativeCurve(double y, int count)
|
||||
{
|
||||
var circle = PostVerificationGeometry.Curve.Create(new Vector(1, 0), new Vector(1, 0), Vector.Zero, false);
|
||||
var points = PostVerificationGeometry.ContactPoints(new Vector(-2, y), new Vector(2, y), [circle], default);
|
||||
Assert.Equal(count, points.Count);
|
||||
Assert.All(points, point => Assert.Equal(1, point.DistanceTo(Vector.Zero), 8));
|
||||
Assert.Equal(new Vector(count == 1 ? 0 : -1, y), points[0]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ArcContactsExcludeUnusedCircleHalf()
|
||||
{
|
||||
var arc = PostVerificationGeometry.Curve.Create(new Vector(0, -1), new Vector(0, 1), Vector.Zero, false);
|
||||
var points = PostVerificationGeometry.ContactPoints(new Vector(-2, 0), new Vector(2, 0), [arc], default);
|
||||
Assert.Equal(new Vector(1, 0), Assert.Single(points));
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,34 @@ namespace OpenNest.WinForms.Tests.Forms;
|
||||
|
||||
public class ValidateNestFormTests
|
||||
{
|
||||
[Fact]
|
||||
public void RapidContactCrosshairIsDrawnAtContactAndClearedWithFinding()
|
||||
{
|
||||
RunSta(() =>
|
||||
{
|
||||
using var view = new PlateView();
|
||||
view.ShowValidationFinding(new PostVerificationFinding(PostVerificationKind.RapidCrossing,
|
||||
1, 2, 1, "crossing")
|
||||
{
|
||||
RapidStart = new Vector(-1, 0),
|
||||
RapidEnd = new Vector(1, 0),
|
||||
ContactPoints = Array.AsReadOnly(new[] { Vector.Zero })
|
||||
});
|
||||
using var bitmap = new Bitmap(120, 120);
|
||||
using var graphics = Graphics.FromImage(bitmap);
|
||||
graphics.TranslateTransform(60, 60);
|
||||
graphics.Clear(Color.Black);
|
||||
view.ValidationOverlay.Draw(graphics);
|
||||
// Off the horizontal rapid, on the contact's vertical crosshair.
|
||||
var pixel = bitmap.GetPixel(60, 65);
|
||||
Assert.True(pixel.R > 150 && pixel.B > 50 && pixel.G < 100);
|
||||
view.ClearValidationFinding();
|
||||
graphics.Clear(Color.Black);
|
||||
view.ValidationOverlay.Draw(graphics);
|
||||
Assert.Equal(Color.Black.ToArgb(), bitmap.GetPixel(60, 65).ToArgb());
|
||||
});
|
||||
}
|
||||
|
||||
#pragma warning disable xUnit1031 // Completed tasks on a dedicated STA thread.
|
||||
[Fact]
|
||||
public void GridNavigatesMainViewAndRestoresViewportOnDisposal()
|
||||
|
||||
@@ -1,11 +1,53 @@
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using OpenNest.Controls;
|
||||
using OpenNest.Diagnostics;
|
||||
using OpenNest.Geometry;
|
||||
|
||||
namespace OpenNest.WinForms.Tests;
|
||||
|
||||
public class LayoutPartColorTests
|
||||
{
|
||||
[Fact]
|
||||
public void ValidationMutesOnlyUnrelatedInstancesAndRestoresColors()
|
||||
=> StaTestThread.Run(() =>
|
||||
{
|
||||
var program = new CNC.Program();
|
||||
program.MoveTo(0, 0);
|
||||
program.LineTo(40, 0);
|
||||
program.LineTo(40, 40);
|
||||
program.LineTo(0, 40);
|
||||
program.LineTo(0, 0);
|
||||
var drawing = new Drawing("shared drawing", program) { Color = Color.Coral };
|
||||
using var view = new PlateView { Width = 240, Height = 100, DrawBounds = false };
|
||||
view.Matrix = new Matrix();
|
||||
for (var i = 0; i < 3; i++)
|
||||
view.Plate.Parts.Add(new Part(drawing) { Location = new Vector(i * 60, 0) });
|
||||
using var image = new Bitmap(240, 100);
|
||||
using var graphics = Graphics.FromImage(image);
|
||||
view.ShowValidationFinding(new PostVerificationFinding(PostVerificationKind.Overlap, 1, 1, 2, "overlap"));
|
||||
view.LayoutParts[2].IsSelected = true;
|
||||
view.Renderer.DrawParts(graphics);
|
||||
Assert.Equal(Color.Coral.ToArgb(), image.GetPixel(10, 10).ToArgb());
|
||||
Assert.Equal(Color.Coral.ToArgb(), image.GetPixel(70, 10).ToArgb());
|
||||
Assert.Equal(Color.Gainsboro.ToArgb(), image.GetPixel(130, 10).ToArgb());
|
||||
Assert.True(view.LayoutParts[2].IsSelected);
|
||||
view.LayoutParts[2].IsSelected = false;
|
||||
|
||||
view.ShowValidationFinding(new PostVerificationFinding(PostVerificationKind.MissingLeadIn, 1, 3, null, "lead"));
|
||||
view.Renderer.DrawParts(graphics);
|
||||
Assert.Equal(Color.Gainsboro.ToArgb(), image.GetPixel(10, 10).ToArgb());
|
||||
Assert.Equal(Color.Gainsboro.ToArgb(), image.GetPixel(70, 10).ToArgb());
|
||||
Assert.Equal(Color.Coral.ToArgb(), image.GetPixel(130, 10).ToArgb());
|
||||
|
||||
view.ClearValidationFinding();
|
||||
view.Renderer.DrawParts(graphics);
|
||||
for (var i = 0; i < 3; i++)
|
||||
Assert.Equal(Color.Coral.ToArgb(), image.GetPixel(i * 60 + 10, 10).ToArgb());
|
||||
Assert.Equal(Color.Coral, drawing.Color);
|
||||
Assert.All(view.LayoutParts, part => Assert.Equal(Color.Coral, part.Color));
|
||||
}, TimeSpan.FromMinutes(1), "Validation color test timed out.");
|
||||
|
||||
[Theory]
|
||||
[InlineData(false)]
|
||||
[InlineData(true)]
|
||||
|
||||
@@ -121,10 +121,12 @@ namespace OpenNest.Controls
|
||||
if (!part.DisplayBounds.IntersectsWith(viewBounds))
|
||||
continue;
|
||||
|
||||
part.Draw(g, (i + 1).ToString());
|
||||
DrawBendLines(g, part.BasePart);
|
||||
DrawEtchMarks(g, part.BasePart);
|
||||
DrawGrainWarning(g, part.BasePart);
|
||||
var finding = view.ValidationOverlay.Finding;
|
||||
var muted = finding != null && finding.PartNumber != i + 1 && finding.OtherPartNumber != i + 1;
|
||||
part.Draw(g, (i + 1).ToString(), muted);
|
||||
DrawBendLines(g, part.BasePart, muted);
|
||||
DrawEtchMarks(g, part.BasePart, muted);
|
||||
DrawGrainWarning(g, part.BasePart, muted);
|
||||
}
|
||||
|
||||
var previewParts = view.PreviewParts;
|
||||
@@ -264,7 +266,7 @@ namespace OpenNest.Controls
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawBendLines(Graphics g, Part part)
|
||||
private void DrawBendLines(Graphics g, Part part, bool muted)
|
||||
{
|
||||
if (
|
||||
!view.ShowBendLines
|
||||
@@ -273,7 +275,7 @@ namespace OpenNest.Controls
|
||||
)
|
||||
return;
|
||||
|
||||
using var bendPen = new Pen(Color.Yellow, 1.5f)
|
||||
using var bendPen = new Pen(muted ? Color.Gray : Color.Yellow, 1.5f)
|
||||
{
|
||||
DashStyle = System.Drawing.Drawing2D.DashStyle.Dash,
|
||||
};
|
||||
@@ -299,7 +301,7 @@ namespace OpenNest.Controls
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawEtchMarks(Graphics g, Part part)
|
||||
private void DrawEtchMarks(Graphics g, Part part, bool muted)
|
||||
{
|
||||
if (
|
||||
!view.ShowBendLines
|
||||
@@ -308,7 +310,7 @@ namespace OpenNest.Controls
|
||||
)
|
||||
return;
|
||||
|
||||
using var etchPen = new Pen(Color.Green, 1.5f);
|
||||
using var etchPen = new Pen(muted ? Color.Gray : Color.Green, 1.5f);
|
||||
var etchLength = 1.0;
|
||||
|
||||
foreach (var bend in part.BaseDrawing.Bends)
|
||||
@@ -353,7 +355,7 @@ namespace OpenNest.Controls
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawGrainWarning(Graphics g, Part part)
|
||||
private void DrawGrainWarning(Graphics g, Part part, bool muted)
|
||||
{
|
||||
var plate = view.Plate;
|
||||
if (
|
||||
@@ -389,7 +391,7 @@ namespace OpenNest.Controls
|
||||
var pt2 = view.PointWorldToGraph(
|
||||
new Vector(location.X + box.Length, location.Y + box.Width)
|
||||
);
|
||||
using var warnPen = new Pen(Color.FromArgb(180, 255, 140, 0), 2f);
|
||||
using var warnPen = new Pen(muted ? Color.Gray : Color.FromArgb(180, 255, 140, 0), 2f);
|
||||
g.DrawRectangle(
|
||||
warnPen,
|
||||
pt1.X,
|
||||
|
||||
@@ -88,6 +88,18 @@ internal sealed class ValidationOverlay : IDisposable
|
||||
{ DashStyle = DashStyle.Dash };
|
||||
graphics.DrawLine(rapid, view.PointWorldToGraph(start), view.PointWorldToGraph(end));
|
||||
}
|
||||
using var contactHalo = new Pen(Color.White, 5 * dpi);
|
||||
using var contactPen = new Pen(Color.DeepPink, 2 * dpi);
|
||||
foreach (var contact in finding.ContactPoints.Where(IsFinite))
|
||||
{
|
||||
var point = view.PointWorldToGraph(contact);
|
||||
foreach (var pen in new[] { contactHalo, contactPen })
|
||||
{
|
||||
graphics.DrawEllipse(pen, point.X - 8 * dpi, point.Y - 8 * dpi, 16 * dpi, 16 * dpi);
|
||||
graphics.DrawLine(pen, point.X - 12 * dpi, point.Y, point.X + 12 * dpi, point.Y);
|
||||
graphics.DrawLine(pen, point.X, point.Y - 12 * dpi, point.X, point.Y + 12 * dpi);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (finding.Location is not { } location || !double.IsFinite(location.X) || !double.IsFinite(location.Y))
|
||||
|
||||
@@ -89,7 +89,7 @@ public sealed class ValidateNestForm : Form
|
||||
{
|
||||
AutoSize = true,
|
||||
Dock = DockStyle.Fill,
|
||||
Text = "Select an issue to locate it in the main plate view. Its highlight pulses while selected.\r\n"
|
||||
Text = "Select an issue to locate it in the main plate view. Its highlight pulses; unrelated parts appear gray.\r\n"
|
||||
+ "Minimum spacing, plate boundaries, and final post-processor output are not checked.",
|
||||
Margin = new Padding(3, 3, 3, 10),
|
||||
};
|
||||
@@ -205,7 +205,9 @@ public sealed class ValidateNestForm : Form
|
||||
var hint = finding.Kind switch
|
||||
{
|
||||
PostVerificationKind.Overlap => "Shaded material is the overlap. The ring marks its area centroid, which can lie between separate patches.",
|
||||
PostVerificationKind.RapidCrossing => "The pulsing dashed orange line is the offending rapid segment; it does not mark an exact contact point.",
|
||||
PostVerificationKind.RapidCrossing => finding.ContactPoints.Count > 0
|
||||
? "Crosshairs mark contacts with the completed contour. For travel along an edge, they mark the contact span's endpoints. The pulsing dashed orange line shows the rapid."
|
||||
: "The pulsing dashed orange line shows the rapid inside the completed contour; there is no boundary contact point to mark.",
|
||||
PostVerificationKind.MissingLeadIn => "The ring marks the cutting contour start where a lead-in is missing.",
|
||||
_ => "The ring marks the affected part's center when available; no exact problem location is known.",
|
||||
};
|
||||
|
||||
+14
-10
@@ -92,23 +92,27 @@ namespace OpenNest
|
||||
SetDisplayColor(fill);
|
||||
}
|
||||
|
||||
public void Draw(Graphics g)
|
||||
public void Draw(Graphics g) => Draw(g, false);
|
||||
|
||||
private void Draw(Graphics g, bool muted)
|
||||
{
|
||||
RefreshColors();
|
||||
using var schemeSelectionBrush = IsSelected && !colorScheme.SelectedPartColor.IsEmpty
|
||||
? new SolidBrush(colorScheme.SelectedPartColor) : null;
|
||||
g.FillPath(schemeSelectionBrush ?? (IsSelected ? selectedBrush : brush), Path);
|
||||
DrawEtch(g);
|
||||
g.FillPath(muted ? Brushes.Gainsboro : schemeSelectionBrush ?? (IsSelected ? selectedBrush : brush), Path);
|
||||
DrawEtch(g, muted);
|
||||
// Keep real cuts visible even where an etch overlaps them.
|
||||
g.DrawPath(IsSelected && colorScheme.PartOutlineColor.IsEmpty ? selectedPen : pen, Path);
|
||||
g.DrawPath(muted ? Pens.Gray : IsSelected && colorScheme.PartOutlineColor.IsEmpty ? selectedPen : pen, Path);
|
||||
|
||||
if (LeadInPath != null)
|
||||
g.DrawPath(leadInPen, LeadInPath);
|
||||
g.DrawPath(muted ? Pens.Gray : leadInPen, LeadInPath);
|
||||
}
|
||||
|
||||
public void Draw(Graphics g, string id)
|
||||
public void Draw(Graphics g, string id) => Draw(g, id, false);
|
||||
|
||||
internal void Draw(Graphics g, string id, bool muted)
|
||||
{
|
||||
Draw(g);
|
||||
Draw(g, muted);
|
||||
|
||||
using var sf = new StringFormat
|
||||
{
|
||||
@@ -118,7 +122,7 @@ namespace OpenNest
|
||||
g.DrawString(
|
||||
id,
|
||||
programIdFont,
|
||||
Brushes.Black,
|
||||
muted ? Brushes.DimGray : Brushes.Black,
|
||||
_labelScreenPoint.X,
|
||||
_labelScreenPoint.Y,
|
||||
sf
|
||||
@@ -127,11 +131,11 @@ namespace OpenNest
|
||||
|
||||
public GraphicsPath OffsetPath { get; private set; }
|
||||
|
||||
internal void DrawEtch(Graphics g)
|
||||
internal void DrawEtch(Graphics g, bool muted = false)
|
||||
{
|
||||
if (EtchPath == null || EtchPath.PointCount == 0)
|
||||
return;
|
||||
using var etchPen = new Pen(colorScheme.EtchColor, 1.5f);
|
||||
using var etchPen = new Pen(muted ? Color.Gray : colorScheme.EtchColor, 1.5f);
|
||||
g.DrawPath(etchPen, EtchPath);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,13 +10,21 @@ numbers in a read-only grid like the cut planner; incomplete or failed checks ar
|
||||
never reported as clear. Select a row to switch the main plate view to that plate
|
||||
and focus the finding. The inspector opens at the right of the screen and can be
|
||||
moved or resized. The details below the grid explain the selected warning.
|
||||
While a finding is selected, its named parts retain their normal colors; other
|
||||
parts use muted gray fills and visible gray outlines, lead-ins, and marks. Selecting
|
||||
another finding updates the emphasis. Clearing the finding or closing validation
|
||||
restores normal rendering without changing drawing colors or selection.
|
||||
|
||||
Overlaps use the same magenta material shading as **View > Overlap Check**. A
|
||||
DPI-scaled ring expands and fades continuously while the finding is selected;
|
||||
both its color and contrasting halo fade as it grows. For overlaps the ring marks the area centroid (possibly between
|
||||
separate patches); for missing lead-ins it marks the contour start. Rapid warnings
|
||||
pulse the offending orange dashed segment continuously, without a ring or an
|
||||
exact contact-point marker. Incomplete findings use the part center when available;
|
||||
pulse the offending orange dashed segment continuously and mark its contacts with
|
||||
the completed contour using fixed-screen-size magenta crosshairs with white halos.
|
||||
Contacts use native lines/arcs and the rapid check's existing numerical tolerances;
|
||||
shared vertices and tangencies get one marker, and travel along an edge marks the
|
||||
contact span endpoints. Start-only departure is excluded. Travel entirely inside
|
||||
a contour has no boundary contact markers. Incomplete findings use the part center when available;
|
||||
findings without a usable location do not invent a marker. Closing the inspector
|
||||
clears the highlight and restores the original plate, zoom, and overlap display mode.
|
||||
|
||||
|
||||
Reference in new issue
Block a user