实现了拖拽式设置方法调用顺序、方法入参参数来源

This commit is contained in:
fengjiayi
2024-10-24 23:32:43 +08:00
parent 0666f0b2c1
commit 6f26d303e4
43 changed files with 2282 additions and 763 deletions

View File

@@ -0,0 +1,34 @@
using Serein.Workbench.Node.View;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
namespace Serein.Workbench.Node
{
/// <summary>
/// 约束一个节点应该有哪些控制点
/// </summary>
public interface INodeJunction
{
/// <summary>
/// 方法执行入口控制点
/// </summary>
JunctionControlBase ExecuteJunction { get; }
/// <summary>
/// 执行完成后下一个要执行的方法控制点
/// </summary>
JunctionControlBase NextStepJunction { get; }
/// <summary>
/// 参数节点控制点
/// </summary>
JunctionControlBase[] ArgDataJunction { get; }
/// <summary>
/// 返回值控制点
/// </summary>
JunctionControlBase ReturnDataJunction { get; }
}
}

View File

@@ -0,0 +1,222 @@
using Serein.Library;
using Serein.Workbench.Extension;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;
namespace Serein.Workbench.Node.View
{
/// <summary>
/// 连接线的类型
/// </summary>
public enum LineType
{
/// <summary>
/// 贝塞尔曲线
/// </summary>
Bezier,
/// <summary>
/// 半圆线
/// </summary>
Semicircle,
}
/// <summary>
/// 贝塞尔曲线
/// </summary>
public class BezierLine : Shape
{
private readonly double strokeThickness;
private readonly LineType lineType;
/// <summary>
/// 确定起始坐标和目标坐标、外光样式的曲线
/// </summary>
/// <param name="start"></param>
/// <param name="end"></param>
/// <param name="brush"></param>
/// <param name="strokeThickness"></param>
public BezierLine(LineType lineType, Point start, Point end, Brush brush, double strokeThickness = 4)
{
this.lineType = lineType;
this.brush = brush;
startPoint = start;
endPoint = end;
this.strokeThickness = strokeThickness;
InitElementPoint();
InvalidateVisual(); // 触发重绘
}
public void InitElementPoint()
{
hitVisiblePen = new Pen(Brushes.Transparent, 1.0); // 初始化碰撞检测线
hitVisiblePen.Freeze(); // Freeze以提高性能
visualPen = new Pen(brush, 3.0); // 默认可视化Pen
visualPen.Freeze(); // Freeze以提高性能
linkSize = 4; // 整线条粗细
Panel.SetZIndex(this, -9999999); // 置底
}
/// <summary>
/// 更新线条落点位置
/// </summary>
/// <param name="start"></param>
/// <param name="end"></param>
public void UpdatePoints(Point start, Point end)
{
startPoint = start;
endPoint = end;
InvalidateVisual(); // 触发重绘
}
/// <summary>
/// 更新线条落点位置
/// </summary>
/// <param name="point"></param>
public void UpdateEndPoints(Point point)
{
endPoint = point;
InvalidateVisual(); // 触发重绘
}
/// <summary>
/// 更新线条落点位置
/// </summary>
/// <param name="point"></param>
public void UpdateStartPoints(Point point)
{
startPoint = point;
InvalidateVisual(); // 触发重绘
}
/// <summary>
/// 控件重绘事件
/// </summary>
/// <param name="drawingContext"></param>
protected override void OnRender(DrawingContext drawingContext)
{
// 刷新线条显示位置
switch (this.lineType)
{
case LineType.Bezier:
DrawBezierCurve(drawingContext, startPoint, endPoint, linkSize);
break;
case LineType.Semicircle:
DrawBezierCurve(drawingContext, startPoint, endPoint);
break;
default:
break;
}
}
private readonly StreamGeometry streamGeometry = new StreamGeometry();
private Point rightCenterOfStartLocation; // 目标节点选择左侧边缘中心
private Point leftCenterOfEndLocation; // 起始节点选择右侧边缘中心
private Pen hitVisiblePen; // 初始化碰撞检测线
private Pen visualPen; // 默认可视化Pen
private Point startPoint; // 连接线的起始节点
private Point endPoint; // 连接线的终点
private Brush brush; // 线条颜色
double linkSize; // 根据缩放比例调整线条粗细
protected override Geometry DefiningGeometry => streamGeometry;
#region
private Point c0, c1; // 用于计算贝塞尔曲线控制点逻辑
private Vector axis = new Vector(1, 0);
private Vector startToEnd;
private void DrawBezierCurve(DrawingContext drawingContext,
Point start,
Point end,
double linkSize,
bool isHitTestVisible = false,
double strokeThickness = 1.0,
bool isMouseOver = false,
double dashOffset = 0.0)
{
// 控制点的计算逻辑
double power = 100; // 控制贝塞尔曲线的“拉伸”强度
// 计算轴向向量与起点到终点的向量
//var axis = new Vector(1, 0);
startToEnd = (end.ToVector() - start.ToVector()).NormalizeTo();
// 计算拉伸程度k拉伸与水平夹角正相关
var k = 1 - Math.Pow(Math.Max(0, axis.DotProduct(startToEnd)), 10.0);
// 如果起点x大于终点x增加额外的偏移量避免重叠
var bias = start.X > end.X ? Math.Abs(start.X - end.X) * 0.25 : 0;
// 控制点的实际计算
c0 = new Point(+(power + bias) * k + start.X, start.Y);
c1 = new Point(-(power + bias) * k + end.X, end.Y);
// 准备StreamGeometry以用于绘制曲线
streamGeometry.Clear();
using (var context = streamGeometry.Open())
{
context.BeginFigure(start, true, false); // 曲线起点
context.BezierTo(c0, c1, end, true, false); // 画贝塞尔曲线
}
drawingContext.DrawGeometry(null, visualPen, streamGeometry);
// 绘制碰撞检测线
//if (true)
//{
// //hitVisiblePen = new Pen(Brushes.Transparent, linkSize + strokeThickness);
// //hitVisiblePen.Freeze();
// drawingContext.DrawGeometry(null, hitVisiblePen, streamGeometry);
//}
//else
//{
//}
}
#endregion
private void DrawBezierCurve(DrawingContext drawingContext, Point start, Point end)
{
// 计算中心点和半径
// 计算圆心和半径
double x = 35 ;
// 创建一个弧线路径
streamGeometry.Clear();
using (var context = streamGeometry.Open())
{
// 开始绘制
context.BeginFigure(start, false, false);
// 生成弧线
context.ArcTo(
end, // 结束点
new Size(x, x), // 椭圆的半径
0, // 椭圆的旋转角度
false, // 是否大弧
SweepDirection.Counterclockwise, // 方向
true, // 是否连接到起始点
true // 是否使用高质量渲染
);
// 结束绘制
context.LineTo(start, false, false); // 连接到起始点(可选)
}
// 绘制弧线
drawingContext.DrawGeometry(null, visualPen, streamGeometry);
}
}
}

View File

@@ -1,93 +0,0 @@
using Serein.Library;
using Serein.Library.Utils;
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
namespace Serein.Workbench.Node.View
{
public abstract class JunctionControlBase : UserControl
{
public double _MyWidth = 20;
public double _MyHeight = 20;
protected JunctionControlBase()
{
//this.Width = 20;
//this.Height = 20;
this.MouseDown += ControlPointBase_MouseDown;
this.MouseMove += ControlPointBase_MouseMove; ;
}
#region
public static readonly DependencyProperty NodeGuidProperty =
DependencyProperty.Register("NodeGuid", typeof(string), typeof(JunctionControlBase), new PropertyMetadata(default(string)));
/// <summary>
/// 所在的节点
/// </summary>
public string NodeGuid
{
get { return (string)GetValue(NodeGuidProperty); }
set { SetValue(NodeGuidProperty, value.ToString()); }
}
#endregion
#region
public static readonly DependencyProperty JunctionTypeProperty =
DependencyProperty.Register("JunctionType", typeof(string), typeof(JunctionControlBase), new PropertyMetadata(default(string)));
public JunctionType JunctionType
{
get { return EnumHelper.ConvertEnum<JunctionType>(GetValue(JunctionTypeProperty).ToString()); }
set { SetValue(JunctionTypeProperty, value.ToString()); }
}
#endregion
public abstract void Render();
private void ControlPointBase_MouseMove(object sender, MouseEventArgs e)
{
if (GlobalJunctionData.MyGlobalData is null) return;
GlobalJunctionData.MyGlobalData.ChangingJunction = this;
}
/// <summary>
/// 在碰撞点上按下鼠标控件开始进行移动
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ControlPointBase_MouseDown(object sender, MouseButtonEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
GlobalJunctionData.MyGlobalData = new ConnectingData();
var myDataType = GlobalJunctionData.MyGlobalData;
myDataType.StartJunction = this;
var canvas = MainWindow.GetParentOfType<Canvas>(this);
myDataType.StartPoint = this.TranslatePoint(new Point(this.Width /2 , this.Height /2 ), canvas);
myDataType.VirtualLine = new MyLine(canvas, new Line // 虚拟线
{
Stroke = Brushes.OldLace,
StrokeThickness = 2
});
}
e.Handled = true;
}
}
}

View File

@@ -0,0 +1,134 @@
using Serein.Library;
using Serein.Library.Utils;
using System;
using System.Net;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
namespace Serein.Workbench.Node.View
{
public abstract class JunctionControlBase : Shape
{
protected JunctionControlBase()
{
this.Width = 25;
this.Height = 20;
this.MouseDown += ControlPointBase_MouseDown;
this.MouseMove += ControlPointBase_MouseMove;
}
#region
public static readonly DependencyProperty NodeProperty =
DependencyProperty.Register(nameof(MyNode), typeof(NodeModelBase), typeof(JunctionControlBase), new PropertyMetadata(default(NodeModelBase)));
/// <summary>
/// 所在的节点
/// </summary>
public NodeModelBase MyNode
{
get { return (NodeModelBase)GetValue(NodeProperty); }
set { SetValue(NodeProperty, value); }
}
#endregion
#region
public static readonly DependencyProperty JunctionTypeProperty =
DependencyProperty.Register(nameof(JunctionType), typeof(string), typeof(JunctionControlBase), new PropertyMetadata(default(string)));
/// <summary>
/// 控制点类型
/// </summary>
public JunctionType JunctionType
{
get { return EnumHelper.ConvertEnum<JunctionType>(GetValue(JunctionTypeProperty).ToString()); }
set { SetValue(JunctionTypeProperty, value.ToString()); }
}
#endregion
protected readonly StreamGeometry StreamGeometry = new StreamGeometry();
protected override Geometry DefiningGeometry => StreamGeometry;
/// <summary>
/// 重绘方法
/// </summary>
/// <param name="drawingContext"></param>
public abstract void Render(DrawingContext drawingContext);
/// <summary>
/// 中心点
/// </summary>
public abstract Point MyCenterPoint { get; }
// 处理鼠标悬停状态
private bool _isMouseOver;
public bool IsMouseOver
{
get => _isMouseOver;
set
{
_isMouseOver = value;
InvalidateVisual();
}
}
/// <summary>
/// 控件重绘事件
/// </summary>
/// <param name="drawingContext"></param>
protected override void OnRender(DrawingContext drawingContext)
{
Render(drawingContext);
}
protected void ControlPointBase_MouseMove(object sender, MouseEventArgs e)
{
if (GlobalJunctionData.MyGlobalConnectingData is null) return;
GlobalJunctionData.MyGlobalConnectingData.CurrentJunction = this;
}
/// <summary>
/// 在碰撞点上按下鼠标控件开始进行移动
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void ControlPointBase_MouseDown(object sender, MouseButtonEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
GlobalJunctionData.MyGlobalConnectingData = new ConnectingData();
var myDataType = GlobalJunctionData.MyGlobalConnectingData;
myDataType.StartJunction = this;
var canvas = MainWindow.GetParentOfType<Canvas>(this);
if (canvas != null)
{
//myDataType.StartPoint = this.MyCenterPoint;
myDataType.StartPoint = this.TranslatePoint(new Point(this.Width / 2, this.Height / 2), canvas);
var bezierLine = new BezierLine(LineType.Bezier, myDataType.StartPoint, myDataType.StartPoint, Brushes.Green);
myDataType.VirtualLine = new MyLine(canvas, bezierLine);
}
}
e.Handled = true;
}
private Point GetStartPoint()
{
return new Point(this.ActualWidth / 2, this.ActualHeight / 2); // 起始节点选择右侧边缘中心
}
}
}

View File

@@ -13,7 +13,7 @@ namespace Serein.Workbench.Node.View
#region Model
public class MyLine
{
public MyLine(Canvas canvas, Line line)
public MyLine(Canvas canvas, BezierLine line)
{
Canvas = canvas;
VirtualLine = line;
@@ -21,7 +21,7 @@ namespace Serein.Workbench.Node.View
}
public Canvas Canvas { get; set; }
public Line VirtualLine { get; set; }
public BezierLine VirtualLine { get; set; }
public void Remove()
{
@@ -32,33 +32,98 @@ namespace Serein.Workbench.Node.View
public class ConnectingData
{
public JunctionControlBase StartJunction { get; set; }
public JunctionControlBase ChangingJunction { get; set; }
public JunctionControlBase CurrentJunction { get; set; }
public Point StartPoint { get; set; }
public MyLine VirtualLine { get; set; }
}
public static class GlobalJunctionData
{
private static ConnectingData? myGlobalData;
/// <summary>
/// 是否允许连接
/// </summary>
public static ConnectingData? MyGlobalData
{
get => myGlobalData;
set
public bool IsCanConnected { get
{
if (myGlobalData == null)
if(StartJunction is null
|| CurrentJunction is null
)
{
myGlobalData = value;
return false;
}
if (!StartPoint.Equals(CurrentJunction))
{
return true;
}
else
{
// 自己连接自己的情况下只能是从arg控制点连接到execute控制点。
if (CurrentJunction.JunctionType == Library.JunctionType.Execute
&& StartJunction.JunctionType == Library.JunctionType.ArgData)
{
return true;
}
if (CurrentJunction.JunctionType == Library.JunctionType.ArgData
&& StartJunction.JunctionType == Library.JunctionType.Execute)
{
// 需要是自己连接自己且只能是从arg控制点连接到execute控制点。
return true;
}
return false;
}
}
}
/// <summary>
/// 更新临时的连接线
/// </summary>
/// <param name="point"></param>
public void UpdatePoint(Point point)
{
if (StartJunction is null
|| CurrentJunction is null
)
{
return;
}
if (StartJunction.JunctionType == Library.JunctionType.Execute
|| StartJunction.JunctionType == Library.JunctionType.ArgData)
{
VirtualLine.VirtualLine.UpdateStartPoints(point);
}
else
{
VirtualLine.VirtualLine.UpdateEndPoints(point);
}
}
public static bool IsCreatingConnection => myGlobalData is not null;
}
public static bool CanCreate => myGlobalData?.ChangingJunction.Equals(myGlobalData?.StartJunction) == false;
public static class GlobalJunctionData
{
private static ConnectingData? myGlobalData;
private static object _lockObj = new object();
/// <summary>
/// 创建节点之间控制点的连接行为
/// </summary>
public static ConnectingData? MyGlobalConnectingData
{
get => myGlobalData;
set
{
lock (_lockObj)
{
myGlobalData ??= value;
}
}
}
/// <summary>
/// 删除连接视觉效果
/// </summary>
public static void OK()
{
myGlobalData?.VirtualLine.Remove();

View File

@@ -10,7 +10,7 @@ namespace Serein.Workbench.Node.View
public ArgJunctionControl()
{
base.JunctionType = JunctionType.ArgData;
Render();
this.InvalidateVisual();
}
#region
@@ -25,28 +25,50 @@ namespace Serein.Workbench.Node.View
get { return (int)GetValue(ArgIndexProperty); }
set { SetValue(ArgIndexProperty, value); }
}
#endregion
public override void Render()
private Point _myCenterPoint;
public override Point MyCenterPoint { get => _myCenterPoint; }
public override void Render(DrawingContext drawingContext)
{
if(double.IsNaN(base.Width))
{
base.Width = base._MyWidth;
}
if (double.IsNaN(base.Height))
{
base.Height = base._MyHeight;
}
double width = ActualWidth;
double height = ActualHeight;
// 输入连接器的背景
var connectorBackground = IsMouseOver ? Brushes.DarkCyan : Brushes.Transparent;
var connectorRect = new Rect(4, 4, width - 8, height - 8);
drawingContext.DrawRectangle(connectorBackground, null, connectorRect);
// 定义圆形的大小和位置
double connectorSize = 10; // 连接器的大小
double circleCenterX = 8; // 圆心 X 坐标
double circleCenterY = height / 2; // 圆心 Y 坐标
var circlePoint = new Point(circleCenterX, circleCenterY);
// 绘制连接器的圆形部分
var ellipse = new EllipseGeometry(circlePoint, connectorSize / 2, connectorSize / 2);
_myCenterPoint = new Point(circleCenterX - connectorSize / 2, circleCenterY);
drawingContext.DrawGeometry(IsMouseOver ? Brushes.DarkCyan : Brushes.Transparent, new Pen(Brushes.Black, 1), ellipse);
var ellipse = new Ellipse
// 定义三角形的间距
double triangleOffsetX = 4; // 三角形与圆形的间距
double triangleCenterX = circleCenterX + connectorSize / 2 + triangleOffsetX; // 三角形中心 X 坐标
double triangleCenterY = circleCenterY; // 三角形中心 Y 坐标
// 绘制三角形
var pathGeometry = new StreamGeometry();
using (var context = pathGeometry.Open())
{
Width = base.Width,
Height = base.Height,
Fill = Brushes.Orange,
ToolTip = "入参"
};
Content = ellipse;
context.BeginFigure(new Point(triangleCenterX, triangleCenterY - 4.5), true, true);
context.LineTo(new Point(triangleCenterX + 5, triangleCenterY), true, false);
context.LineTo(new Point(triangleCenterX, triangleCenterY + 4.5), true, false);
context.LineTo(new Point(triangleCenterX, triangleCenterY - 4.5), true, false);
}
drawingContext.DrawGeometry(IsMouseOver ? Brushes.DarkCyan : Brushes.Transparent, new Pen(Brushes.Black, 1), pathGeometry);
}
}

View File

@@ -1,4 +1,6 @@
using System.Windows.Media;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;
using Serein.Library;
@@ -6,32 +8,75 @@ namespace Serein.Workbench.Node.View
{
public class ExecuteJunctionControl : JunctionControlBase
{
//public override JunctionType JunctionType { get; } = JunctionType.Execute;
public ExecuteJunctionControl()
{
base.JunctionType = JunctionType.Execute;
Render();
this.InvalidateVisual();
}
public override void Render()
private Point _myCenterPoint;
public override Point MyCenterPoint { get => _myCenterPoint; }
public override void Render(DrawingContext drawingContext)
{
if (double.IsNaN(base.Width))
{
base.Width = base._MyWidth;
}
if (double.IsNaN(base.Height))
{
base.Height = base._MyHeight;
}
double width = ActualWidth;
double height = ActualHeight;
var rect = new Rectangle
// 绘制边框
//var borderBrush = new SolidColorBrush(Colors.Black);
//var borderThickness = 1.0;
//var borderRect = new Rect(0, 0, width, height);
//drawingContext.DrawRectangle(null, new Pen(borderBrush, borderThickness), borderRect);
// 输入连接器的背景
var connectorBackground = IsMouseOver ? Brushes.DarkCyan : Brushes.Transparent;
var connectorRect = new Rect(4, 4, width - 8, height - 8);
drawingContext.DrawRectangle(connectorBackground, null, connectorRect);
// 定义圆形的大小和位置
double connectorSize = 10; // 连接器的大小
double circleCenterX = 8; // 圆心 X 坐标
double circleCenterY = height / 2; // 圆心 Y 坐标
var circlePoint = new Point(circleCenterX, circleCenterY);
// 绘制连接器的圆形部分
var ellipse = new EllipseGeometry(circlePoint, connectorSize / 2, connectorSize / 2);
_myCenterPoint = new Point(circleCenterX - connectorSize / 2, circleCenterY);
drawingContext.DrawGeometry(IsMouseOver ? Brushes.DarkCyan : Brushes.Transparent, new Pen(Brushes.Black, 1), ellipse);
// 定义三角形的间距
double triangleOffsetX = 4; // 三角形与圆形的间距
double triangleCenterX = circleCenterX + connectorSize / 2 + triangleOffsetX; // 三角形中心 X 坐标
double triangleCenterY = circleCenterY; // 三角形中心 Y 坐标
// 绘制三角形
var pathGeometry = new StreamGeometry();
using (var context = pathGeometry.Open())
{
Width = base.Width,
Height = base.Height,
Fill = Brushes.Green,
ToolTip = "方法执行"
};
Content = rect;
context.BeginFigure(new Point(triangleCenterX, triangleCenterY - 4.5), true, true);
context.LineTo(new Point(triangleCenterX + 5, triangleCenterY), true, false);
context.LineTo(new Point(triangleCenterX, triangleCenterY + 4.5), true, false);
context.LineTo(new Point(triangleCenterX, triangleCenterY - 4.5), true, false);
}
drawingContext.DrawGeometry(IsMouseOver ? Brushes.DarkCyan : Brushes.Transparent, new Pen(Brushes.Black, 1), pathGeometry);
// 绘制标签
//var formattedText = new FormattedText(
// "执行",
// System.Globalization.CultureInfo.CurrentCulture,
// FlowDirection.LeftToRight,
// new Typeface("Segoe UI"),
// 12,
// Brushes.Black,
// VisualTreeHelper.GetDpi(this).PixelsPerDip);
//drawingContext.DrawText(formattedText, new Point(18,1));
}
}

View File

@@ -1,4 +1,5 @@
using System.Windows.Media;
using System.Windows;
using System.Windows.Media;
using System.Windows.Shapes;
using Serein.Library;
@@ -11,28 +12,50 @@ namespace Serein.Workbench.Node.View
public NextStepJunctionControl()
{
base.JunctionType = JunctionType.NextStep;
Render();
this.InvalidateVisual();
}
public override void Render()
private Point _myCenterPoint;
public override Point MyCenterPoint { get => _myCenterPoint; }
public override void Render(DrawingContext drawingContext)
{
if (double.IsNaN(base.Width))
{
base.Width = base._MyWidth;
}
if (double.IsNaN(base.Height))
{
base.Height = base._MyHeight;
}
double width = ActualWidth;
double height = ActualHeight;
var rect = new Rectangle
// 输入连接器的背景
var connectorBackground = IsMouseOver ? Brushes.DarkCyan : Brushes.Transparent;
var connectorRect = new Rect(4, 4, width - 8, height - 8);
drawingContext.DrawRectangle(connectorBackground, null, connectorRect);
// 定义圆形的大小和位置
double connectorSize = 10; // 连接器的大小
double circleCenterX = 8; // 圆心 X 坐标
double circleCenterY = height / 2; // 圆心 Y 坐标
var circlePoint = new Point(circleCenterX, circleCenterY);
// 绘制连接器的圆形部分
var ellipse = new EllipseGeometry(circlePoint, connectorSize / 2, connectorSize / 2);
drawingContext.DrawGeometry(IsMouseOver ? Brushes.DarkCyan : Brushes.Transparent, new Pen(Brushes.Black, 1), ellipse);
_myCenterPoint = new Point(circleCenterX + connectorSize / 2, circleCenterY);
// 绘制连接器的圆形部分
//var ellipse = new EllipseGeometry(circlePoint, connectorSize / 2, connectorSize / 2);
// 定义三角形的间距
double triangleOffsetX = 4; // 三角形与圆形的间距
double triangleCenterX = circleCenterX + connectorSize / 2 + triangleOffsetX; // 三角形中心 X 坐标
double triangleCenterY = circleCenterY; // 三角形中心 Y 坐标
// 绘制三角形
var pathGeometry = new StreamGeometry();
using (var context = pathGeometry.Open())
{
Width = base.Width,
Height = base.Height,
Fill = Brushes.Blue,
ToolTip = "下一个方法值"
};
Content = rect;
context.BeginFigure(new Point(triangleCenterX, triangleCenterY - 4.5), true, true);
context.LineTo(new Point(triangleCenterX + 5, triangleCenterY), true, false);
context.LineTo(new Point(triangleCenterX, triangleCenterY + 4.5), true, false);
context.LineTo(new Point(triangleCenterX, triangleCenterY - 4.5), true, false);
}
drawingContext.DrawGeometry(IsMouseOver ? Brushes.DarkCyan : Brushes.Transparent, new Pen(Brushes.Black, 1), pathGeometry);
}
}
}

View File

@@ -1,4 +1,5 @@
using System.Windows.Media;
using System.Windows;
using System.Windows.Media;
using System.Windows.Shapes;
using Serein.Library;
@@ -12,28 +13,47 @@ namespace Serein.Workbench.Node.View
public ResultJunctionControl()
{
base.JunctionType = JunctionType.ReturnData;
Render();
this.InvalidateVisual();
}
private Point _myCenterPoint;
public override Point MyCenterPoint { get => _myCenterPoint; }
public override void Render()
public override void Render(DrawingContext drawingContext)
{
if (double.IsNaN(base.Width))
{
base.Width = base._MyWidth;
}
if (double.IsNaN(base.Height))
{
base.Height = base._MyHeight;
}
double width = ActualWidth;
double height = ActualHeight;
var rect = new Rectangle
// 输入连接器的背景
var connectorBackground = IsMouseOver ? Brushes.DarkCyan : Brushes.Transparent;
var connectorRect = new Rect(4, 4, width - 8, height - 8);
drawingContext.DrawRectangle(connectorBackground, null, connectorRect);
// 定义圆形的大小和位置
double connectorSize = 10; // 连接器的大小
double circleCenterX = 8; // 圆心 X 坐标
double circleCenterY = height / 2; // 圆心 Y 坐标
var circlePoint = new Point(circleCenterX, circleCenterY);
// 绘制连接器的圆形部分
var ellipse = new EllipseGeometry(circlePoint, connectorSize / 2, connectorSize / 2);
_myCenterPoint = new Point(circleCenterX - connectorSize / 2, circleCenterY);
drawingContext.DrawGeometry(IsMouseOver ? Brushes.DarkCyan : Brushes.Transparent, new Pen(Brushes.Black, 1), ellipse);
// 定义三角形的间距
double triangleOffsetX = 4; // 三角形与圆形的间距
double triangleCenterX = circleCenterX + connectorSize / 2 + triangleOffsetX; // 三角形中心 X 坐标
double triangleCenterY = circleCenterY; // 三角形中心 Y 坐标
// 绘制三角形
var pathGeometry = new StreamGeometry();
using (var context = pathGeometry.Open())
{
Width = base.Width,
Height = base.Height,
Fill = Brushes.Red,
ToolTip = "返回值"
};
Content = rect;
context.BeginFigure(new Point(triangleCenterX, triangleCenterY - 4.5), true, true);
context.LineTo(new Point(triangleCenterX + 5, triangleCenterY), true, false);
context.LineTo(new Point(triangleCenterX, triangleCenterY + 4.5), true, false);
context.LineTo(new Point(triangleCenterX, triangleCenterY - 4.5), true, false);
}
drawingContext.DrawGeometry(IsMouseOver ? Brushes.DarkCyan : Brushes.Transparent, new Pen(Brushes.Black, 1), pathGeometry);
}
}
}

View File

@@ -15,6 +15,11 @@ namespace Serein.Workbench.Node.View
/// </summary>
public abstract class NodeControlBase : UserControl, IDynamicFlowNode
{
/// <summary>
/// 记录与该节点控件有关的所有连接
/// </summary>
private readonly List<ConnectionControl> connectionControls = new List<ConnectionControl>();
public NodeControlViewModelBase ViewModel { get; set; }
@@ -30,7 +35,51 @@ namespace Serein.Workbench.Node.View
SetBinding();
}
/// <summary>
/// 添加与该节点有关的连接后,记录下来
/// </summary>
/// <param name="connection"></param>
public void AddCnnection(ConnectionControl connection)
{
connectionControls.Add(connection);
}
/// <summary>
/// 删除了连接之后,还需要从节点中的记录移除
/// </summary>
/// <param name="connection"></param>
public void RemoveCnnection(ConnectionControl connection)
{
connectionControls.Remove(connection);
connection.Remote();
}
/// <summary>
/// 删除了连接之后,还需要从节点中的记录移除
/// </summary>
public void RemoveAllConection()
{
foreach (var connection in this.connectionControls)
{
connection.Remote(); // 主动更新连线位置
}
}
/// <summary>
/// 更新与该节点有关的数据
/// </summary>
public void UpdateLocationConnections()
{
foreach (var connection in this.connectionControls)
{
connection.RefreshLine(); // 主动更新连线位置
}
}
/// <summary>
/// 设置绑定:
/// Canvas.X and Y 画布位置
/// </summary>
public void SetBinding()
{
// 绑定 Canvas.Left

View File

@@ -1,6 +1,8 @@
using Serein.Library;
using Serein.Library.Api;
using Serein.Workbench.Extension;
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
@@ -39,7 +41,7 @@ namespace Serein.Workbench.Node.View
/// <summary>
/// 连接类型
/// </summary>
public ConnectionType Type { get; set; }
public ConnectionInvokeType Type { get; set; }
}
@@ -68,26 +70,26 @@ namespace Serein.Workbench.Node.View
}
/*
* 有1个Execute
* 有1个NextStep
* 有0~65535个入参 ushort
* 有1个ReturnData(void方法返回null)
*
* Execute // 执行这个方法
* 只接受 NextStep 的连接
* ArgData
* 互相之间不能连接,只能接受 Execute、ReturnData 的连接
* Execute表示从 Execute所在节点 获取数据
* ReturnData 表示从对应节点获取数据
* ReturnData:
* 只能发起主动连接,且只能连接到 ArgData
* NextStep
* 只能连接连接 Execute
*
*
*
*/
@@ -102,34 +104,58 @@ namespace Serein.Workbench.Node.View
/// <summary>
/// 连接控件,表示控件的连接关系
/// </summary>
public class ConnectionControl : Shape
public class ConnectionControl
{
private readonly IFlowEnvironment environment;
private readonly Action RemoteCallback;
/// <summary>
/// 初始化连接控件
/// 关于调用
/// </summary>
/// <param name="Canvas"></param>
/// <param name="Type"></param>
public ConnectionControl(IFlowEnvironment environment,
Canvas Canvas,
ConnectionType Type,
NodeControlBase Start,
NodeControlBase End)
public ConnectionControl(Canvas Canvas,
ConnectionInvokeType Type,
JunctionControlBase Start,
JunctionControlBase End,
Action remoteCallback)
{
this.environment = environment;
this.LineType = LineType.Bezier;
this.RemoteCallback = remoteCallback;
this.Canvas = Canvas;
this.Type = Type;
this.Start = Start;
this.End = End;
//this.Start.Background = GetLineColor(Type); // 线条颜色
//this.End.Background = GetLineColor(Type); // 线条颜色
InitElementPoint();
}
/// <summary>
/// 关于入参
/// </summary>
/// <param name="Canvas"></param>
/// <param name="Type"></param>
public ConnectionControl(LineType LineType,
Canvas Canvas,
int argIndex,
ConnectionArgSourceType connectionArgSourceType,
JunctionControlBase Start,
JunctionControlBase End,
Action remoteCallback)
{
this.LineType = LineType;
this.RemoteCallback = remoteCallback;
this.Canvas = Canvas;
this.ArgIndex = ArgIndex;
this.ConnectionArgSourceType = connectionArgSourceType;
this.Start = Start;
this.End = End;
//this.Start.Background = GetLineColor(Type); // 线条颜色
//this.End.Background = GetLineColor(Type); // 线条颜色
InitElementPoint();
}
@@ -139,108 +165,114 @@ namespace Serein.Workbench.Node.View
public Canvas Canvas { get; }
/// <summary>
/// 连接类型
/// 调用方法类型,连接类型
/// </summary>
public ConnectionType Type { get; }
public ConnectionInvokeType Type { get; }
/// <summary>
/// 起始控件
/// 获取参数类型,第几个参数
/// </summary>
public NodeControlBase Start { get; set; }
public int ArgIndex { get; set; } = -1;
/// <summary>
/// 结束控件
/// 参数来源(决定了连接线的样式)
/// </summary>
public NodeControlBase End { get; set; }
public ConnectionArgSourceType ConnectionArgSourceType { get; set; }
/// <summary>
/// 起始控制点
/// </summary>
public JunctionControlBase Start { get; set; }
/// <summary>
/// 目标控制点
/// </summary>
public JunctionControlBase End { get; set; }
/// <summary>
/// 连接线
/// </summary>
private BezierLine BezierLine;
private LineType LineType;
/// <summary>
/// 配置连接曲线的右键菜单
/// </summary>
/// <param name="line"></param>
private void ConfigureLineContextMenu(ConnectionControl connection)
private void ConfigureLineContextMenu()
{
var contextMenu = new ContextMenu();
contextMenu.Items.Add(MainWindow.CreateMenuItem("删除连线", (s, e) => DeleteConnection(connection)));
connection.ContextMenu = contextMenu;
connection.ContextMenu = contextMenu;
contextMenu.Items.Add(MainWindow.CreateMenuItem("删除连线", (s, e) => this.DeleteConnection()));
BezierLine.ContextMenu = contextMenu;
}
/// <summary>
/// 删除该连线
/// </summary>
/// <param name="line"></param>
private void DeleteConnection(ConnectionControl connection)
public void DeleteConnection()
{
var connectionToRemove = connection;
if (connectionToRemove is null)
if(this.Start is JunctionControlBase startJunctionControlBase)
{
return;
//startJunctionControlBase.Background = Brushes.Transparent;
}
// 获取起始节点与终止节点,消除映射关系
var fromNodeGuid = connectionToRemove.Start.ViewModel.NodeModel.Guid;
var toNodeGuid = connectionToRemove.End.ViewModel.NodeModel.Guid;
environment.RemoveConnectAsync(fromNodeGuid, toNodeGuid, connection.Type);
}
/// <summary>
/// 移除
/// </summary>
public void RemoveFromCanvas()
{
Canvas.Children.Remove(this); // 移除线
if (this.End is JunctionControlBase endJunctionControlBase)
{
//endJunctionControlBase.Background = Brushes.Transparent;
}
Canvas.Children.Remove(BezierLine); // 移除线
RemoteCallback?.Invoke();
}
/// <summary>
/// 重新绘制
/// </summary>
public void AddOrRefreshLine()
public void RefreshLine()
{
this.InvalidateVisual();
(Point startPoint, Point endPoint) = RefreshPoint(Canvas, Start, End);
BezierLine.UpdatePoints(startPoint, endPoint);
//BezierLine.UpdatePoints(startPoint, endPoint);
}
/// <summary>
/// 删除该连线
/// </summary>
public void Remote()
{
Canvas.Children.Remove(BezierLine);
}
/// <summary>
/// 绘制
/// </summary>
public void InitElementPoint()
{
leftCenterOfEndLocation = new Point(0, End.ActualHeight / 2); // 目标节点选择左侧边缘中心
rightCenterOfStartLocation = new Point(Start.ActualWidth, Start.ActualHeight / 2); // 起始节点选择右侧边缘中心
brush = GetLineColor(Type); // 线条颜色
hitVisiblePen = new Pen(Brushes.Transparent, 1.0); // 初始化碰撞检测线
hitVisiblePen.Freeze(); // Freeze以提高性能
visualPen = new Pen(brush, 1.0); // 默认可视化Pen
visualPen.Freeze(); // Freeze以提高性能
ConfigureLineContextMenu(this); // 设置连接右键事件
leftCenterOfEndLocation = Start.MyCenterPoint;
rightCenterOfStartLocation = End.MyCenterPoint;
//leftCenterOfEndLocation = new Point(0, End.ActualHeight / 2); // 目标节点选择左侧边缘中心
//rightCenterOfStartLocation = new Point(Start.ActualWidth, Start.ActualHeight / 2); // 起始节点选择右侧边缘中心
linkSize = 4; // 整线条粗细
Canvas.Children.Add(this); // 添加线
Grid.SetZIndex(this, -9999999); // 置底
(Point startPoint, Point endPoint) = RefreshPoint(Canvas, Start, End);
BezierLine = new BezierLine(LineType, startPoint, endPoint, GetLineColor(Type), linkSize);
Grid.SetZIndex(BezierLine, -9999999); // 置底
Canvas.Children.Add(BezierLine);
ConfigureLineContextMenu(); //配置右键菜单
}
/// <summary>
/// 控件重绘事件
/// </summary>
/// <param name="drawingContext"></param>
protected override void OnRender(DrawingContext drawingContext)
{
RefreshPoint(Canvas, this.Start, this.End); // 刷新坐标
DrawBezierCurve(drawingContext, startPoint, endPoint, linkSize, brush); // 刷新线条显示位置
}
private readonly StreamGeometry streamGeometry = new StreamGeometry();
double linkSize; // 根据缩放比例调整线条粗细
private Point rightCenterOfStartLocation; // 目标节点选择左侧边缘中心
private Point leftCenterOfEndLocation; // 起始节点选择右侧边缘中心
private Pen hitVisiblePen; // 初始化碰撞检测线
private Pen visualPen; // 默认可视化Pen
private Point startPoint; // 连接线的起始节点
private Point endPoint; // 连接线的终点
private Brush brush; // 线条颜色
double linkSize; // 根据缩放比例调整线条粗细
protected override Geometry DefiningGeometry => streamGeometry;
#region
public void RefreshPoint(Canvas canvas, FrameworkElement startElement, FrameworkElement endElement)
private (Point startPoint,Point endPoint) RefreshPoint(Canvas canvas, FrameworkElement startElement, FrameworkElement endElement)
{
endPoint = endElement.TranslatePoint(leftCenterOfEndLocation, canvas); // 计算终点位置
startPoint = startElement.TranslatePoint(rightCenterOfStartLocation, canvas); // 获取起始节点的中心位置
var startPoint = startElement.TranslatePoint(rightCenterOfStartLocation, canvas); // 获取起始节点的中心位置
var endPoint = endElement.TranslatePoint(leftCenterOfEndLocation, canvas); // 计算终点位置
return (startPoint, endPoint);
}
/// <summary>
@@ -249,72 +281,280 @@ namespace Serein.Workbench.Node.View
/// <param name="currentConnectionType"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public static SolidColorBrush GetLineColor(ConnectionType currentConnectionType)
public static SolidColorBrush GetLineColor(ConnectionInvokeType currentConnectionType)
{
return currentConnectionType switch
{
ConnectionType.IsSucceed => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#04FC10")),
ConnectionType.IsFail => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#F18905")),
ConnectionType.IsError => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FE1343")),
ConnectionType.Upstream => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#4A82E4")),
_ => throw new Exception(),
ConnectionInvokeType.IsSucceed => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#04FC10")),
ConnectionInvokeType.IsFail => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#F18905")),
ConnectionInvokeType.IsError => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FE1343")),
ConnectionInvokeType.Upstream => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#4A82E4")),
_ => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#56CEF6")),
//_ => throw new Exception(),
};
}
private Point c0, c1; // 用于计算贝塞尔曲线控制点逻辑
private Vector axis = new Vector(1, 0);
private Vector startToEnd;
private void DrawBezierCurve(DrawingContext drawingContext,
Point start,
Point end,
double linkSize,
Brush brush,
bool isHitTestVisible = false,
double strokeThickness = 1.0,
bool isMouseOver = false,
double dashOffset = 0.0)
{
// 控制点的计算逻辑
double power = 8 * 8; // 控制贝塞尔曲线的“拉伸”强度
// 计算轴向向量与起点到终点的向量
//var axis = new Vector(1, 0);
startToEnd = (end.ToVector() - start.ToVector()).NormalizeTo();
// 计算拉伸程度k拉伸与水平夹角正相关
var k = 1 - Math.Pow(Math.Max(0, axis.DotProduct(startToEnd)), 10.0);
// 如果起点x大于终点x增加额外的偏移量避免重叠
var bias = start.X > end.X ? Math.Abs(start.X - end.X) * 0.25 : 0;
// 控制点的实际计算
c0 = new Point(+(power + bias) * k + start.X, start.Y);
c1 = new Point(-(power + bias) * k + end.X, end.Y);
// 准备StreamGeometry以用于绘制曲线
streamGeometry.Clear();
using (var context = streamGeometry.Open())
{
context.BeginFigure(start, true, false); // 曲线起点
context.BezierTo(c0, c1, end, true, false); // 画贝塞尔曲线
}
drawingContext.DrawGeometry(null, visualPen, streamGeometry);
// 绘制碰撞检测线
//if (true)
//{
// //hitVisiblePen = new Pen(Brushes.Transparent, linkSize + strokeThickness);
// //hitVisiblePen.Freeze();
// drawingContext.DrawGeometry(null, hitVisiblePen, streamGeometry);
//}
//else
//{
//}
}
#endregion
}
/*
/// <summary>
/// 连接控件,表示控件的连接关系
/// </summary>
public class ConnectionControl : Shape
{
private readonly Action RemoteCallback;
/// <summary>
/// 关于调用
/// </summary>
/// <param name="Canvas"></param>
/// <param name="Type"></param>
public ConnectionControl(Canvas Canvas,
ConnectionInvokeType Type,
JunctionControlBase Start,
JunctionControlBase End,
Action remoteCallback)
{
this.RemoteCallback = remoteCallback;
this.Canvas = Canvas;
this.Type = Type;
this.Start = Start;
this.End = End;
this.Start.Background = GetLineColor(Type); // 线条颜色
this.End.Background = GetLineColor(Type); // 线条颜色
InitElementPoint();
}
/// <summary>
/// 关于入参
/// </summary>
/// <param name="Canvas"></param>
/// <param name="Type"></param>
public ConnectionControl(Canvas Canvas,
int argIndex,
ConnectionArgSourceType connectionArgSourceType,
JunctionControlBase Start,
JunctionControlBase End,
Action remoteCallback)
{
this.RemoteCallback = remoteCallback;
this.Canvas = Canvas;
this.ArgIndex = ArgIndex;
this.ConnectionArgSourceType = connectionArgSourceType;
this.Start = Start;
this.End = End;
this.Start.Background = GetLineColor(Type); // 线条颜色
this.End.Background = GetLineColor(Type); // 线条颜色
InitElementPoint();
}
/// <summary>
/// 所在的画布
/// </summary>
public Canvas Canvas { get; }
/// <summary>
/// 调用方法类型,连接类型
/// </summary>
public ConnectionInvokeType Type { get; }
/// <summary>
/// 获取参数类型,第几个参数
/// </summary>
public int ArgIndex { get; set; } = -1;
/// <summary>
/// 参数来源(决定了连接线的样式)
/// </summary>
public ConnectionArgSourceType ConnectionArgSourceType { get; set; }
/// <summary>
/// 起始控制点
/// </summary>
public JunctionControlBase Start { get; set; }
/// <summary>
/// 目标控制点
/// </summary>
public JunctionControlBase End { get; set; }
/// <summary>
/// 配置连接曲线的右键菜单
/// </summary>
/// <param name="line"></param>
private void ConfigureLineContextMenu(ConnectionControl connection)
{
var contextMenu = new ContextMenu();
contextMenu.Items.Add(MainWindow.CreateMenuItem("删除连线", (s, e) => DeleteConnection(connection)));
connection.ContextMenu = contextMenu;
}
/// <summary>
/// 删除该连线
/// </summary>
/// <param name="line"></param>
private void DeleteConnection(ConnectionControl connection)
{
var connectionToRemove = connection;
if (connectionToRemove is null)
{
return;
}
if(this.Start is JunctionControlBase startJunctionControlBase)
{
startJunctionControlBase.Background = Brushes.Transparent;
}
if (this.End is JunctionControlBase endJunctionControlBase)
{
endJunctionControlBase.Background = Brushes.Transparent;
}
this.Canvas.g
RemoteCallback?.Invoke();
}
/// <summary>
/// 移除
/// </summary>
public void RemoveFromCanvas()
{
Canvas.Children.Remove(this); // 移除线
}
/// <summary>
/// 重新绘制
/// </summary>
public void AddOrRefreshLine()
{
this.InvalidateVisual();
}
public void InitElementPoint()
{
leftCenterOfEndLocation = new Point(0, End.ActualHeight / 2); // 目标节点选择左侧边缘中心
rightCenterOfStartLocation = new Point(Start.ActualWidth, Start.ActualHeight / 2); // 起始节点选择右侧边缘中心
brush = GetLineColor(Type); // 线条颜色
hitVisiblePen = new Pen(Brushes.Transparent, 1.0); // 初始化碰撞检测线
hitVisiblePen.Freeze(); // Freeze以提高性能
visualPen = new Pen(brush, 2.0); // 默认可视化Pen
visualPen.Freeze(); // Freeze以提高性能
ConfigureLineContextMenu(this); // 设置连接右键事件
linkSize = 4; // 整线条粗细
Canvas.Children.Add(this); // 添加线
Grid.SetZIndex(this, -9999999); // 置底
}
/// <summary>
/// 控件重绘事件
/// </summary>
/// <param name="drawingContext"></param>
protected override void OnRender(DrawingContext drawingContext)
{
RefreshPoint(Canvas, this.Start, this.End); // 刷新坐标
DrawBezierCurve(drawingContext, startPoint, endPoint, linkSize, brush); // 刷新线条显示位置
}
private readonly StreamGeometry streamGeometry = new StreamGeometry();
private Point rightCenterOfStartLocation; // 目标节点选择左侧边缘中心
private Point leftCenterOfEndLocation; // 起始节点选择右侧边缘中心
private Pen hitVisiblePen; // 初始化碰撞检测线
private Pen visualPen; // 默认可视化Pen
private Point startPoint; // 连接线的起始节点
private Point endPoint; // 连接线的终点
private Brush brush; // 线条颜色
double linkSize; // 根据缩放比例调整线条粗细
protected override Geometry DefiningGeometry => streamGeometry;
#region 工具方法
public void RefreshPoint(Canvas canvas, FrameworkElement startElement, FrameworkElement endElement)
{
endPoint = endElement.TranslatePoint(leftCenterOfEndLocation, canvas); // 计算终点位置
startPoint = startElement.TranslatePoint(rightCenterOfStartLocation, canvas); // 获取起始节点的中心位置
}
/// <summary>
/// 根据连接类型指定颜色
/// </summary>
/// <param name="currentConnectionType"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public static SolidColorBrush GetLineColor(ConnectionInvokeType currentConnectionType)
{
return currentConnectionType switch
{
ConnectionInvokeType.IsSucceed => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#04FC10")),
ConnectionInvokeType.IsFail => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#F18905")),
ConnectionInvokeType.IsError => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FE1343")),
ConnectionInvokeType.Upstream => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#4A82E4")),
_ => throw new Exception(),
};
}
private Point c0, c1; // 用于计算贝塞尔曲线控制点逻辑
private Vector axis = new Vector(1, 0);
private Vector startToEnd;
private void DrawBezierCurve(DrawingContext drawingContext,
Point start,
Point end,
double linkSize,
Brush brush,
bool isHitTestVisible = false,
double strokeThickness = 1.0,
bool isMouseOver = false,
double dashOffset = 0.0)
{
// 控制点的计算逻辑
double power = 8 * 8; // 控制贝塞尔曲线的“拉伸”强度
// 计算轴向向量与起点到终点的向量
//var axis = new Vector(1, 0);
startToEnd = (end.ToVector() - start.ToVector()).NormalizeTo();
// 计算拉伸程度k拉伸与水平夹角正相关
var k = 1 - Math.Pow(Math.Max(0, axis.DotProduct(startToEnd)), 10.0);
// 如果起点x大于终点x增加额外的偏移量避免重叠
var bias = start.X > end.X ? Math.Abs(start.X - end.X) * 0.25 : 0;
// 控制点的实际计算
c0 = new Point(+(power + bias) * k + start.X, start.Y);
c1 = new Point(-(power + bias) * k + end.X, end.Y);
// 准备StreamGeometry以用于绘制曲线
streamGeometry.Clear();
using (var context = streamGeometry.Open())
{
context.BeginFigure(start, true, false); // 曲线起点
context.BezierTo(c0, c1, end, true, false); // 画贝塞尔曲线
}
drawingContext.DrawGeometry(null, visualPen, streamGeometry);
// 绘制碰撞检测线
//if (true)
//{
// //hitVisiblePen = new Pen(Brushes.Transparent, linkSize + strokeThickness);
// //hitVisiblePen.Freeze();
// drawingContext.DrawGeometry(null, hitVisiblePen, streamGeometry);
//}
//else
//{
//}
}
#endregion
}
*/
}