Files
XplorePlane/XP.ImageProcessing.RoiControl/Models/AngleGroup.cs
T

88 lines
3.7 KiB
C#

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;
namespace XP.ImageProcessing.RoiControl.Models
{
/// <summary>一次角度测量的所有视觉元素(顶点V + 射线端点A/B + 两条射线 + 弧线 + 标签)</summary>
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 int Index { 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 = (Index > 0 ? $"#{Index} " : "") + $"{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;
}
}
}