From 60a557bd379a35949fe7ba3991677c1e01bce44b Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Wed, 18 Mar 2026 17:31:59 -0400 Subject: [PATCH] feat(ui): add DensityBar sparkline control for density visualization Co-Authored-By: Claude Sonnet 4.6 --- OpenNest/Controls/DensityBar.cs | 70 +++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 OpenNest/Controls/DensityBar.cs diff --git a/OpenNest/Controls/DensityBar.cs b/OpenNest/Controls/DensityBar.cs new file mode 100644 index 0000000..e9a6c65 --- /dev/null +++ b/OpenNest/Controls/DensityBar.cs @@ -0,0 +1,70 @@ +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Windows.Forms; + +namespace OpenNest.Controls +{ + public class DensityBar : Control + { + private static readonly Color TrackColor = Color.FromArgb(224, 224, 224); + private static readonly Color LowColor = Color.FromArgb(245, 166, 35); + private static readonly Color HighColor = Color.FromArgb(76, 175, 80); + + private double value; + + public DensityBar() + { + DoubleBuffered = true; + SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true); + Size = new Size(60, 8); + } + + public double Value + { + get => value; + set + { + this.value = System.Math.Clamp(value, 0.0, 1.0); + Invalidate(); + } + } + + protected override void OnPaint(PaintEventArgs e) + { + base.OnPaint(e); + var g = e.Graphics; + g.SmoothingMode = SmoothingMode.AntiAlias; + + var rect = new Rectangle(0, 0, Width - 1, Height - 1); + + // Track background + using var trackPath = CreateRoundedRect(rect, 4); + using var trackBrush = new SolidBrush(TrackColor); + g.FillPath(trackBrush, trackPath); + + // Fill + var fillWidth = (int)(rect.Width * value); + if (fillWidth > 0) + { + var fillRect = new Rectangle(rect.X, rect.Y, fillWidth, rect.Height); + using var fillPath = CreateRoundedRect(fillRect, 4); + using var gradientBrush = new LinearGradientBrush( + new Point(rect.X, 0), new Point(rect.Right, 0), + LowColor, HighColor); + g.FillPath(gradientBrush, fillPath); + } + } + + private static GraphicsPath CreateRoundedRect(Rectangle rect, int radius) + { + var path = new GraphicsPath(); + var d = radius * 2; + path.AddArc(rect.X, rect.Y, d, d, 180, 90); + path.AddArc(rect.Right - d, rect.Y, d, d, 270, 90); + path.AddArc(rect.Right - d, rect.Bottom - d, d, d, 0, 90); + path.AddArc(rect.X, rect.Bottom - d, d, d, 90, 90); + path.CloseFigure(); + return path; + } + } +}