using System; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Shapes; namespace XP.ImageProcessing.RoiControl.Models { /// 一次角度测量的所有视觉元素(顶点V + 射线端点A/B + 两条射线 + 弧线 + 标签) public class AngleGroup { public Ellipse DotV { get; set; } // 顶点 public Ellipse DotA { get; set; } // 射线端点A public Ellipse DotB { get; set; } // 射线端点B public Line LineA { get; set; } // 射线VA public Line LineB { get; set; } // 射线VB public Path Arc { get; set; } // 角度弧线 public TextBlock Label { get; set; } public Point V { get; set; } public Point A { get; set; } public Point B { get; set; } public double AngleDeg { get { double vax = A.X - V.X, vay = A.Y - V.Y; double vbx = B.X - V.X, vby = B.Y - V.Y; double lenA = Math.Sqrt(vax * vax + vay * vay); double lenB = Math.Sqrt(vbx * vbx + vby * vby); if (lenA < 0.001 || lenB < 0.001) return 0; double dot = vax * vbx + vay * vby; double cos = Math.Clamp(dot / (lenA * lenB), -1.0, 1.0); return Math.Acos(cos) * 180.0 / Math.PI; } } public void UpdateVisuals() { // 射线 LineA.X1 = V.X; LineA.Y1 = V.Y; LineA.X2 = A.X; LineA.Y2 = A.Y; LineA.Visibility = Visibility.Visible; LineB.X1 = V.X; LineB.Y1 = V.Y; LineB.X2 = B.X; LineB.Y2 = B.Y; LineB.Visibility = Visibility.Visible; // 弧线 double vax = A.X - V.X, vay = A.Y - V.Y; double vbx = B.X - V.X, vby = B.Y - V.Y; double lenA = Math.Sqrt(vax * vax + vay * vay); double lenB = Math.Sqrt(vbx * vbx + vby * vby); if (lenA < 1 || lenB < 1) { Arc.Visibility = Visibility.Collapsed; return; } double arcRadius = Math.Min(30, Math.Min(lenA, lenB) * 0.3); if (arcRadius < 8) arcRadius = 8; double angleARad = Math.Atan2(vay, vax); double angleBRad = Math.Atan2(vby, vbx); double cross = vax * vby - vay * vbx; double angleDeg = AngleDeg; bool isLargeArc = angleDeg > 180; var sweepDir = cross >= 0 ? SweepDirection.Clockwise : SweepDirection.Counterclockwise; var startPt = new Point(V.X + arcRadius * Math.Cos(angleARad), V.Y + arcRadius * Math.Sin(angleARad)); var endPt = new Point(V.X + arcRadius * Math.Cos(angleBRad), V.Y + arcRadius * Math.Sin(angleBRad)); var arcSeg = new ArcSegment(endPt, new Size(arcRadius, arcRadius), 0, isLargeArc, sweepDir, true); var fig = new PathFigure(startPt, new[] { arcSeg }, false); Arc.Data = new PathGeometry(new[] { fig }); Arc.Visibility = Visibility.Visible; // 标签位置 double midAngle = (angleARad + angleBRad) / 2.0; double testX = Math.Cos(midAngle), testY = Math.Sin(midAngle); double testCross = vax * testY - vay * testX; if ((cross >= 0 && testCross < 0) || (cross < 0 && testCross >= 0)) midAngle += Math.PI; double labelDist = arcRadius + 16; Label.Text = $"{angleDeg:F1}°"; Canvas.SetLeft(Label, V.X + labelDist * Math.Cos(midAngle) - 15); Canvas.SetTop(Label, V.Y + labelDist * Math.Sin(midAngle) - 8); Label.Visibility = Visibility.Visible; } } }