using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Drawing.Imaging; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using Inspectron.Hawkeye.Vision.Geometry; using OpenCvSharp; using OpenCvSharp.Extensions; using Point = System.Drawing.Point; namespace Lindt.Candybox.Demo { public partial class MaskControl : UserControl { private Bitmap _backBuffer; private GrahamConvexHull _hull; private List _vectorLines; private EditMode _currentMode = EditMode.Brush; private bool _isDrawingLine = false; private Point _lineStartPoint; private VectorLine _selectedLine; private bool _isDraggingLine = false; private bool _isDraggingVertex = false; private bool _isDraggingStartVertex = false; private Point _dragOffset; public MaskControl() { InitializeComponent(); _backBuffer = new Bitmap(1800, 1408); _hull = new GrahamConvexHull(); _vectorLines = new List(); } [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] public EditMode CurrentMode { get => _currentMode; set { _currentMode = value; // Clear any ongoing operations when mode changes _isDrawingLine = false; _isDraggingLine = false; _isDraggingVertex = false; _selectedLine = null; Invalidate(); } } [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] public List VectorLines { get => _vectorLines; set => _vectorLines = value; } [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] public Bitmap Mask { get; set; } = new Bitmap(1800, 1408); [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] public Bitmap Original { get; set; } = new Bitmap(1800, 1408); protected override void OnPaintBackground(PaintEventArgs e) { //base.OnPaintBackground(e); } private bool _isDown = false; private Point _lastMouse; protected override void OnMouseDown(MouseEventArgs e) { _isDown = true; _lastMouse = ConvertCoordinates(new Point(e.X, e.Y)); switch (_currentMode) { case EditMode.DrawLine: if (!_isDrawingLine) { _isDrawingLine = true; _lineStartPoint = _lastMouse; } else { // Finish drawing line var newLine = new VectorLine(_lineStartPoint, _lastMouse); _vectorLines.Add(newLine); _isDrawingLine = false; Invalidate(); } break; case EditMode.RemoveLine: var lineToRemove = _vectorLines.FirstOrDefault(line => line.IsPointOnLine(_lastMouse)); if (lineToRemove != null) { _vectorLines.Remove(lineToRemove); Invalidate(); } break; case EditMode.MoveLine: _selectedLine = _vectorLines.FirstOrDefault(line => line.IsPointOnLine(_lastMouse)); if (_selectedLine != null) { _isDraggingLine = true; _dragOffset = new Point(_lastMouse.X - _selectedLine.StartPoint.X, _lastMouse.Y - _selectedLine.StartPoint.Y); } break; case EditMode.MoveVertices: foreach (var line in _vectorLines) { if (line.IsPointNearVertex(_lastMouse, out bool isStartVertex)) { _selectedLine = line; _isDraggingVertex = true; _isDraggingStartVertex = isStartVertex; break; } } break; } } protected override void OnMouseUp(MouseEventArgs e) { _isDown = false; _isDraggingLine = false; _isDraggingVertex = false; } private Pen _pen = new Pen(Color.White, 30); private Pen _penBlack = new Pen(Color.Black, 30); protected override void OnMouseMove(MouseEventArgs e) { var currentPoint = ConvertCoordinates(new Point(e.X, e.Y)); // Handle brush mode (existing functionality) if (_currentMode == EditMode.Brush) { if (_isDown && e.Button == MouseButtons.Left) { using (Graphics g = Graphics.FromImage(Mask)) { g.DrawLine(_pen, currentPoint.X, currentPoint.Y, _lastMouse.X, _lastMouse.Y); g.FillEllipse(Brushes.White, _lastMouse.X - 15, _lastMouse.Y - 15, 30, 30); } } if (_isDown && e.Button == MouseButtons.Right) { using (Graphics g = Graphics.FromImage(Mask)) { g.DrawLine(_penBlack, currentPoint.X, currentPoint.Y, _lastMouse.X, _lastMouse.Y); g.FillEllipse(Brushes.Black, _lastMouse.X - 15, _lastMouse.Y - 15, 30, 30); } } } // Handle line dragging else if (_currentMode == EditMode.MoveLine && _isDraggingLine && _selectedLine != null) { var deltaX = currentPoint.X - _lastMouse.X; var deltaY = currentPoint.Y - _lastMouse.Y; _selectedLine.StartPoint = new Point(_selectedLine.StartPoint.X + deltaX, _selectedLine.StartPoint.Y + deltaY); _selectedLine.EndPoint = new Point(_selectedLine.EndPoint.X + deltaX, _selectedLine.EndPoint.Y + deltaY); } // Handle vertex dragging else if (_currentMode == EditMode.MoveVertices && _isDraggingVertex && _selectedLine != null) { if (_isDraggingStartVertex) { _selectedLine.StartPoint = currentPoint; } else { _selectedLine.EndPoint = currentPoint; } } _lastMouse = currentPoint; Invalidate(); } private Pen _contourPen = new Pen(Color.GreenYellow, 3); private Pen _vectorLinePen = new Pen(Color.DeepPink, 12); private Brush _vertexBrush = new SolidBrush(Color.Orange); protected override void OnPaint(PaintEventArgs e) { OnPaintInternal(e); } void OnPaintInternal(PaintEventArgs e) { var mat = Mask.ToMat().CvtColor(ColorConversionCodes.BGR2GRAY); var contours = mat.FindContoursAsArray(RetrievalModes.External, ContourApproximationModes.ApproxNone); LastContours = contours; using (Graphics g = Graphics.FromImage(_backBuffer)) { g.Clear(Color.Transparent); g.DrawImage(Original, 0, 0); // Draw contours for (int i = 0; i < contours.Length; i++) { if (contours[i].Length > 1) { var points = contours[i].Select(x => new System.Drawing.Point(x.X, x.Y)).ToArray(); g.DrawPolygon(_contourPen, points); } } // Draw vector lines foreach (var line in _vectorLines) { // Convert coordinates back to display coordinates var startDisplay = new Point(line.StartPoint.X, line.StartPoint.Y); var endDisplay = new Point(line.EndPoint.X, line.EndPoint.Y); g.DrawLine(_vectorLinePen, startDisplay, endDisplay); // Draw vertices when in move vertices mode if (_currentMode == EditMode.MoveVertices) { g.FillEllipse(_vertexBrush, startDisplay.X - 5, startDisplay.Y - 5, 10, 10); g.FillEllipse(_vertexBrush, endDisplay.X - 5, endDisplay.Y - 5, 10, 10); } } // Draw temporary line when drawing if (_currentMode == EditMode.DrawLine && _isDrawingLine) { var startDisplay = new Point(_lineStartPoint.X, _lineStartPoint.Y); var endDisplay = new Point(_lastMouse.X, _lastMouse.Y); using (var tempPen = new Pen(Color.DeepPink, 12) { DashStyle = System.Drawing.Drawing2D.DashStyle.Dash }) { g.DrawLine(tempPen, startDisplay, endDisplay); } } // Draw mouse cursor indicator for brush mode if (_currentMode == EditMode.Brush) { var cursorDisplay = new Point(_lastMouse.X, _lastMouse.Y); g.DrawEllipse(Pens.Gray, cursorDisplay.X - 15, cursorDisplay.Y - 15, 30, 30); } } e.Graphics.DrawImage(_backBuffer, 0, 0, Width, Height); } [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] public OpenCvSharp.Point[][] LastContours { get; set; } Point ConvertCoordinates(Point coords) { return new Point(coords.X * 2, coords.Y * 2); } protected override void OnResize(EventArgs e) { base.OnResize(e); } } public enum EditMode { Brush, DrawLine, RemoveLine, MoveLine, MoveVertices } public class VectorLine { public Point StartPoint { get; set; } public Point EndPoint { get; set; } public bool Selected { get; set; } public VectorLine() { } public VectorLine Scale(float sX, float sY) { return new VectorLine(new Point((int)(StartPoint.X * sX), (int)(StartPoint.Y * sY)), new Point((int)(EndPoint.X * sX), (int)(EndPoint.Y * sY))); } public VectorLine Translate(int tX, int tY) { return new VectorLine(new Point(StartPoint.X + tX, StartPoint.Y + tY), new Point(EndPoint.X + tX, EndPoint.Y + tY)); } public VectorLine Translate(Point point) { return Translate(point.X, point.Y); } public VectorLine Sub(int tX, int tY) { return new VectorLine(new Point(StartPoint.X - tX, StartPoint.Y - tY), new Point(EndPoint.X - tX, EndPoint.Y - tY)); } public VectorLine Sub(Point point) { return Sub(point.X, point.Y); } public VectorLine(Point start, Point end) { StartPoint = start; EndPoint = end; Selected = false; } public bool IsPointOnLine(Point point, int tolerance = 10) { // Calculate distance from point to line segment double A = point.X - StartPoint.X; double B = point.Y - StartPoint.Y; double C = EndPoint.X - StartPoint.X; double D = EndPoint.Y - StartPoint.Y; double dot = A * C + B * D; double lenSq = C * C + D * D; if (lenSq == 0) return Math.Sqrt(A * A + B * B) <= tolerance; double param = dot / lenSq; double xx, yy; if (param < 0) { xx = StartPoint.X; yy = StartPoint.Y; } else if (param > 1) { xx = EndPoint.X; yy = EndPoint.Y; } else { xx = StartPoint.X + param * C; yy = StartPoint.Y + param * D; } double dx = point.X - xx; double dy = point.Y - yy; return Math.Sqrt(dx * dx + dy * dy) <= tolerance; } public bool IsPointNearVertex(Point point, out bool isStartVertex, int tolerance = 15) { isStartVertex = false; double distToStart = Math.Sqrt(Math.Pow(point.X - StartPoint.X, 2) + Math.Pow(point.Y - StartPoint.Y, 2)); double distToEnd = Math.Sqrt(Math.Pow(point.X - EndPoint.X, 2) + Math.Pow(point.Y - EndPoint.Y, 2)); if (distToStart <= tolerance) { isStartVertex = true; return true; } if (distToEnd <= tolerance) { isStartVertex = false; return true; } return false; } } }