mirror of
https://gitee.com/wang-yin1/wpf-visual-process-framework
synced 2026-03-03 00:00:56 +08:00
677 lines
20 KiB
C#
677 lines
20 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using System.Windows;
|
|
using System.Windows.Controls;
|
|
using System.Windows.Data;
|
|
using System.Windows.Documents;
|
|
using System.Windows.Input;
|
|
using System.Windows.Media;
|
|
using System.Windows.Media.Imaging;
|
|
using System.Windows.Navigation;
|
|
using System.Windows.Shapes;
|
|
|
|
namespace VisionFrame.Nodes
|
|
{
|
|
/// <summary>
|
|
/// LineNode.xaml 的交互逻辑
|
|
/// </summary>
|
|
public partial class LineNode : UserControl, INotifyPropertyChanged
|
|
{
|
|
public double X1
|
|
{
|
|
get { return (double)GetValue(X1Property); }
|
|
set { SetValue(X1Property, value); }
|
|
}
|
|
public static readonly DependencyProperty X1Property =
|
|
DependencyProperty.Register("X1", typeof(double),
|
|
typeof(LineNode),
|
|
new PropertyMetadata(0.0, OnPropertyChanged));
|
|
|
|
public double Y1
|
|
{
|
|
get { return (double)GetValue(Y1Property); }
|
|
set { SetValue(Y1Property, value); }
|
|
}
|
|
public static readonly DependencyProperty Y1Property =
|
|
DependencyProperty.Register("Y1", typeof(double),
|
|
typeof(LineNode),
|
|
new PropertyMetadata(0.0, OnPropertyChanged));
|
|
|
|
|
|
|
|
public double X2
|
|
{
|
|
get { return (double)GetValue(X2Property); }
|
|
set { SetValue(X2Property, value); }
|
|
}
|
|
public static readonly DependencyProperty X2Property =
|
|
DependencyProperty.Register("X2", typeof(double),
|
|
typeof(LineNode),
|
|
new PropertyMetadata(0.0, OnPropertyChanged));
|
|
|
|
public double Y2
|
|
{
|
|
get { return (double)GetValue(Y2Property); }
|
|
set { SetValue(Y2Property, value); }
|
|
}
|
|
public static readonly DependencyProperty Y2Property =
|
|
DependencyProperty.Register("Y2", typeof(double),
|
|
typeof(LineNode),
|
|
new PropertyMetadata(0.0, OnPropertyChanged));
|
|
|
|
|
|
private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
|
{
|
|
(d as LineNode).InvalidateVisual();
|
|
}
|
|
|
|
public string StartAnchor
|
|
{
|
|
get { return (string)GetValue(StartAnchorProperty); }
|
|
set { SetValue(StartAnchorProperty, value); }
|
|
}
|
|
public static readonly DependencyProperty StartAnchorProperty =
|
|
DependencyProperty.Register("StartAnchor", typeof(string),
|
|
typeof(LineNode),
|
|
new PropertyMetadata(""));
|
|
|
|
public string EndAnchor
|
|
{
|
|
get { return (string)GetValue(EndAnchorProperty); }
|
|
set { SetValue(EndAnchorProperty, value); }
|
|
}
|
|
public static readonly DependencyProperty EndAnchorProperty =
|
|
DependencyProperty.Register("EndAnchor", typeof(string),
|
|
typeof(LineNode),
|
|
new PropertyMetadata(""));
|
|
|
|
public event PropertyChangedEventHandler? PropertyChanged;
|
|
|
|
public Thickness CancelLocation { get; set; }
|
|
|
|
|
|
|
|
public LineNode()
|
|
{
|
|
InitializeComponent();
|
|
}
|
|
|
|
protected override void OnRender(DrawingContext drawingContext)
|
|
{
|
|
List<Point> points = new List<Point>();
|
|
|
|
// 起始与终止锚点坐标
|
|
Point start = new Point(X1, Y1);
|
|
Point end = new Point(X2, Y2);
|
|
//
|
|
if (EndAnchor == "N")
|
|
{
|
|
if (X2 < X1)
|
|
end += new Vector(2, 0);
|
|
else if (X2 > X1)
|
|
end += new Vector(-2, 0);
|
|
|
|
if (Y2 > Y1)
|
|
end += new Vector(0, -2);
|
|
else if (Y2 < Y1)
|
|
end += new Vector(0, 2);
|
|
}
|
|
|
|
// 起始延伸坐标
|
|
Point start_extend = new Point(X1, Y1);
|
|
if (StartAnchor == "T")
|
|
start_extend = new Point(X1, Y1 - 20);
|
|
else if (StartAnchor == "B")
|
|
start_extend = new Point(X1, Y1 + 20);
|
|
else if (StartAnchor == "L")
|
|
start_extend = new Point(X1 - 20, Y1);
|
|
else if (StartAnchor == "R")
|
|
start_extend = new Point(X1 + 20, Y1);
|
|
|
|
// 终点延伸坐标
|
|
Point end_extend = new Point(end.X, end.Y);
|
|
if (EndAnchor == "T")
|
|
end_extend = new Point(end.X, end.Y - 20);
|
|
else if (EndAnchor == "B")
|
|
end_extend = new Point(end.X, end.Y + 20);
|
|
else if (EndAnchor == "L")
|
|
end_extend = new Point(end.X - 20, end.Y);
|
|
else if (EndAnchor == "R")
|
|
end_extend = new Point(end.X + 20, end.Y);
|
|
|
|
// 添加第一点
|
|
points.Add(start);
|
|
|
|
// 添加延伸点(针对第一点),起点是哪个锚点
|
|
points.Add(start_extend);
|
|
|
|
|
|
// 添加动态点
|
|
// 锚点名称 TBLR 反射
|
|
MethodInfo mi = this.GetType().GetMethod(StartAnchor + "2" + EndAnchor,
|
|
BindingFlags.NonPublic | BindingFlags.Instance);
|
|
if (mi == null) return;
|
|
Point[] ps = (Point[])mi.Invoke(this, new object[] {
|
|
start_extend,
|
|
end_extend
|
|
});
|
|
points.AddRange(ps);
|
|
//points.AddRange(this.B2T(new Point(X1, Y1 + 20), new Point(X2, Y2 - 20)));
|
|
|
|
|
|
// 添加一个延伸点(针对最终点),终点是哪个锚点
|
|
points.Add(end_extend);
|
|
|
|
// 添加最终点
|
|
points.Add(end);
|
|
|
|
|
|
//StreamGeometry geometry = new StreamGeometry();
|
|
//using (StreamGeometryContext ctx = geometry.Open())
|
|
//{
|
|
// Point first = points.FirstOrDefault();
|
|
// ctx.BeginFigure(first, false, false);
|
|
// ctx.PolyLineTo(points, true, true);
|
|
//}
|
|
|
|
var geometry = GetBrokenGeometry(points.ToArray(), false, false);
|
|
|
|
if (EndAnchor != "N")
|
|
{
|
|
//points[points.Count - 2];// 终延
|
|
//points[points.Count - 1];// 终
|
|
Point[] arrow_points = this.GetArrowPoints(points[points.Count - 2], points[points.Count - 1]);
|
|
var arrow_geo = this.GetBrokenGeometry(arrow_points, true, true);
|
|
PathGeometry path = PathGeometry.CreateFromGeometry(geometry);
|
|
// 整合
|
|
path.AddGeometry(arrow_geo);
|
|
path.Freeze();
|
|
|
|
geometry = path;
|
|
}
|
|
|
|
|
|
//drawingContext.DrawLine(new Pen(Brushes.Red, 2), new Point(X1, Y1), new Point(X2, Y2));
|
|
drawingContext.DrawGeometry(
|
|
Brushes.Orange,
|
|
new Pen(Brushes.Orange, 2),
|
|
geometry);
|
|
|
|
|
|
if (ps.Length == 2)
|
|
{
|
|
var p = ps[0] + (ps[1] - ps[0]) / 2;
|
|
CancelLocation = new Thickness(p.X - 7, p.Y - 7, 0, 0);
|
|
}
|
|
else if (ps.Length == 1)
|
|
CancelLocation = new Thickness(ps[0].X - 7, ps[0].Y - 7, 0, 0);
|
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(CancelLocation)));
|
|
}
|
|
|
|
private Geometry GetBrokenGeometry(Point[] points, bool isFill, bool isClose)
|
|
{
|
|
StreamGeometry geometry = new StreamGeometry();
|
|
using (StreamGeometryContext ctx = geometry.Open())
|
|
{
|
|
Point first = points.FirstOrDefault();
|
|
ctx.BeginFigure(first, isFill, isClose);
|
|
ctx.PolyLineTo(points, true, true);
|
|
}
|
|
return geometry;
|
|
}
|
|
private Point[] GetArrowPoints(Point start, Point end)
|
|
{
|
|
Vector vec = start - end;
|
|
// 规范化 单位化
|
|
vec.Normalize();
|
|
vec *= 8;
|
|
|
|
Matrix matrix = new Matrix();
|
|
matrix.Rotate(20);
|
|
Point p1 = end + vec * matrix;
|
|
matrix.Rotate(-40);
|
|
Point p2 = end + vec * matrix;
|
|
|
|
return new Point[] { p1, end, p2 };
|
|
}
|
|
|
|
#region 下锚点
|
|
// 下对上
|
|
private Point[] B2T(Point start, Point end)
|
|
{
|
|
double cx = (end.X - start.X) / 2;
|
|
double cy = (end.Y - start.Y) / 2;
|
|
|
|
List<Point> ps = new List<Point>();
|
|
if (end.Y > start.Y)
|
|
{
|
|
ps.Add(new Point(start.X, start.Y + cy));
|
|
ps.Add(new Point(end.X, start.Y + cy));
|
|
}
|
|
else if (end.Y < start.Y)
|
|
{
|
|
ps.Add(new Point(start.X + cx, start.Y));
|
|
ps.Add(new Point(start.X + cx, end.Y));
|
|
}
|
|
|
|
return ps.ToArray();
|
|
}
|
|
|
|
// 下对左
|
|
private Point[] B2L(Point start, Point end)
|
|
{
|
|
double cx = (end.X - start.X) / 2;
|
|
double cy = (end.Y - start.Y) / 2;
|
|
|
|
List<Point> ps = new List<Point>();
|
|
if (start.X > end.X)
|
|
{
|
|
ps.Add(new Point(end.X, start.Y));
|
|
}
|
|
else if (start.Y < end.Y)
|
|
{
|
|
ps.Add(new Point(start.X, end.Y));
|
|
}
|
|
else
|
|
{
|
|
ps.Add(new Point(start.X + cx, start.Y));
|
|
ps.Add(new Point(end.X - cx, end.Y));
|
|
}
|
|
return ps.ToArray();
|
|
}
|
|
|
|
// 下对右
|
|
private Point[] B2R(Point start, Point end)
|
|
{
|
|
double cx = (end.X - start.X) / 2;
|
|
double cy = (end.Y - start.Y) / 2;
|
|
|
|
List<Point> ps = new List<Point>();
|
|
|
|
if (start.X < end.X)
|
|
{
|
|
ps.Add(new Point(end.X, start.Y));
|
|
}
|
|
else if (start.Y < end.Y)
|
|
{
|
|
ps.Add(new Point(start.X, end.Y));
|
|
}
|
|
else
|
|
{
|
|
ps.Add(new Point(start.X + cx, start.Y));
|
|
ps.Add(new Point(end.X - cx, end.Y));
|
|
}
|
|
return ps.ToArray();
|
|
}
|
|
|
|
// 下对下
|
|
private Point[] B2B(Point start, Point end)
|
|
{
|
|
double cy = end.Y - start.Y;
|
|
|
|
List<Point> ps = new List<Point>();
|
|
if (start.Y > end.Y)
|
|
{
|
|
ps.Add(new Point(end.X, start.Y));
|
|
}
|
|
else if (start.Y < end.Y)
|
|
{
|
|
ps.Add(new Point(start.X, end.Y));
|
|
}
|
|
|
|
return ps.ToArray();
|
|
}
|
|
|
|
// 下对空
|
|
private Point[] B2N(Point start, Point end)
|
|
{
|
|
List<Point> ps = new List<Point>();
|
|
if (start.Y > end.Y)
|
|
{
|
|
ps.Add(new Point(end.X, start.Y));
|
|
}
|
|
else
|
|
{
|
|
ps.Add(new Point(start.X, end.Y));
|
|
}
|
|
|
|
return ps.ToArray();
|
|
}
|
|
#endregion
|
|
|
|
#region 右锚点
|
|
// From右锚点To上锚点
|
|
private Point[] R2T(Point start, Point end)
|
|
{
|
|
double cx = (end.X - start.X) / 2;
|
|
|
|
List<Point> ps = new List<Point>();
|
|
|
|
if (start.X > end.X)
|
|
{
|
|
ps.Add(new Point(start.X, end.Y));
|
|
}
|
|
else if (start.Y < end.Y)
|
|
{
|
|
ps.Add(new Point(end.X, start.Y));
|
|
}
|
|
else
|
|
{
|
|
ps.Add(new Point(start.X + cx, start.Y));
|
|
ps.Add(new Point(end.X - cx, end.Y));
|
|
}
|
|
|
|
return ps.ToArray();
|
|
}
|
|
|
|
// From右锚点To左锚点
|
|
private Point[] R2L(Point start, Point end)
|
|
{
|
|
double cx = (end.X - start.X) / 2;
|
|
double cy = (end.Y - start.Y) / 2;
|
|
|
|
List<Point> ps = new List<Point>();
|
|
if (start.X > end.X)
|
|
{
|
|
ps.Add(new Point(start.X, start.Y + cy));
|
|
ps.Add(new Point(end.X, end.Y - cy));
|
|
}
|
|
else
|
|
{
|
|
ps.Add(new Point(start.X + cx, start.Y));
|
|
ps.Add(new Point(end.X - cx, end.Y));
|
|
}
|
|
|
|
return ps.ToArray();
|
|
}
|
|
|
|
// From右锚点To右锚点
|
|
private Point[] R2R(Point start, Point end)
|
|
{
|
|
List<Point> ps = new List<Point>();
|
|
if (start.X > end.X)
|
|
{
|
|
ps.Add(new Point(start.X, end.Y));
|
|
}
|
|
else
|
|
{
|
|
ps.Add(new Point(end.X, start.Y));
|
|
}
|
|
|
|
return ps.ToArray();
|
|
}
|
|
|
|
// From右锚点To下锚点
|
|
private Point[] R2B(Point start, Point end)
|
|
{
|
|
double cx = (end.X - start.X) / 2;
|
|
double cy = (end.Y - start.Y) / 2;
|
|
|
|
List<Point> ps = new List<Point>();
|
|
|
|
if (start.X > end.X && start.Y > end.Y)
|
|
{
|
|
ps.Add(new Point(start.X, start.Y + cy));
|
|
ps.Add(new Point(end.X, start.Y + cy));
|
|
}
|
|
else if (start.X < end.X && start.Y < end.Y)
|
|
{
|
|
ps.Add(new Point(start.X + cx, start.Y));
|
|
ps.Add(new Point(end.X - cx, end.Y));
|
|
}
|
|
else if (start.X < end.X)
|
|
{
|
|
ps.Add(new Point(end.X, start.Y));
|
|
}
|
|
else
|
|
{
|
|
ps.Add(new Point(start.X, end.Y));
|
|
}
|
|
|
|
return ps.ToArray();
|
|
}
|
|
|
|
// Fromd右锚点To NULL
|
|
private Point[] R2N(Point start, Point end)
|
|
{
|
|
List<Point> ps = new List<Point>();
|
|
if (start.X > end.X)
|
|
{
|
|
ps.Add(new Point(start.X, end.Y));
|
|
}
|
|
else
|
|
{
|
|
ps.Add(new Point(end.X, start.Y));
|
|
}
|
|
|
|
return ps.ToArray();
|
|
}
|
|
#endregion
|
|
|
|
#region 左锚点
|
|
// From左锚点To上锚点
|
|
private Point[] L2T(Point start, Point end)
|
|
{
|
|
double cx = (end.X - start.X) / 2;
|
|
double cy = (end.Y - start.Y) / 2;
|
|
|
|
List<Point> ps = new List<Point>();
|
|
if (start.X > end.X && start.Y > end.Y)
|
|
{
|
|
ps.Add(new Point(start.X + cx, start.Y));
|
|
ps.Add(new Point(end.X - cx, end.Y));
|
|
}
|
|
else if (start.X < end.X && start.Y < end.Y)
|
|
{
|
|
ps.Add(new Point(start.X, start.Y + cy));
|
|
ps.Add(new Point(end.X, end.Y - cy));
|
|
}
|
|
else if (start.X < end.X)
|
|
{
|
|
ps.Add(new Point(start.X, end.Y));
|
|
}
|
|
else
|
|
{
|
|
ps.Add(new Point(end.X, start.Y));
|
|
}
|
|
return ps.ToArray();
|
|
}
|
|
|
|
// From左锚点To左锚点
|
|
private Point[] L2L(Point start, Point end)
|
|
{
|
|
List<Point> ps = new List<Point>();
|
|
if (start.X > end.X)
|
|
{
|
|
ps.Add(new Point(end.X, start.Y));
|
|
}
|
|
else
|
|
{
|
|
ps.Add(new Point(start.X, end.Y));
|
|
}
|
|
|
|
return ps.ToArray();
|
|
}
|
|
|
|
// From左锚点To右锚点
|
|
private Point[] L2R(Point start, Point end)
|
|
{
|
|
double cx = (end.X - start.X) / 2;
|
|
double cy = (end.Y - start.Y) / 2;
|
|
|
|
List<Point> ps = new List<Point>();
|
|
if (start.X < end.X)
|
|
{
|
|
ps.Add(new Point(start.X, start.Y + cy));
|
|
ps.Add(new Point(end.X, start.Y + cy));
|
|
}
|
|
else
|
|
{
|
|
ps.Add(new Point(start.X + cx, start.Y));
|
|
ps.Add(new Point(start.X + cx, end.Y));
|
|
}
|
|
|
|
return ps.ToArray();
|
|
}
|
|
|
|
// From左锚点To下锚点
|
|
private Point[] L2B(Point start, Point end)
|
|
{
|
|
double cx = (end.X - start.X) / 2;
|
|
double cy = (end.Y - start.Y) / 2;
|
|
|
|
List<Point> ps = new List<Point>();
|
|
if (start.X < end.X && start.Y > end.Y)
|
|
{
|
|
ps.Add(new Point(start.X, start.Y + cy));
|
|
ps.Add(new Point(end.X, end.Y - cy));
|
|
}
|
|
else if (start.X > end.X && start.Y < end.Y)
|
|
{
|
|
ps.Add(new Point(start.X + cx, start.Y));
|
|
ps.Add(new Point(end.X - cx, end.Y));
|
|
}
|
|
else if (start.X > end.X)
|
|
{
|
|
ps.Add(new Point(end.X, start.Y));
|
|
}
|
|
else
|
|
{
|
|
ps.Add(new Point(start.X, end.Y));
|
|
}
|
|
return ps.ToArray();
|
|
}
|
|
|
|
// Fromd左锚点To NULL
|
|
private Point[] L2N(Point start, Point end)
|
|
{
|
|
List<Point> ps = new List<Point>();
|
|
if (start.X > end.X)
|
|
{
|
|
ps.Add(new Point(end.X, start.Y));
|
|
}
|
|
else
|
|
{
|
|
ps.Add(new Point(start.X, end.Y));
|
|
}
|
|
|
|
return ps.ToArray();
|
|
}
|
|
#endregion
|
|
|
|
#region 上锚点
|
|
// From上锚点To上锚点
|
|
private Point[] T2T(Point start, Point end)
|
|
{
|
|
List<Point> ps = new List<Point>();
|
|
if (start.Y > end.Y)
|
|
{
|
|
ps.Add(new Point(start.X, end.Y));
|
|
}
|
|
else
|
|
{
|
|
ps.Add(new Point(end.X, start.Y));
|
|
}
|
|
return ps.ToArray();
|
|
}
|
|
|
|
// From上锚点To下锚点
|
|
private Point[] T2B(Point start, Point end)
|
|
{
|
|
double cx = (end.X - start.X) / 2;
|
|
double cy = (end.Y - start.Y) / 2;
|
|
|
|
List<Point> ps = new List<Point>();
|
|
if (start.Y > end.Y)
|
|
{
|
|
ps.Add(new Point(start.X, start.Y + cy));
|
|
ps.Add(new Point(end.X, end.Y - cy));
|
|
}
|
|
else
|
|
{
|
|
ps.Add(new Point(start.X + cx, start.Y));
|
|
ps.Add(new Point(end.X - cx, end.Y));
|
|
}
|
|
return ps.ToArray();
|
|
}
|
|
|
|
// From上锚点To左锚点
|
|
private Point[] T2L(Point start, Point end)
|
|
{
|
|
double cx = (end.X - start.X) / 2;
|
|
double cy = (end.Y - start.Y) / 2;
|
|
|
|
List<Point> ps = new List<Point>();
|
|
if (start.Y > end.Y && start.X > end.X)
|
|
{
|
|
ps.Add(new Point(start.X, start.Y + cy));
|
|
ps.Add(new Point(end.X, end.Y - cy));
|
|
}
|
|
else if (start.Y < end.Y && start.X < end.X)
|
|
{
|
|
ps.Add(new Point(start.X + cx, start.Y));
|
|
ps.Add(new Point(end.X - cx, end.Y));
|
|
}
|
|
else if (start.X < end.X)
|
|
{
|
|
ps.Add(new Point(start.X, end.Y));
|
|
}
|
|
else
|
|
{
|
|
ps.Add(new Point(end.X, start.Y));
|
|
}
|
|
return ps.ToArray();
|
|
}
|
|
|
|
// From上锚点To右锚点
|
|
private Point[] T2R(Point start, Point end)
|
|
{
|
|
double cx = (end.X - start.X) / 2;
|
|
double cy = (end.Y - start.Y) / 2;
|
|
|
|
List<Point> ps = new List<Point>();
|
|
if (start.Y > end.Y && start.X > end.X)
|
|
{
|
|
ps.Add(new Point(start.X, end.Y));
|
|
}
|
|
else if (start.Y < end.Y && start.X < end.X)
|
|
{
|
|
ps.Add(new Point(end.X, start.Y));
|
|
}
|
|
else if (start.X < end.X)
|
|
{
|
|
ps.Add(new Point(start.X, start.Y + cy));
|
|
ps.Add(new Point(end.X, end.Y - cy));
|
|
}
|
|
else
|
|
{
|
|
ps.Add(new Point(start.X + cx, start.Y));
|
|
ps.Add(new Point(end.X - cx, end.Y));
|
|
}
|
|
return ps.ToArray();
|
|
}
|
|
|
|
// Fromd上锚点To NULL
|
|
private Point[] T2N(Point start, Point end)
|
|
{
|
|
List<Point> ps = new List<Point>();
|
|
if (start.Y > end.Y)
|
|
{
|
|
ps.Add(new Point(start.X, end.Y));
|
|
}
|
|
else
|
|
{
|
|
ps.Add(new Point(end.X, start.Y));
|
|
}
|
|
|
|
return ps.ToArray();
|
|
}
|
|
#endregion
|
|
}
|
|
}
|