From 9d1a39aa8fb1837c608914517684f09c370d2150 Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Thu, 30 Apr 2026 12:08:38 -0400 Subject: [PATCH] feat(ui): ghost rendering for title block entities and text in EntityView Co-Authored-By: Claude Opus 4.6 --- OpenNest/Controls/EntityView.cs | 47 +++++++++++++++++++++++++++++- OpenNest/Forms/CadConverterForm.cs | 1 + 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/OpenNest/Controls/EntityView.cs b/OpenNest/Controls/EntityView.cs index 53f62d4..c0bbdd4 100644 --- a/OpenNest/Controls/EntityView.cs +++ b/OpenNest/Controls/EntityView.cs @@ -30,13 +30,16 @@ namespace OpenNest.Controls public List OriginalEntities { get; set; } public bool ShowEntityLabels { get; set; } public List Texts { get; set; } = new List(); + public HashSet TitleBlockEntityIds { get; set; } private readonly Pen gridPen = new Pen(Color.FromArgb(70, 70, 70)); private readonly Dictionary penCache = new Dictionary(); + private readonly Dictionary ghostPenCache = new Dictionary(); private readonly Font labelFont = new Font("Segoe UI", 7f); private readonly SolidBrush labelBrush = new SolidBrush(Color.FromArgb(220, 255, 255, 200)); private readonly SolidBrush labelBackBrush = new SolidBrush(Color.FromArgb(33, 40, 48)); private readonly SolidBrush textBrush = new SolidBrush(Color.FromArgb(180, 200, 200, 200)); + private readonly SolidBrush ghostTextBrush = new SolidBrush(Color.FromArgb(50, 200, 200, 200)); public event EventHandler LinePicked; public event EventHandler PickCancelled; @@ -102,6 +105,13 @@ namespace OpenNest.Controls foreach (var entity in Entities) { if (IsEtchLayer(entity.Layer)) continue; + + if (TitleBlockEntityIds != null && TitleBlockEntityIds.Contains(entity.Id)) + { + DrawGhostEntity(e.Graphics, entity); + continue; + } + var isHighlighted = simplifierHighlightSet != null && simplifierHighlightSet.Contains(entity); var pen = isHighlighted ? GetEntityPen(Color.FromArgb(60, entity.Color)) @@ -243,11 +253,26 @@ namespace OpenNest.Controls return pen; } + private Pen GetGhostPen(Color color) + { + var ghostColor = Color.FromArgb(60, color.R, color.G, color.B); + var argb = ghostColor.ToArgb(); + if (!ghostPenCache.TryGetValue(argb, out var pen)) + { + pen = new Pen(ghostColor); + ghostPenCache[argb] = pen; + } + return pen; + } + public void ClearPenCache() { foreach (var pen in penCache.Values) pen.Dispose(); penCache.Clear(); + foreach (var pen in ghostPenCache.Values) + pen.Dispose(); + ghostPenCache.Clear(); } private static bool IsEtchLayer(Layer layer) => @@ -413,10 +438,28 @@ namespace OpenNest.Controls labelBrush.Dispose(); labelBackBrush.Dispose(); textBrush.Dispose(); + ghostTextBrush.Dispose(); } base.Dispose(disposing); } + private void DrawGhostEntity(Graphics g, Entity e) + { + var pen = GetGhostPen(e.Color); + switch (e.Type) + { + case EntityType.Arc: + DrawArc(g, (Arc)e, pen); + break; + case EntityType.Circle: + DrawCircle(g, (Circle)e, pen); + break; + case EntityType.Line: + DrawLine(g, (Line)e, pen); + break; + } + } + private void DrawEntity(Graphics g, Entity e, Pen pen) { if (!e.Layer.IsVisible || !e.IsVisible) @@ -501,8 +544,10 @@ namespace OpenNest.Controls sf.Alignment = text.HAlign; sf.LineAlignment = text.VAlign; + var brush = TitleBlockEntityIds != null && TitleBlockEntityIds.Count > 0 + ? ghostTextBrush : textBrush; using var font = new Font("Segoe UI", fontSize, GraphicsUnit.Pixel); - g.DrawString(text.Value, font, textBrush, 0, 0, sf); + g.DrawString(text.Value, font, brush, 0, 0, sf); g.Restore(state); } } diff --git a/OpenNest/Forms/CadConverterForm.cs b/OpenNest/Forms/CadConverterForm.cs index 6aad054..bc1eb24 100644 --- a/OpenNest/Forms/CadConverterForm.cs +++ b/OpenNest/Forms/CadConverterForm.cs @@ -162,6 +162,7 @@ namespace OpenNest.Forms entityView1.Entities.AddRange(item.Entities); entityView1.Bends = item.Bends ?? new List(); entityView1.Texts = item.Texts ?? new List(); + entityView1.TitleBlockEntityIds = item.TitleBlockEntityIds; item.Entities.ForEach(e => e.IsVisible = true); if (item.Entities.Any(e => e.Layer != null))