气泡测量:工具面板UI、BubbleMeasure模式、矩形ROI绘制/拖动/右下角调整大小、工具切换联动
This commit is contained in:
@@ -370,6 +370,28 @@ namespace XP.ImageProcessing.RoiControl.Controls
|
||||
private Point? _bgaPendingCenter; // 等待第二次点击定半径
|
||||
private Ellipse _bgaPendingDot;
|
||||
|
||||
// 气泡测量状态
|
||||
public enum BubbleSubTool { Roi, Wand, Brush, Eraser }
|
||||
private BubbleSubTool _bubbleTool = BubbleSubTool.Roi;
|
||||
private Rectangle _bubbleRoiRect;
|
||||
private Ellipse _bubbleRoiHandle; // 右下角调整手柄
|
||||
private Rect? _bubbleRoi;
|
||||
private Point? _bubbleRoiStart;
|
||||
private bool _bubbleRoiDragging;
|
||||
private bool _bubbleRoiMoving; // 拖动整个 ROI
|
||||
private bool _bubbleRoiResizing; // 右下角调整大小
|
||||
private Point _bubbleRoiDragOffset;
|
||||
private Image _bubbleMaskImage;
|
||||
private System.Windows.Media.Imaging.WriteableBitmap _bubbleMask;
|
||||
private int _bubbleThreshold = 128;
|
||||
private int _bubbleBrushSize = 5;
|
||||
private bool _bubbleBrushDragging;
|
||||
|
||||
public void SetBubbleTool(BubbleSubTool tool) => _bubbleTool = tool;
|
||||
public void SetBubbleThreshold(int val) => _bubbleThreshold = val;
|
||||
public void SetBubbleBrushSize(int val) => _bubbleBrushSize = val;
|
||||
public Rect? BubbleRoi => _bubbleRoi;
|
||||
|
||||
// 拖拽状态
|
||||
private Ellipse _mDraggingDot;
|
||||
private object _mDraggingOwner;
|
||||
@@ -448,6 +470,7 @@ namespace XP.ImageProcessing.RoiControl.Controls
|
||||
HandleFillRateClick(pos);
|
||||
else if (CurrentMeasureMode == Models.MeasureMode.BgaVoid)
|
||||
HandleBgaVoidClick(pos);
|
||||
// BubbleMeasure 的点击在 MouseDown/Move/Up 中处理(拖拽画 ROI 和画笔)
|
||||
}
|
||||
|
||||
// ── 点点距 ──
|
||||
@@ -1084,6 +1107,125 @@ namespace XP.ImageProcessing.RoiControl.Controls
|
||||
}
|
||||
}
|
||||
|
||||
// ── 气泡测量辅助 ──
|
||||
|
||||
private void EnsureBubbleRoiVisuals()
|
||||
{
|
||||
EnsureMeasureOverlay();
|
||||
if (_bubbleRoiRect == null)
|
||||
{
|
||||
_bubbleRoiRect = new Rectangle
|
||||
{
|
||||
Stroke = Brushes.Red,
|
||||
StrokeThickness = 1.5,
|
||||
Fill = Brushes.Transparent,
|
||||
Visibility = Visibility.Collapsed,
|
||||
IsHitTestVisible = false
|
||||
};
|
||||
_measureOverlay.Children.Add(_bubbleRoiRect);
|
||||
}
|
||||
if (_bubbleRoiHandle == null)
|
||||
{
|
||||
_bubbleRoiHandle = new Ellipse
|
||||
{
|
||||
Width = 10, Height = 10,
|
||||
Fill = Brushes.Red, Stroke = Brushes.White, StrokeThickness = 1,
|
||||
Cursor = Cursors.SizeNWSE,
|
||||
Visibility = Visibility.Collapsed,
|
||||
IsHitTestVisible = false // 命中测试由 MouseDown 中的距离判断处理
|
||||
};
|
||||
_measureOverlay.Children.Add(_bubbleRoiHandle);
|
||||
}
|
||||
}
|
||||
|
||||
private void SyncBubbleRoiVisuals()
|
||||
{
|
||||
if (_bubbleRoiRect == null || !_bubbleRoi.HasValue) return;
|
||||
var r = _bubbleRoi.Value;
|
||||
Canvas.SetLeft(_bubbleRoiRect, r.X);
|
||||
Canvas.SetTop(_bubbleRoiRect, r.Y);
|
||||
_bubbleRoiRect.Width = r.Width;
|
||||
_bubbleRoiRect.Height = r.Height;
|
||||
_bubbleRoiRect.Visibility = Visibility.Visible;
|
||||
|
||||
if (_bubbleRoiHandle != null)
|
||||
{
|
||||
Canvas.SetLeft(_bubbleRoiHandle, r.Right - _bubbleRoiHandle.Width / 2);
|
||||
Canvas.SetTop(_bubbleRoiHandle, r.Bottom - _bubbleRoiHandle.Height / 2);
|
||||
_bubbleRoiHandle.Visibility = Visibility.Visible;
|
||||
}
|
||||
}
|
||||
|
||||
private void InitBubbleMask()
|
||||
{
|
||||
if (!_bubbleRoi.HasValue) return;
|
||||
int w = (int)CanvasWidth, h = (int)CanvasHeight;
|
||||
if (w <= 0 || h <= 0) return;
|
||||
|
||||
_bubbleMask = new System.Windows.Media.Imaging.WriteableBitmap(w, h, 96, 96,
|
||||
PixelFormats.Bgra32, null);
|
||||
|
||||
if (_bubbleMaskImage == null)
|
||||
{
|
||||
EnsureMeasureOverlay();
|
||||
_bubbleMaskImage = new Image
|
||||
{
|
||||
IsHitTestVisible = false,
|
||||
Opacity = 0.45,
|
||||
Stretch = Stretch.Fill
|
||||
};
|
||||
_bubbleMaskImage.SetBinding(Image.WidthProperty, new System.Windows.Data.Binding("CanvasWidth") { Source = this });
|
||||
_bubbleMaskImage.SetBinding(Image.HeightProperty, new System.Windows.Data.Binding("CanvasHeight") { Source = this });
|
||||
_measureOverlay.Children.Add(_bubbleMaskImage);
|
||||
}
|
||||
_bubbleMaskImage.Source = _bubbleMask;
|
||||
}
|
||||
|
||||
private void ApplyBrushAt(Point pos)
|
||||
{
|
||||
// 画笔/橡皮:暂时空实现,下一步实现
|
||||
}
|
||||
|
||||
private void UpdateBubbleResult()
|
||||
{
|
||||
// 计算空隙率:暂时空实现,下一步实现
|
||||
}
|
||||
|
||||
/// <summary>清除气泡测量所有状态</summary>
|
||||
public void ClearBubbleMeasure()
|
||||
{
|
||||
if (_measureOverlay != null)
|
||||
{
|
||||
if (_bubbleRoiRect != null) { _measureOverlay.Children.Remove(_bubbleRoiRect); _bubbleRoiRect = null; }
|
||||
if (_bubbleRoiHandle != null) { _measureOverlay.Children.Remove(_bubbleRoiHandle); _bubbleRoiHandle = null; }
|
||||
if (_bubbleMaskImage != null) { _measureOverlay.Children.Remove(_bubbleMaskImage); _bubbleMaskImage = null; }
|
||||
}
|
||||
_bubbleRoi = null;
|
||||
_bubbleMask = null;
|
||||
_bubbleRoiStart = null;
|
||||
_bubbleRoiDragging = false;
|
||||
_bubbleRoiMoving = false;
|
||||
_bubbleRoiResizing = false;
|
||||
_bubbleBrushDragging = false;
|
||||
_bubbleTool = BubbleSubTool.Roi;
|
||||
}
|
||||
|
||||
// 气泡工具切换事件
|
||||
public static readonly RoutedEvent BubbleToolChangedEvent =
|
||||
EventManager.RegisterRoutedEvent(nameof(BubbleToolChanged), RoutingStrategy.Bubble,
|
||||
typeof(RoutedEventHandler), typeof(PolygonRoiCanvas));
|
||||
|
||||
public event RoutedEventHandler BubbleToolChanged
|
||||
{
|
||||
add { AddHandler(BubbleToolChangedEvent, value); }
|
||||
remove { RemoveHandler(BubbleToolChangedEvent, value); }
|
||||
}
|
||||
|
||||
private void RaiseBubbleToolChanged()
|
||||
{
|
||||
RaiseEvent(new RoutedEventArgs(BubbleToolChangedEvent));
|
||||
}
|
||||
|
||||
// ── 重编号 ──
|
||||
|
||||
private void RenumberAll()
|
||||
@@ -1354,6 +1496,57 @@ namespace XP.ImageProcessing.RoiControl.Controls
|
||||
|
||||
private void Canvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
// 气泡测量模式:ROI 拖拽 / 画笔涂抹
|
||||
if (CurrentMeasureMode == Models.MeasureMode.BubbleMeasure)
|
||||
{
|
||||
var pos = e.GetPosition(mainCanvas);
|
||||
if (_bubbleTool == BubbleSubTool.Roi)
|
||||
{
|
||||
// 检查是否点击了右下角手柄
|
||||
if (_bubbleRoiHandle != null && _bubbleRoi.HasValue)
|
||||
{
|
||||
var hx = Canvas.GetLeft(_bubbleRoiHandle) + _bubbleRoiHandle.Width / 2;
|
||||
var hy = Canvas.GetTop(_bubbleRoiHandle) + _bubbleRoiHandle.Height / 2;
|
||||
if (Math.Abs(pos.X - hx) < 10 && Math.Abs(pos.Y - hy) < 10)
|
||||
{
|
||||
_bubbleRoiResizing = true;
|
||||
mainCanvas.CaptureMouse();
|
||||
e.Handled = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// 检查是否点击了 ROI 内部(拖动)
|
||||
if (_bubbleRoi.HasValue && _bubbleRoi.Value.Contains(pos))
|
||||
{
|
||||
_bubbleRoiMoving = true;
|
||||
_bubbleRoiDragOffset = new Point(pos.X - _bubbleRoi.Value.X, pos.Y - _bubbleRoi.Value.Y);
|
||||
mainCanvas.CaptureMouse();
|
||||
e.Handled = true;
|
||||
return;
|
||||
}
|
||||
// 没有 ROI 时才画新的,有 ROI 时点击外部不拦截(让图像正常拖动)
|
||||
if (!_bubbleRoi.HasValue)
|
||||
{
|
||||
_bubbleRoiStart = pos;
|
||||
_bubbleRoiDragging = true;
|
||||
EnsureBubbleRoiVisuals();
|
||||
mainCanvas.CaptureMouse();
|
||||
e.Handled = true;
|
||||
return;
|
||||
}
|
||||
// 有 ROI 但点击了外部 → 不拦截,走正常拖动逻辑
|
||||
}
|
||||
if ((_bubbleTool == BubbleSubTool.Brush || _bubbleTool == BubbleSubTool.Eraser) && _bubbleRoi.HasValue)
|
||||
{
|
||||
_bubbleBrushDragging = true;
|
||||
ApplyBrushAt(pos);
|
||||
mainCanvas.CaptureMouse();
|
||||
e.Handled = true;
|
||||
return;
|
||||
}
|
||||
// 魔棒在 MouseUp(CanvasClicked)中处理
|
||||
}
|
||||
|
||||
lastMousePosition = e.GetPosition(imageDisplayGrid);
|
||||
isDragging = false;
|
||||
mainCanvas.CaptureMouse();
|
||||
@@ -1361,6 +1554,54 @@ namespace XP.ImageProcessing.RoiControl.Controls
|
||||
|
||||
private void Canvas_MouseMove(object sender, MouseEventArgs e)
|
||||
{
|
||||
// 气泡测量:ROI 拖拽画新矩形
|
||||
if (_bubbleRoiDragging && _bubbleRoiStart.HasValue && _bubbleRoiRect != null)
|
||||
{
|
||||
var pos = e.GetPosition(mainCanvas);
|
||||
double x = Math.Min(_bubbleRoiStart.Value.X, pos.X);
|
||||
double y = Math.Min(_bubbleRoiStart.Value.Y, pos.Y);
|
||||
double w = Math.Abs(pos.X - _bubbleRoiStart.Value.X);
|
||||
double h = Math.Abs(pos.Y - _bubbleRoiStart.Value.Y);
|
||||
Canvas.SetLeft(_bubbleRoiRect, x);
|
||||
Canvas.SetTop(_bubbleRoiRect, y);
|
||||
_bubbleRoiRect.Width = w;
|
||||
_bubbleRoiRect.Height = h;
|
||||
_bubbleRoiRect.Visibility = Visibility.Visible;
|
||||
if (_bubbleRoiHandle != null) _bubbleRoiHandle.Visibility = Visibility.Collapsed;
|
||||
e.Handled = true;
|
||||
return;
|
||||
}
|
||||
// 气泡测量:拖动 ROI
|
||||
if (_bubbleRoiMoving && _bubbleRoi.HasValue)
|
||||
{
|
||||
var pos = e.GetPosition(mainCanvas);
|
||||
double nx = pos.X - _bubbleRoiDragOffset.X;
|
||||
double ny = pos.Y - _bubbleRoiDragOffset.Y;
|
||||
_bubbleRoi = new Rect(nx, ny, _bubbleRoi.Value.Width, _bubbleRoi.Value.Height);
|
||||
SyncBubbleRoiVisuals();
|
||||
e.Handled = true;
|
||||
return;
|
||||
}
|
||||
// 气泡测量:右下角调整大小
|
||||
if (_bubbleRoiResizing && _bubbleRoi.HasValue)
|
||||
{
|
||||
var pos = e.GetPosition(mainCanvas);
|
||||
double w = Math.Max(20, pos.X - _bubbleRoi.Value.X);
|
||||
double h = Math.Max(20, pos.Y - _bubbleRoi.Value.Y);
|
||||
_bubbleRoi = new Rect(_bubbleRoi.Value.X, _bubbleRoi.Value.Y, w, h);
|
||||
SyncBubbleRoiVisuals();
|
||||
e.Handled = true;
|
||||
return;
|
||||
}
|
||||
// 气泡测量:画笔/橡皮拖拽
|
||||
if (_bubbleBrushDragging && _bubbleRoi.HasValue)
|
||||
{
|
||||
var pos = e.GetPosition(mainCanvas);
|
||||
ApplyBrushAt(pos);
|
||||
e.Handled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.LeftButton == MouseButtonState.Pressed && mainCanvas.IsMouseCaptured)
|
||||
{
|
||||
Point currentPosition = e.GetPosition(imageDisplayGrid);
|
||||
@@ -1378,6 +1619,49 @@ namespace XP.ImageProcessing.RoiControl.Controls
|
||||
|
||||
private void Canvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
// 气泡测量:ROI 拖拽完成
|
||||
if (_bubbleRoiDragging && _bubbleRoiStart.HasValue)
|
||||
{
|
||||
var pos = e.GetPosition(mainCanvas);
|
||||
double x = Math.Min(_bubbleRoiStart.Value.X, pos.X);
|
||||
double y = Math.Min(_bubbleRoiStart.Value.Y, pos.Y);
|
||||
double w = Math.Abs(pos.X - _bubbleRoiStart.Value.X);
|
||||
double h = Math.Abs(pos.Y - _bubbleRoiStart.Value.Y);
|
||||
_bubbleRoiDragging = false;
|
||||
_bubbleRoiStart = null;
|
||||
mainCanvas.ReleaseMouseCapture();
|
||||
|
||||
if (w > 5 && h > 5)
|
||||
{
|
||||
_bubbleRoi = new Rect(x, y, w, h);
|
||||
SyncBubbleRoiVisuals();
|
||||
InitBubbleMask();
|
||||
RaiseMeasureStatusChanged($"ROI 已设置: {w:F0}×{h:F0},可拖动/调整大小,或在面板切换魔棒工具");
|
||||
}
|
||||
e.Handled = true;
|
||||
return;
|
||||
}
|
||||
// 气泡测量:拖动/调整完成
|
||||
if (_bubbleRoiMoving || _bubbleRoiResizing)
|
||||
{
|
||||
_bubbleRoiMoving = false;
|
||||
_bubbleRoiResizing = false;
|
||||
mainCanvas.ReleaseMouseCapture();
|
||||
if (_bubbleRoi.HasValue)
|
||||
RaiseMeasureStatusChanged($"ROI: {_bubbleRoi.Value.Width:F0}×{_bubbleRoi.Value.Height:F0}");
|
||||
e.Handled = true;
|
||||
return;
|
||||
}
|
||||
// 气泡测量:画笔/橡皮松开
|
||||
if (_bubbleBrushDragging)
|
||||
{
|
||||
_bubbleBrushDragging = false;
|
||||
mainCanvas.ReleaseMouseCapture();
|
||||
UpdateBubbleResult();
|
||||
e.Handled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
mainCanvas.ReleaseMouseCapture();
|
||||
|
||||
if (!isDragging)
|
||||
|
||||
@@ -7,6 +7,7 @@ namespace XP.ImageProcessing.RoiControl.Models
|
||||
PointToLine,
|
||||
Angle,
|
||||
FillRate,
|
||||
BgaVoid
|
||||
BgaVoid,
|
||||
BubbleMeasure
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user