连接线改成path绘制,方便绘制曲线

This commit is contained in:
akwkevin
2021-08-06 18:12:05 +08:00
parent e9c043ae3a
commit 8bec38e040
17 changed files with 494 additions and 124 deletions

View File

@@ -188,7 +188,7 @@ namespace Util.DiagramDesigner
Rect rectangleBounds = sourceConnector.TransformToVisual(this).TransformBounds(new Rect(sourceConnector.RenderSize));
Point point = new Point(rectangleBounds.Left + (rectangleBounds.Width / 2),
rectangleBounds.Bottom + (rectangleBounds.Height / 2));
partialConnection = new ConnectorViewModel(sourceDataItem, new PartCreatedConnectionInfo(point));
partialConnection = new ConnectorViewModel(sourceDataItem, new PartCreatedConnectionInfo(point), _service.DrawModeViewModel.VectorLineDrawMode);
_viewModel.DirectAddItemCommand.Execute(partialConnection);
}
}
@@ -209,7 +209,7 @@ namespace Util.DiagramDesigner
Rect rectangleBounds = new Rect(sourceConnectorInfo.DataItem.Left, sourceConnectorInfo.DataItem.Top, 3, 3);
Point point = new Point(rectangleBounds.Left + (rectangleBounds.Width / 2),
rectangleBounds.Bottom + (rectangleBounds.Height / 2));
partialConnection = new ConnectorViewModel(sourceConnectorInfo, new PartCreatedConnectionInfo(point));
partialConnection = new ConnectorViewModel(sourceConnectorInfo, new PartCreatedConnectionInfo(point), _service.DrawModeViewModel.VectorLineDrawMode);
_viewModel.DirectAddItemCommand.Execute(partialConnection);
}
}
@@ -341,7 +341,7 @@ namespace Util.DiagramDesigner
int indexOfLastTempConnection = sinkDataItem.DataItem.Parent.Items.Count - 1;
sinkDataItem.DataItem.Parent.DirectRemoveItemCommand.Execute(
sinkDataItem.DataItem.Parent.Items[indexOfLastTempConnection]);
sinkDataItem.DataItem.Parent.AddItemCommand.Execute(new ConnectorViewModel(sourceDataItem, sinkDataItem));
sinkDataItem.DataItem.Parent.AddItemCommand.Execute(new ConnectorViewModel(sourceDataItem, sinkDataItem, _service.DrawModeViewModel.VectorLineDrawMode));
}
else if (_service.DrawModeViewModel.GetDrawMode() == DrawMode.ConnectingLine && connectorsHit.Count() == 1)
{
@@ -352,7 +352,7 @@ namespace Util.DiagramDesigner
_viewModel.DirectRemoveItemCommand.Execute(_viewModel.Items[indexOfLastTempConnection]);
_viewModel.DirectAddItemCommand.Execute(pointItemView);
var connector = new ConnectorViewModel(sourceDataItem, sinkDataItem);
var connector = new ConnectorViewModel(sourceDataItem, sinkDataItem, _service.DrawModeViewModel.VectorLineDrawMode);
_viewModel.AddItemCommand.Execute(connector);
sourceDataItem.DataItem.ZIndex++;

View File

@@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace Util.DiagramDesigner
{
public class ConnectionDataConverter : IMultiValueConverter
{
static ConnectionDataConverter()
{
Instance = new ConnectionDataConverter();
}
public static ConnectionDataConverter Instance
{
get;
private set;
}
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
List<PointInfoBase> points = (List<PointInfoBase>)values[0];
PathGeometry pathGeometry = new PathGeometry();
PathFigure figure = new PathFigure();
figure.StartPoint = points[0];
if (values[1]?.ToString() == DrawMode.RadiusConnectingLine.ToString())
{
for (var i = 0; i < points.Count - 1; i++)
{
int current = i, last = i - 1, next = i + 1, next2 = i + 2;
if (last == -1)
{
last = 0;
}
if (next == points.Count)
{
next = points.Count - 1;
}
if (next2 == points.Count)
{
next2 = points.Count - 1;
}
var bzs = SegmentHelper.GetBezierSegment(points[current], points[last], points[next], points[next2]);
figure.Segments.Add(bzs);
}
}
else
{
for (int i = 0; i < points.Count; i++)
{
LineSegment arc = new LineSegment(points[i], true);
figure.Segments.Add(arc);
}
}
pathGeometry.Figures.Add(figure);
return pathGeometry;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

View File

@@ -9,6 +9,6 @@ namespace Util.DiagramDesigner
public interface IPathFinder
{
List<Point> GetConnectionLine(ConnectorInfo source, ConnectorInfo sink, bool showLastLine, bool sourceInnerPoint = false);
List<Point> GetConnectionLine(ConnectorInfo source, Point sinkPoint, ConnectorOrientation preferredOrientation, bool isInnerPoint = false);
List<Point> GetConnectionLine(ConnectorInfo source, Point sinkPoint, ConnectorOrientation preferredOrientation, bool showLastLine, bool isInnerPoint = false);
}
}

View File

@@ -56,6 +56,10 @@ namespace Util.DiagramDesigner
{
bool flag;
Point n = GetNearestNeighborSource(source, endPoint, rectSource, rectSink, out flag, sourceInnerPoint);
if (linePoints.Contains(n))
{
break;
}
linePoints.Add(n);
currentPoint = n;
@@ -209,7 +213,7 @@ namespace Util.DiagramDesigner
return linePoints;
}
public List<Point> GetConnectionLine(ConnectorInfo source, Point sinkPoint, ConnectorOrientation preferredOrientation, bool isInnerPoint = false)
public List<Point> GetConnectionLine(ConnectorInfo source, Point sinkPoint, ConnectorOrientation preferredOrientation, bool showLastLine, bool isInnerPoint = false)
{
List<Point> linePoints = new List<Point>();
int margin = isInnerPoint ? 0 : const_margin;
@@ -265,7 +269,7 @@ namespace Util.DiagramDesigner
else
linePoints = OptimizeLinePoints(linePoints, new Rect[] { rectSource }, source.Orientation, GetOpositeOrientation(source.Orientation));
if (!isInnerPoint)
if (!showLastLine)
{
linePoints.Insert(0, source.Position);
}

View File

@@ -0,0 +1,85 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Media;
namespace Util.DiagramDesigner
{
public class SegmentHelper
{
/// <summary>
/// 获得贝塞尔曲线
/// </summary>
/// <param name="currentPt">当前点</param>
/// <param name="lastPt">上一个点</param>
/// <param name="nextPt1">下一个点1</param>
/// <param name="nextPt2">下一个点2</param>
/// <returns></returns>
public static BezierSegment GetBezierSegment(Point currentPt, Point lastPt, Point nextPt1, Point nextPt2)
{
//计算中点
var lastC = GetCenterPoint(lastPt, currentPt);
var nextC1 = GetCenterPoint(currentPt, nextPt1); //贝塞尔控制点
var nextC2 = GetCenterPoint(nextPt1, nextPt2);
//计算相邻中点连线跟目的点的垂足
//效果并不算太好,因为可能点在两个线上或者线的延长线上,计算会有误差
//所以就直接使用中点平移方法。
//var C1 = GetFootPoint(lastC, nextC1, currentPt);
//var C2 = GetFootPoint(nextC1, nextC2, nextPt1);
//计算“相邻中点”的中点
var c1 = GetCenterPoint(lastC, nextC1);
var c2 = GetCenterPoint(nextC1, nextC2);
//计算【"中点"的中点】需要的点位移
var controlPtOffset1 = currentPt - c1;
var controlPtOffset2 = nextPt1 - c2;
//移动控制点
var controlPt1 = nextC1 + controlPtOffset1;
var controlPt2 = nextC1 + controlPtOffset2;
//如果觉得曲线幅度太大,可以将控制点向当前点靠近一定的系数。
controlPt1 = controlPt1 + 0 * (currentPt - controlPt1);
controlPt2 = controlPt2 + 0 * (nextPt1 - controlPt2);
var bzs = new BezierSegment(controlPt1, controlPt2, nextPt1, true);
return bzs;
}
/// <summary>
/// 过c点做A和B连线的垂足
/// </summary>
/// <param name="aPoint"></param>
/// <param name="bPoint"></param>
/// <param name="cPoint"></param>
/// <returns></returns>
private static Point GetFootPoint(Point aPoint, Point bPoint, Point cPoint)
{
//设三点坐标是ABCAB构成直线C是线外的点
//三点对边距离是a,b,c,垂足为D
//根据距离推导公式得AD距离是b平方-a平方+c平方/2c
//本人数学不好可能没考虑点c在线ab上的情况
var offsetADist = (Math.Pow(cPoint.X - aPoint.X, 2) + Math.Pow(cPoint.Y - aPoint.Y, 2) - Math.Pow(bPoint.X - cPoint.X, 2) - Math.Pow(bPoint.Y - cPoint.Y, 2) + Math.Pow(aPoint.X - bPoint.X, 2) + Math.Pow(aPoint.Y - bPoint.Y, 2)) / (2 * GetDistance(aPoint, bPoint));
var v = bPoint - aPoint;
var distab = GetDistance(aPoint, bPoint);
var offsetVector = v * offsetADist / distab;
return aPoint + offsetVector;
}
private static Point GetCenterPoint(Point pt1, Point pt2)
{
return new Point((pt1.X + pt2.X) / 2, (pt1.Y + pt2.Y) / 2);
}
private static double GetDistance(Point pt1, Point pt2)
{
return Math.Sqrt(Math.Pow(pt1.X - pt2.X, 2) + Math.Pow(pt1.Y - pt2.Y, 2));
}
}
}

View File

@@ -17,26 +17,27 @@ namespace Util.DiagramDesigner
{
}
public ConnectionItem(Guid id, Guid sourceId, ConnectorOrientation sourceOrientation, Type sourceType, double sourceXRatio, double sourceYRatio, bool sourceInnerPoint,
Guid sinkId, ConnectorOrientation sinkOrientation, Type sinkType, double sinkXRatio, double sinkYRatio, bool sinkInnerPoint,
int zIndex, bool isGroup, Guid parentId, ColorViewModel colorViewModel, FontViewModel fontViewModel) : base(id, zIndex, isGroup, parentId, colorViewModel, fontViewModel)
{
this.SourceId = sourceId;
this.SourceOrientation = sourceOrientation;
this.SourceType = sourceType;
this.SourceTypeName = sourceType.FullName;
this.SourceXRatio = sourceXRatio;
this.SourceYRatio = sourceYRatio;
this.SourceInnerPoint = sourceInnerPoint;
//public ConnectionItem(Guid id, Guid sourceId, ConnectorOrientation sourceOrientation, Type sourceType, double sourceXRatio, double sourceYRatio, bool sourceInnerPoint,
// Guid sinkId, ConnectorOrientation sinkOrientation, Type sinkType, double sinkXRatio, double sinkYRatio, bool sinkInnerPoint,
// int zIndex, bool isGroup, Guid parentId, DrawMode vectorLineDrawMode, ColorViewModel colorViewModel, FontViewModel fontViewModel) : base(id, zIndex, isGroup, parentId, colorViewModel, fontViewModel)
//{
// this.SourceId = sourceId;
// this.SourceOrientation = sourceOrientation;
// this.SourceType = sourceType;
// this.SourceTypeName = sourceType.FullName;
// this.SourceXRatio = sourceXRatio;
// this.SourceYRatio = sourceYRatio;
// this.SourceInnerPoint = sourceInnerPoint;
this.SinkId = sinkId;
this.SinkOrientation = sinkOrientation;
this.SinkType = sinkType;
this.SinkTypeName = sinkType.FullName;
this.SinkXRatio = sinkXRatio;
this.SinkYRatio = sinkYRatio;
this.SinkInnerPoint = sinkInnerPoint;
}
// this.SinkId = sinkId;
// this.SinkOrientation = sinkOrientation;
// this.SinkType = sinkType;
// this.SinkTypeName = sinkType.FullName;
// this.SinkXRatio = sinkXRatio;
// this.SinkYRatio = sinkYRatio;
// this.SinkInnerPoint = sinkInnerPoint;
// this.VectorLineDrawMode = vectorLineDrawMode;
//}
public ConnectionItem(Guid sourceId, ConnectorOrientation sourceOrientation, Type sourceType, double sourceXRatio, double sourceYRatio, bool sourceInnerPoint,
Guid sinkId, ConnectorOrientation sinkOrientation, Type sinkType, double sinkXRatio, double sinkYRatio, bool sinkInnerPoint, ConnectorViewModel viewmodel) : base(viewmodel)
@@ -56,6 +57,7 @@ namespace Util.DiagramDesigner
this.SinkXRatio = sinkXRatio;
this.SinkYRatio = sinkYRatio;
this.SinkInnerPoint = sinkInnerPoint;
this.VectorLineDrawMode = viewmodel.VectorLineDrawMode;
}
[XmlAttribute]
@@ -99,5 +101,8 @@ namespace Util.DiagramDesigner
[XmlAttribute]
public bool SinkInnerPoint { get; set; }
[XmlAttribute]
public DrawMode VectorLineDrawMode { get; set; }
}
}

View File

@@ -21,17 +21,17 @@ namespace Util.DiagramDesigner
FontItem = new FontItem();
}
public SelectableDesignerItemBase(Guid id, int zIndex, bool isGroup, Guid parentId, IColorViewModel colorViewModel, IFontViewModel fontViewModel)
{
this.Id = id;
this.ZIndex = zIndex;
this.IsGroup = isGroup;
this.ParentId = parentId;
//public SelectableDesignerItemBase(Guid id, int zIndex, bool isGroup, Guid parentId, IColorViewModel colorViewModel, IFontViewModel fontViewModel)
//{
// this.Id = id;
// this.ZIndex = zIndex;
// this.IsGroup = isGroup;
// this.ParentId = parentId;
ColorItem = CopyHelper.Mapper<ColorItem>(colorViewModel);
FontItem = CopyHelper.Mapper<FontItem, IFontViewModel>(fontViewModel);
}
// ColorItem = CopyHelper.Mapper<ColorItem>(colorViewModel);
// FontItem = CopyHelper.Mapper<FontItem, IFontViewModel>(fontViewModel);
//}
public SelectableDesignerItemBase(SelectableDesignerItemViewModelBase viewmodel)
{

View File

@@ -540,12 +540,21 @@
<Canvas.InputBindings>
<MouseBinding MouseAction="LeftDoubleClick" Command="{Binding EditCommand}" CommandParameter="{Binding }" />
</Canvas.InputBindings>
<Polyline x:Name="poly"
<!--<Polyline x:Name="poly"
Points="{Binding Path=ConnectionPoints, Converter={x:Static s:ConnectionPathConverter.Instance}}"
Stroke="{Binding ColorViewModel.LineColor,Converter={StaticResource ColorBrushConverter}}"
StrokeThickness="{Binding ColorViewModel.LineWidth}"
StrokeDashArray="{Binding ColorViewModel.LineDashStyle,Converter={StaticResource LineDashConverter}}" />
StrokeDashArray="{Binding ColorViewModel.LineDashStyle,Converter={StaticResource LineDashConverter}}" />-->
<Path x:Name="poly" Stroke="{Binding ColorViewModel.LineColor,Converter={StaticResource ColorBrushConverter}}"
StrokeThickness="{Binding ColorViewModel.LineWidth}"
StrokeDashArray="{Binding ColorViewModel.LineDashStyle,Converter={StaticResource LineDashConverter}}">
<Path.Data>
<MultiBinding Converter="{x:Static s:ConnectionDataConverter.Instance}">
<Binding Path="ConnectionPoints"/>
<Binding Path="VectorLineDrawMode"/>
</MultiBinding>
</Path.Data>
</Path>
<Path x:Name="rightarrow"
Data="{Binding ColorViewModel.RightArrowPathStyle,Converter={StaticResource ArrowPathConverter}}"
Visibility="{Binding Path=IsFullConnection, Converter={x:Static s:BoolToVisibilityConverter.Instance}}"

View File

@@ -105,7 +105,7 @@ namespace Util.DiagramDesigner
}
}
private DrawMode _vectorLineDrawMode = DrawMode.RadiusConnectingLine;
private DrawMode _vectorLineDrawMode = DrawMode.CornerConnectingLine;
public DrawMode VectorLineDrawMode
{
get

View File

@@ -10,16 +10,16 @@ namespace Util.DiagramDesigner
{
public class ConnectorViewModel : SelectableDesignerItemViewModelBase
{
private IDiagramServiceProvider _service { get { return DiagramServicesProvider.Instance.Provider; } }
public ConnectorViewModel(IDiagramViewModel parent, FullyCreatedConnectorInfo sourceConnectorInfo, FullyCreatedConnectorInfo sinkConnectorInfo,
SelectableDesignerItemBase designer) : base(parent, designer)
SelectableDesignerItemBase designer, DrawMode vectorLineDrawMode) : base(parent, designer)
{
VectorLineDrawMode = vectorLineDrawMode;
Init(sourceConnectorInfo, sinkConnectorInfo);
}
public ConnectorViewModel(FullyCreatedConnectorInfo sourceConnectorInfo, ConnectorInfoBase sinkConnectorInfo)
public ConnectorViewModel(FullyCreatedConnectorInfo sourceConnectorInfo, ConnectorInfoBase sinkConnectorInfo, DrawMode vectorLineDrawMode)
{
VectorLineDrawMode = vectorLineDrawMode;
Init(sourceConnectorInfo, sinkConnectorInfo);
}
@@ -120,13 +120,15 @@ namespace Util.DiagramDesigner
private set
{
if (SetProperty(ref _area, value))
{
{
UpdateConnectionPoints();
OutTextItemLocation(_area, value);
}
}
}
public DrawMode VectorLineDrawMode { get; set; }
public ConnectorInfo ConnectorInfo(ConnectorOrientation orientation, double left, double top, double width, double height, Point position)
{
@@ -189,11 +191,11 @@ namespace Util.DiagramDesigner
private void UpdateConnectionPoints()
{
if (_service.DrawModeViewModel.VectorLineDrawMode == DrawMode.ConnectingLine)
if (VectorLineDrawMode == DrawMode.ConnectingLine)
{
UpdateConnectionPointsByLine();
}
else if (_service.DrawModeViewModel.VectorLineDrawMode == DrawMode.BoundaryConnectingLine)
else if (VectorLineDrawMode == DrawMode.BoundaryConnectingLine)
{
UpdateConnectionPointsByBoundary();
}
@@ -247,7 +249,7 @@ namespace Util.DiagramDesigner
}
else
{
ConnectionPoints = PointInfoBase.ToList(PathFinder.GetConnectionLine(sourceInfo, points[1], SourceConnectorInfo.Orientation, SourceConnectorInfo.IsInnerPoint));
ConnectionPoints = PointInfoBase.ToList(PathFinder.GetConnectionLine(sourceInfo, points[1], SourceConnectorInfo.Orientation, false, SourceConnectorInfo.IsInnerPoint));
EndPoint = new Point();
}
}

View File

@@ -117,7 +117,7 @@ namespace Util.DiagramDesigner
}
}
private Size _gridCellSize = new Size(50, 50);
private Size _gridCellSize = new Size(100, 100);
public Size GridCellSize
{
get
@@ -130,6 +130,32 @@ namespace Util.DiagramDesigner
}
}
public double GridCellWidth
{
get
{
return _gridCellSize.Width;
}
set
{
_gridCellSize.Width = value;
RaisePropertyChanged(nameof(GridCellSize));
}
}
public double GridCellHeight
{
get
{
return _gridCellSize.Height;
}
set
{
_gridCellSize.Height = value;
RaisePropertyChanged(nameof(GridCellSize));
}
}
private Color _pageBackground = Colors.White;
public Color PageBackground
{