BGA手动空隙测量:画气泡圆+焊球圆交互、拖拽调整、空隙率计算、VoidLimit编辑、右键删除

This commit is contained in:
李伟
2026-04-27 10:38:56 +08:00
parent e2f1b13e0e
commit e7ae7085df
6 changed files with 350 additions and 6 deletions
@@ -0,0 +1,87 @@
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;
namespace XP.ImageProcessing.RoiControl.Models
{
/// <summary>BGA 空隙测量中的一个圆(气泡或焊球)</summary>
public class BgaCircle
{
public Ellipse Shape { get; set; } // 圆形轮廓
public Ellipse CenterDot { get; set; } // 中心拖拽点
public Ellipse EdgeDot { get; set; } // 边缘拖拽点(调半径)
public Point Center { get; set; }
public double Radius { get; set; }
public bool IsBall { get; set; } // true=焊球, false=气泡
public double Area => Math.PI * Radius * Radius;
public void UpdateVisuals()
{
double d = Radius * 2;
Shape.Width = d; Shape.Height = d;
Canvas.SetLeft(Shape, Center.X - Radius);
Canvas.SetTop(Shape, Center.Y - Radius);
Canvas.SetLeft(CenterDot, Center.X - CenterDot.Width / 2);
Canvas.SetTop(CenterDot, Center.Y - CenterDot.Height / 2);
var edgePt = new Point(Center.X + Radius, Center.Y);
Canvas.SetLeft(EdgeDot, edgePt.X - EdgeDot.Width / 2);
Canvas.SetTop(EdgeDot, edgePt.Y - EdgeDot.Height / 2);
}
}
/// <summary>一次 BGA 空隙测量组(1个焊球 + N个气泡 + 标签)</summary>
public class BgaVoidGroup
{
public BgaCircle Ball { get; set; }
public List<BgaCircle> Voids { get; } = new();
public TextBlock Label { get; set; }
public int Index { get; set; }
public double VoidLimit { get; set; } = 25.0;
public double VoidRate
{
get
{
if (Ball == null || Ball.Area < 1) return 0;
double totalVoid = 0;
foreach (var v in Voids) totalVoid += v.Area;
return Math.Clamp(totalVoid / Ball.Area * 100.0, 0, 100);
}
}
public string Classification => VoidRate <= VoidLimit ? "PASS" : "FAIL";
public void UpdateLabel()
{
if (Label == null || Ball == null) return;
double rate = VoidRate;
string cls = Classification;
Label.Text = (Index > 0 ? $"#{Index} " : "") +
$"Void: {rate:F1}% | Limit: {VoidLimit:F1}% | {cls}";
Label.Foreground = cls == "PASS" ? Brushes.Lime : Brushes.Red;
Canvas.SetLeft(Label, Ball.Center.X + Ball.Radius + 10);
Canvas.SetTop(Label, Ball.Center.Y - 10);
Label.Visibility = Visibility.Visible;
}
/// <summary>获取所有 UI 元素</summary>
public List<UIElement> AllElements
{
get
{
var list = new List<UIElement>();
if (Ball != null) { list.Add(Ball.Shape); list.Add(Ball.CenterDot); list.Add(Ball.EdgeDot); }
foreach (var v in Voids) { list.Add(v.Shape); list.Add(v.CenterDot); list.Add(v.EdgeDot); }
if (Label != null) list.Add(Label);
return list;
}
}
}
}
@@ -6,6 +6,7 @@ namespace XP.ImageProcessing.RoiControl.Models
PointDistance,
PointToLine,
Angle,
FillRate
FillRate,
BgaVoid
}
}