2021-07-23 09:42:22 +08:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections.Generic;
|
2023-01-14 21:52:05 +08:00
|
|
|
|
using System.Text;
|
2023-01-12 23:02:53 +08:00
|
|
|
|
using AIStudio.Wpf.DiagramDesigner.Geometrys;
|
2021-07-23 09:42:22 +08:00
|
|
|
|
|
2022-10-28 22:45:39 +08:00
|
|
|
|
namespace AIStudio.Wpf.DiagramDesigner
|
2021-07-23 09:42:22 +08:00
|
|
|
|
{
|
2023-01-14 21:52:05 +08:00
|
|
|
|
public static partial class PathGenerators
|
2021-07-23 09:42:22 +08:00
|
|
|
|
{
|
2023-01-14 21:52:05 +08:00
|
|
|
|
public static PathGeneratorResult Corner(IDiagramViewModel _, ConnectorViewModel link, PointBase[] route, PointBase source, PointBase target)
|
2022-11-30 22:28:22 +08:00
|
|
|
|
{
|
2023-01-14 21:52:05 +08:00
|
|
|
|
route = ConcatRouteAndSourceAndTarget(route, source, target);
|
2022-11-30 22:28:22 +08:00
|
|
|
|
|
2023-01-14 21:52:05 +08:00
|
|
|
|
if (route.Length > 2)
|
|
|
|
|
|
return CurveThroughPoints(route, link);
|
2022-11-30 22:28:22 +08:00
|
|
|
|
|
2023-01-14 21:52:05 +08:00
|
|
|
|
if (link.IsFullConnection)
|
|
|
|
|
|
route = GetRouteWithFullConnectionLine(_, link, route);
|
2022-11-30 22:28:22 +08:00
|
|
|
|
else
|
2023-01-14 21:52:05 +08:00
|
|
|
|
route = GetRouteWithPartConnectionLine(_, link, route);
|
|
|
|
|
|
|
2023-01-15 11:59:51 +08:00
|
|
|
|
double sourceAngle = SourceMarkerAdjustement(route, link.ColorViewModel.LeftArrowPathStyle == ArrowPathStyle.None ? 0d : (double)link.ColorViewModel.LeftArrowSizeStyle);
|
|
|
|
|
|
double targetAngle = TargetMarkerAdjustement(route, link.ColorViewModel.RightArrowPathStyle == ArrowPathStyle.None ? 0d : (double)link.ColorViewModel.RightArrowPathStyle);
|
2023-01-14 21:52:05 +08:00
|
|
|
|
|
|
|
|
|
|
DoShift(route, link);
|
|
|
|
|
|
|
|
|
|
|
|
var paths = new string[route.Length - 1];
|
|
|
|
|
|
for (var i = 0; i < route.Length - 1; i++)
|
2022-11-30 22:28:22 +08:00
|
|
|
|
{
|
2023-01-14 21:52:05 +08:00
|
|
|
|
paths[i] = FormattableString.Invariant($"M {route[i].X} {route[i].Y} L {route[i + 1].X} {route[i + 1].Y}");
|
2022-11-30 22:28:22 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-14 21:52:05 +08:00
|
|
|
|
return new PathGeneratorResult(paths, sourceAngle, route[0], targetAngle, route[route.Length - 1]);
|
2022-11-30 22:28:22 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-14 21:52:05 +08:00
|
|
|
|
private const int const_margin = 20;
|
2022-11-30 22:28:22 +08:00
|
|
|
|
|
2023-01-14 21:52:05 +08:00
|
|
|
|
private static PointBase[] GetRouteWithFullConnectionLine(IDiagramViewModel _, ConnectorViewModel link, PointBase[] route)
|
2021-07-23 09:42:22 +08:00
|
|
|
|
{
|
2023-01-14 21:52:05 +08:00
|
|
|
|
var sourceInnerPoint = link.SourceConnectorInfo.IsInnerPoint;
|
2023-01-15 20:27:39 +08:00
|
|
|
|
PointBase sourcePoint = link.SourceConnectorInfo.MiddlePosition;
|
|
|
|
|
|
PointBase sinkPoint = link.SinkConnectorInfo.MiddlePosition;
|
2023-01-14 21:52:05 +08:00
|
|
|
|
ConnectorOrientation sourceOrientation = link.SourceConnectorInfo.Orientation;
|
|
|
|
|
|
ConnectorOrientation sinkOrientation = link.SinkConnectorInfoFully.Orientation;
|
|
|
|
|
|
|
2023-01-08 09:22:37 +08:00
|
|
|
|
List<PointBase> linePoints = new List<PointBase>();
|
2021-07-23 09:42:22 +08:00
|
|
|
|
int margin1 = sourceInnerPoint ? 0 : const_margin;
|
|
|
|
|
|
int margin2 = const_margin;
|
|
|
|
|
|
|
2023-01-14 21:52:05 +08:00
|
|
|
|
RectangleBase rectSource = GetRectWithMargin(sourcePoint, margin1);
|
|
|
|
|
|
RectangleBase rectSink = GetRectWithMargin(sinkPoint, margin2);
|
2021-07-23 09:42:22 +08:00
|
|
|
|
|
2023-01-14 21:52:05 +08:00
|
|
|
|
PointBase startPoint = GetOffsetPoint(sourcePoint, sourceOrientation, rectSource, sourceInnerPoint);
|
|
|
|
|
|
PointBase endPoint = GetOffsetPoint(sinkPoint, sinkOrientation, rectSink);
|
2021-07-23 09:42:22 +08:00
|
|
|
|
|
|
|
|
|
|
linePoints.Add(startPoint);
|
2023-01-08 09:22:37 +08:00
|
|
|
|
PointBase currentPoint = startPoint;
|
2021-07-23 09:42:22 +08:00
|
|
|
|
|
|
|
|
|
|
if (!rectSink.Contains(currentPoint) && !rectSource.Contains(endPoint))
|
|
|
|
|
|
{
|
|
|
|
|
|
while (true)
|
|
|
|
|
|
{
|
|
|
|
|
|
#region source node
|
|
|
|
|
|
|
2023-01-08 09:22:37 +08:00
|
|
|
|
if (IsPointVisible(currentPoint, endPoint, new RectangleBase[] { rectSource, rectSink }))
|
2021-07-23 09:42:22 +08:00
|
|
|
|
{
|
|
|
|
|
|
linePoints.Add(endPoint);
|
|
|
|
|
|
currentPoint = endPoint;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-14 21:52:05 +08:00
|
|
|
|
PointBase neighbour = GetNearestVisibleNeighborSink(currentPoint, endPoint, sinkOrientation, rectSource, rectSink);
|
2021-07-23 09:42:22 +08:00
|
|
|
|
if (!double.IsNaN(neighbour.X))
|
|
|
|
|
|
{
|
|
|
|
|
|
linePoints.Add(neighbour);
|
|
|
|
|
|
linePoints.Add(endPoint);
|
|
|
|
|
|
currentPoint = endPoint;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (currentPoint == startPoint)
|
|
|
|
|
|
{
|
|
|
|
|
|
bool flag;
|
2023-01-14 21:52:05 +08:00
|
|
|
|
PointBase n = GetNearestNeighborSource(sourceOrientation, endPoint, rectSource, rectSink, out flag, sourceInnerPoint);
|
2021-08-06 18:12:05 +08:00
|
|
|
|
if (linePoints.Contains(n))
|
|
|
|
|
|
{
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
2021-07-23 09:42:22 +08:00
|
|
|
|
linePoints.Add(n);
|
|
|
|
|
|
currentPoint = n;
|
|
|
|
|
|
|
2023-01-08 09:22:37 +08:00
|
|
|
|
if (!IsRectVisible(currentPoint, rectSink, new RectangleBase[] { rectSource }))
|
2021-07-23 09:42:22 +08:00
|
|
|
|
{
|
2023-01-08 09:22:37 +08:00
|
|
|
|
PointBase n1, n2;
|
2023-01-14 21:52:05 +08:00
|
|
|
|
GetOppositeCorners(sourceOrientation, rectSource, out n1, out n2, sourceInnerPoint);
|
2021-07-23 09:42:22 +08:00
|
|
|
|
if (flag)
|
|
|
|
|
|
{
|
|
|
|
|
|
linePoints.Add(n1);
|
|
|
|
|
|
currentPoint = n1;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
linePoints.Add(n2);
|
|
|
|
|
|
currentPoint = n2;
|
|
|
|
|
|
}
|
2023-01-08 09:22:37 +08:00
|
|
|
|
if (!IsRectVisible(currentPoint, rectSink, new RectangleBase[] { rectSource }))
|
2021-07-23 09:42:22 +08:00
|
|
|
|
{
|
|
|
|
|
|
if (flag)
|
|
|
|
|
|
{
|
|
|
|
|
|
linePoints.Add(n2);
|
|
|
|
|
|
currentPoint = n2;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
linePoints.Add(n1);
|
|
|
|
|
|
currentPoint = n1;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
#region sink node
|
|
|
|
|
|
|
|
|
|
|
|
else // from here on we jump to the sink node
|
|
|
|
|
|
{
|
2023-01-08 09:22:37 +08:00
|
|
|
|
PointBase n1, n2; // neighbour corner
|
|
|
|
|
|
PointBase s1, s2; // opposite corner
|
2023-01-14 21:52:05 +08:00
|
|
|
|
GetNeighborCorners(sinkOrientation, rectSink, out s1, out s2);
|
|
|
|
|
|
GetOppositeCorners(sinkOrientation, rectSink, out n1, out n2);
|
2021-07-23 09:42:22 +08:00
|
|
|
|
|
2023-01-08 09:22:37 +08:00
|
|
|
|
bool n1Visible = IsPointVisible(currentPoint, n1, new RectangleBase[] { rectSource, rectSink });
|
|
|
|
|
|
bool n2Visible = IsPointVisible(currentPoint, n2, new RectangleBase[] { rectSource, rectSink });
|
2021-07-23 09:42:22 +08:00
|
|
|
|
|
|
|
|
|
|
if (n1Visible && n2Visible)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (rectSource.Contains(n1))
|
|
|
|
|
|
{
|
|
|
|
|
|
linePoints.Add(n2);
|
|
|
|
|
|
if (rectSource.Contains(s2))
|
|
|
|
|
|
{
|
|
|
|
|
|
linePoints.Add(n1);
|
|
|
|
|
|
linePoints.Add(s1);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
linePoints.Add(s2);
|
|
|
|
|
|
|
|
|
|
|
|
linePoints.Add(endPoint);
|
|
|
|
|
|
currentPoint = endPoint;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (rectSource.Contains(n2))
|
|
|
|
|
|
{
|
|
|
|
|
|
linePoints.Add(n1);
|
|
|
|
|
|
if (rectSource.Contains(s1))
|
|
|
|
|
|
{
|
|
|
|
|
|
linePoints.Add(n2);
|
|
|
|
|
|
linePoints.Add(s2);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
linePoints.Add(s1);
|
|
|
|
|
|
|
|
|
|
|
|
linePoints.Add(endPoint);
|
|
|
|
|
|
currentPoint = endPoint;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ((Distance(n1, endPoint) <= Distance(n2, endPoint)))
|
|
|
|
|
|
{
|
|
|
|
|
|
linePoints.Add(n1);
|
|
|
|
|
|
if (rectSource.Contains(s1))
|
|
|
|
|
|
{
|
|
|
|
|
|
linePoints.Add(n2);
|
|
|
|
|
|
linePoints.Add(s2);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
linePoints.Add(s1);
|
|
|
|
|
|
linePoints.Add(endPoint);
|
|
|
|
|
|
currentPoint = endPoint;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
linePoints.Add(n2);
|
|
|
|
|
|
if (rectSource.Contains(s2))
|
|
|
|
|
|
{
|
|
|
|
|
|
linePoints.Add(n1);
|
|
|
|
|
|
linePoints.Add(s1);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
linePoints.Add(s2);
|
|
|
|
|
|
linePoints.Add(endPoint);
|
|
|
|
|
|
currentPoint = endPoint;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (n1Visible)
|
|
|
|
|
|
{
|
|
|
|
|
|
linePoints.Add(n1);
|
|
|
|
|
|
if (rectSource.Contains(s1))
|
|
|
|
|
|
{
|
|
|
|
|
|
linePoints.Add(n2);
|
|
|
|
|
|
linePoints.Add(s2);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
linePoints.Add(s1);
|
|
|
|
|
|
linePoints.Add(endPoint);
|
|
|
|
|
|
currentPoint = endPoint;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
linePoints.Add(n2);
|
|
|
|
|
|
if (rectSource.Contains(s2))
|
|
|
|
|
|
{
|
|
|
|
|
|
linePoints.Add(n1);
|
|
|
|
|
|
linePoints.Add(s1);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
linePoints.Add(s2);
|
|
|
|
|
|
linePoints.Add(endPoint);
|
|
|
|
|
|
currentPoint = endPoint;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
linePoints.Add(endPoint);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-14 21:52:05 +08:00
|
|
|
|
linePoints = OptimizeLinePoints(linePoints, new RectangleBase[] { rectSource, rectSink }, sourceOrientation, sinkOrientation);
|
2021-07-23 09:42:22 +08:00
|
|
|
|
|
2023-01-14 21:52:05 +08:00
|
|
|
|
linePoints.Insert(0, sourcePoint);
|
|
|
|
|
|
linePoints.Add(sinkPoint);
|
2021-07-23 09:42:22 +08:00
|
|
|
|
|
2023-01-14 21:52:05 +08:00
|
|
|
|
return linePoints.ToArray();
|
2021-07-23 09:42:22 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-14 21:52:05 +08:00
|
|
|
|
private static PointBase[] GetRouteWithPartConnectionLine(IDiagramViewModel diagramViewModel, ConnectorViewModel link, PointBase[] route)
|
2021-07-23 09:42:22 +08:00
|
|
|
|
{
|
2023-01-14 21:52:05 +08:00
|
|
|
|
var sourceInnerPoint = link.SourceConnectorInfo.IsInnerPoint;
|
2023-01-15 20:27:39 +08:00
|
|
|
|
PointBase sourcePoint = link.SourceConnectorInfo.MiddlePosition;
|
|
|
|
|
|
PointBase sinkPoint = link.SinkConnectorInfo.MiddlePosition;
|
2023-01-14 21:52:05 +08:00
|
|
|
|
ConnectorOrientation sourceOrientation = link.SourceConnectorInfo.Orientation;
|
|
|
|
|
|
ConnectorOrientation preferredOrientation = link.SourceConnectorInfo.Orientation;
|
|
|
|
|
|
|
2023-01-08 09:22:37 +08:00
|
|
|
|
List<PointBase> linePoints = new List<PointBase>();
|
2023-01-14 21:52:05 +08:00
|
|
|
|
int margin = sourceInnerPoint ? 0 : const_margin;
|
2022-11-30 22:28:22 +08:00
|
|
|
|
|
2023-01-14 21:52:05 +08:00
|
|
|
|
RectangleBase rectSource = GetRectWithMargin(sourcePoint, margin);
|
|
|
|
|
|
PointBase startPoint = GetOffsetPoint(sourcePoint, sourceOrientation, rectSource, sourceInnerPoint);
|
2023-01-08 09:22:37 +08:00
|
|
|
|
PointBase endPoint = sinkPoint;
|
2021-07-23 09:42:22 +08:00
|
|
|
|
|
|
|
|
|
|
linePoints.Add(startPoint);
|
2023-01-08 09:22:37 +08:00
|
|
|
|
PointBase currentPoint = startPoint;
|
2021-07-23 09:42:22 +08:00
|
|
|
|
|
|
|
|
|
|
if (!rectSource.Contains(endPoint))
|
|
|
|
|
|
{
|
|
|
|
|
|
while (true)
|
|
|
|
|
|
{
|
2023-01-08 09:22:37 +08:00
|
|
|
|
if (IsPointVisible(currentPoint, endPoint, new RectangleBase[] { rectSource }))
|
2021-07-23 09:42:22 +08:00
|
|
|
|
{
|
|
|
|
|
|
linePoints.Add(endPoint);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool sideFlag;
|
2023-01-14 21:52:05 +08:00
|
|
|
|
PointBase n = GetNearestNeighborSource(sourceOrientation, endPoint, rectSource, out sideFlag, sourceInnerPoint);
|
2021-07-23 09:42:22 +08:00
|
|
|
|
linePoints.Add(n);
|
|
|
|
|
|
currentPoint = n;
|
|
|
|
|
|
|
2023-01-08 09:22:37 +08:00
|
|
|
|
if (IsPointVisible(currentPoint, endPoint, new RectangleBase[] { rectSource }))
|
2021-07-23 09:42:22 +08:00
|
|
|
|
{
|
|
|
|
|
|
linePoints.Add(endPoint);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2023-01-08 09:22:37 +08:00
|
|
|
|
PointBase n1, n2;
|
2023-01-14 21:52:05 +08:00
|
|
|
|
GetOppositeCorners(sourceOrientation, rectSource, out n1, out n2, sourceInnerPoint);
|
2021-07-23 09:42:22 +08:00
|
|
|
|
if (sideFlag)
|
|
|
|
|
|
linePoints.Add(n1);
|
|
|
|
|
|
else
|
|
|
|
|
|
linePoints.Add(n2);
|
|
|
|
|
|
|
|
|
|
|
|
linePoints.Add(endPoint);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
linePoints.Add(endPoint);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (preferredOrientation != ConnectorOrientation.None)
|
2023-01-14 21:52:05 +08:00
|
|
|
|
linePoints = OptimizeLinePoints(linePoints, new RectangleBase[] { rectSource }, sourceOrientation, preferredOrientation);
|
2021-07-23 09:42:22 +08:00
|
|
|
|
else
|
2023-01-14 21:52:05 +08:00
|
|
|
|
linePoints = OptimizeLinePoints(linePoints, new RectangleBase[] { rectSource }, sourceOrientation, GetOpositeOrientation(sourceOrientation));
|
|
|
|
|
|
|
|
|
|
|
|
linePoints.Insert(0, sourcePoint);
|
2021-07-23 09:42:22 +08:00
|
|
|
|
|
2023-01-14 21:52:05 +08:00
|
|
|
|
return linePoints.ToArray();
|
2021-07-23 09:42:22 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-08 09:22:37 +08:00
|
|
|
|
private static List<PointBase> OptimizeLinePoints(List<PointBase> linePoints, RectangleBase[] rectangles, ConnectorOrientation sourceOrientation, ConnectorOrientation sinkOrientation)
|
2021-07-23 09:42:22 +08:00
|
|
|
|
{
|
2023-01-08 09:22:37 +08:00
|
|
|
|
List<PointBase> points = new List<PointBase>();
|
2021-07-23 09:42:22 +08:00
|
|
|
|
int cut = 0;
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < linePoints.Count; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (i >= cut)
|
|
|
|
|
|
{
|
|
|
|
|
|
for (int k = linePoints.Count - 1; k > i; k--)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (IsPointVisible(linePoints[i], linePoints[k], rectangles))
|
|
|
|
|
|
{
|
|
|
|
|
|
cut = k;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
points.Add(linePoints[i]);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#region Line
|
|
|
|
|
|
for (int j = 0; j < points.Count - 1; j++)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (points[j].X != points[j + 1].X && points[j].Y != points[j + 1].Y)
|
|
|
|
|
|
{
|
|
|
|
|
|
ConnectorOrientation orientationFrom;
|
|
|
|
|
|
ConnectorOrientation orientationTo;
|
|
|
|
|
|
|
|
|
|
|
|
// orientation from point
|
|
|
|
|
|
if (j == 0)
|
|
|
|
|
|
orientationFrom = sourceOrientation;
|
|
|
|
|
|
else
|
|
|
|
|
|
orientationFrom = GetOrientation(points[j], points[j - 1]);
|
|
|
|
|
|
|
|
|
|
|
|
// orientation to pint
|
|
|
|
|
|
if (j == points.Count - 2)
|
|
|
|
|
|
orientationTo = sinkOrientation;
|
|
|
|
|
|
else
|
|
|
|
|
|
orientationTo = GetOrientation(points[j + 1], points[j + 2]);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if ((orientationFrom == ConnectorOrientation.Left || orientationFrom == ConnectorOrientation.Right) &&
|
|
|
|
|
|
(orientationTo == ConnectorOrientation.Left || orientationTo == ConnectorOrientation.Right))
|
|
|
|
|
|
{
|
|
|
|
|
|
double centerX = Math.Min(points[j].X, points[j + 1].X) + Math.Abs(points[j].X - points[j + 1].X) / 2;
|
2023-01-08 09:22:37 +08:00
|
|
|
|
points.Insert(j + 1, new PointBase(centerX, points[j].Y));
|
|
|
|
|
|
points.Insert(j + 2, new PointBase(centerX, points[j + 2].Y));
|
2021-07-23 09:42:22 +08:00
|
|
|
|
if (points.Count - 1 > j + 3)
|
|
|
|
|
|
points.RemoveAt(j + 3);
|
|
|
|
|
|
return points;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ((orientationFrom == ConnectorOrientation.Top || orientationFrom == ConnectorOrientation.Bottom) &&
|
|
|
|
|
|
(orientationTo == ConnectorOrientation.Top || orientationTo == ConnectorOrientation.Bottom))
|
|
|
|
|
|
{
|
|
|
|
|
|
double centerY = Math.Min(points[j].Y, points[j + 1].Y) + Math.Abs(points[j].Y - points[j + 1].Y) / 2;
|
2023-01-08 09:22:37 +08:00
|
|
|
|
points.Insert(j + 1, new PointBase(points[j].X, centerY));
|
|
|
|
|
|
points.Insert(j + 2, new PointBase(points[j + 2].X, centerY));
|
2021-07-23 09:42:22 +08:00
|
|
|
|
if (points.Count - 1 > j + 3)
|
|
|
|
|
|
points.RemoveAt(j + 3);
|
|
|
|
|
|
return points;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ((orientationFrom == ConnectorOrientation.Left || orientationFrom == ConnectorOrientation.Right) &&
|
|
|
|
|
|
(orientationTo == ConnectorOrientation.Top || orientationTo == ConnectorOrientation.Bottom))
|
|
|
|
|
|
{
|
2023-01-08 09:22:37 +08:00
|
|
|
|
points.Insert(j + 1, new PointBase(points[j + 1].X, points[j].Y));
|
2021-07-23 09:42:22 +08:00
|
|
|
|
return points;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ((orientationFrom == ConnectorOrientation.Top || orientationFrom == ConnectorOrientation.Bottom) &&
|
|
|
|
|
|
(orientationTo == ConnectorOrientation.Left || orientationTo == ConnectorOrientation.Right))
|
|
|
|
|
|
{
|
2023-01-08 09:22:37 +08:00
|
|
|
|
points.Insert(j + 1, new PointBase(points[j].X, points[j + 1].Y));
|
2021-07-23 09:42:22 +08:00
|
|
|
|
return points;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
return points;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-08 09:22:37 +08:00
|
|
|
|
private static ConnectorOrientation GetOrientation(PointBase p1, PointBase p2)
|
2021-07-23 09:42:22 +08:00
|
|
|
|
{
|
|
|
|
|
|
if (p1.X == p2.X)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (p1.Y >= p2.Y)
|
|
|
|
|
|
return ConnectorOrientation.Bottom;
|
|
|
|
|
|
else
|
|
|
|
|
|
return ConnectorOrientation.Top;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (p1.Y == p2.Y)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (p1.X >= p2.X)
|
|
|
|
|
|
return ConnectorOrientation.Right;
|
|
|
|
|
|
else
|
|
|
|
|
|
return ConnectorOrientation.Left;
|
|
|
|
|
|
}
|
|
|
|
|
|
throw new Exception("Failed to retrieve orientation");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-14 21:52:05 +08:00
|
|
|
|
private static PointBase GetNearestNeighborSource(ConnectorOrientation orientation, PointBase endPoint, RectangleBase rectSource, RectangleBase rectSink, out bool flag, bool isInnerPoint)
|
2021-07-23 09:42:22 +08:00
|
|
|
|
{
|
2023-01-08 09:22:37 +08:00
|
|
|
|
PointBase n1, n2; // neighbors
|
2023-01-14 21:52:05 +08:00
|
|
|
|
GetNeighborCorners(orientation, rectSource, out n1, out n2, isInnerPoint);
|
2021-07-23 09:42:22 +08:00
|
|
|
|
|
|
|
|
|
|
if (rectSink.Contains(n1))
|
|
|
|
|
|
{
|
|
|
|
|
|
flag = false;
|
|
|
|
|
|
return n2;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (rectSink.Contains(n2))
|
|
|
|
|
|
{
|
|
|
|
|
|
flag = true;
|
|
|
|
|
|
return n1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ((Distance(n1, endPoint) <= Distance(n2, endPoint)))
|
|
|
|
|
|
{
|
|
|
|
|
|
flag = true;
|
|
|
|
|
|
return n1;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
flag = false;
|
|
|
|
|
|
return n2;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-14 21:52:05 +08:00
|
|
|
|
private static PointBase GetNearestNeighborSource(ConnectorOrientation orientation, PointBase endPoint, RectangleBase rectSource, out bool flag, bool isInnerPoint)
|
2021-07-23 09:42:22 +08:00
|
|
|
|
{
|
2023-01-08 09:22:37 +08:00
|
|
|
|
PointBase n1, n2; // neighbors
|
2023-01-14 21:52:05 +08:00
|
|
|
|
GetNeighborCorners(orientation, rectSource, out n1, out n2, isInnerPoint);
|
2021-07-23 09:42:22 +08:00
|
|
|
|
|
|
|
|
|
|
if ((Distance(n1, endPoint) <= Distance(n2, endPoint)))
|
|
|
|
|
|
{
|
|
|
|
|
|
flag = true;
|
|
|
|
|
|
return n1;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
flag = false;
|
|
|
|
|
|
return n2;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-14 21:52:05 +08:00
|
|
|
|
private static PointBase GetNearestVisibleNeighborSink(PointBase currentPoint, PointBase endPoint, ConnectorOrientation orientation, RectangleBase rectSource, RectangleBase rectSink)
|
2021-07-23 09:42:22 +08:00
|
|
|
|
{
|
2023-01-08 09:22:37 +08:00
|
|
|
|
PointBase s1, s2; // neighbors on sink side
|
2023-01-14 21:52:05 +08:00
|
|
|
|
GetNeighborCorners(orientation, rectSink, out s1, out s2);
|
2021-07-23 09:42:22 +08:00
|
|
|
|
|
2023-01-08 09:22:37 +08:00
|
|
|
|
bool flag1 = IsPointVisible(currentPoint, s1, new RectangleBase[] { rectSource, rectSink });
|
|
|
|
|
|
bool flag2 = IsPointVisible(currentPoint, s2, new RectangleBase[] { rectSource, rectSink });
|
2021-07-23 09:42:22 +08:00
|
|
|
|
|
|
|
|
|
|
if (flag1) // s1 visible
|
|
|
|
|
|
{
|
|
|
|
|
|
if (flag2) // s1 and s2 visible
|
|
|
|
|
|
{
|
|
|
|
|
|
if (rectSink.Contains(s1))
|
|
|
|
|
|
return s2;
|
|
|
|
|
|
|
|
|
|
|
|
if (rectSink.Contains(s2))
|
|
|
|
|
|
return s1;
|
|
|
|
|
|
|
|
|
|
|
|
if ((Distance(s1, endPoint) <= Distance(s2, endPoint)))
|
|
|
|
|
|
return s1;
|
|
|
|
|
|
else
|
|
|
|
|
|
return s2;
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
return s1;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else // s1 not visible
|
|
|
|
|
|
{
|
|
|
|
|
|
if (flag2) // only s2 visible
|
|
|
|
|
|
{
|
|
|
|
|
|
return s2;
|
|
|
|
|
|
}
|
|
|
|
|
|
else // s1 and s2 not visible
|
|
|
|
|
|
{
|
2023-01-08 09:22:37 +08:00
|
|
|
|
return new PointBase(double.NaN, double.NaN);
|
2021-07-23 09:42:22 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-08 09:22:37 +08:00
|
|
|
|
private static bool IsPointVisible(PointBase fromPoint, PointBase targetPoint, RectangleBase[] rectangles)
|
2021-07-23 09:42:22 +08:00
|
|
|
|
{
|
2023-01-08 09:22:37 +08:00
|
|
|
|
foreach (RectangleBase rect in rectangles)
|
2021-07-23 09:42:22 +08:00
|
|
|
|
{
|
|
|
|
|
|
if (RectangleIntersectsLine(rect, fromPoint, targetPoint))
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-08 09:22:37 +08:00
|
|
|
|
private static bool IsRectVisible(PointBase fromPoint, RectangleBase targetRect, RectangleBase[] rectangles)
|
2021-07-23 09:42:22 +08:00
|
|
|
|
{
|
|
|
|
|
|
if (IsPointVisible(fromPoint, targetRect.TopLeft, rectangles))
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
|
|
if (IsPointVisible(fromPoint, targetRect.TopRight, rectangles))
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
|
|
if (IsPointVisible(fromPoint, targetRect.BottomLeft, rectangles))
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
|
|
if (IsPointVisible(fromPoint, targetRect.BottomRight, rectangles))
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-08 09:22:37 +08:00
|
|
|
|
private static bool RectangleIntersectsLine(RectangleBase rect, PointBase startPoint, PointBase endPoint)
|
2021-07-23 09:42:22 +08:00
|
|
|
|
{
|
|
|
|
|
|
rect.Inflate(-1, -1);
|
2023-01-08 09:22:37 +08:00
|
|
|
|
return rect.IntersectsWith(new RectangleBase(startPoint, endPoint));
|
2021-07-23 09:42:22 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-08 09:22:37 +08:00
|
|
|
|
private static void GetOppositeCorners(ConnectorOrientation orientation, RectangleBase rect, out PointBase n1, out PointBase n2, bool isInnerPoint = false)
|
2021-07-23 09:42:22 +08:00
|
|
|
|
{
|
|
|
|
|
|
if (isInnerPoint)
|
|
|
|
|
|
{
|
|
|
|
|
|
n1 = rect.Location; n2 = rect.Location;
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
switch (orientation)
|
|
|
|
|
|
{
|
|
|
|
|
|
case ConnectorOrientation.Left:
|
|
|
|
|
|
n1 = rect.TopRight; n2 = rect.BottomRight;
|
|
|
|
|
|
break;
|
|
|
|
|
|
case ConnectorOrientation.Top:
|
|
|
|
|
|
n1 = rect.BottomLeft; n2 = rect.BottomRight;
|
|
|
|
|
|
break;
|
|
|
|
|
|
case ConnectorOrientation.Right:
|
|
|
|
|
|
n1 = rect.TopLeft; n2 = rect.BottomLeft;
|
|
|
|
|
|
break;
|
|
|
|
|
|
case ConnectorOrientation.Bottom:
|
|
|
|
|
|
n1 = rect.TopLeft; n2 = rect.TopRight;
|
|
|
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
|
|
|
throw new Exception("No opposite corners found!");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-08 09:22:37 +08:00
|
|
|
|
private static void GetNeighborCorners(ConnectorOrientation orientation, RectangleBase rect, out PointBase n1, out PointBase n2, bool isInnerPoint = false)
|
2021-07-23 09:42:22 +08:00
|
|
|
|
{
|
|
|
|
|
|
if (isInnerPoint)
|
|
|
|
|
|
{
|
|
|
|
|
|
n1 = rect.Location; n2 = rect.Location;
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
switch (orientation)
|
|
|
|
|
|
{
|
|
|
|
|
|
case ConnectorOrientation.Left:
|
|
|
|
|
|
n1 = rect.TopLeft; n2 = rect.BottomLeft;
|
|
|
|
|
|
break;
|
|
|
|
|
|
case ConnectorOrientation.Top:
|
|
|
|
|
|
n1 = rect.TopLeft; n2 = rect.TopRight;
|
|
|
|
|
|
break;
|
|
|
|
|
|
case ConnectorOrientation.Right:
|
|
|
|
|
|
n1 = rect.TopRight; n2 = rect.BottomRight;
|
|
|
|
|
|
break;
|
|
|
|
|
|
case ConnectorOrientation.Bottom:
|
|
|
|
|
|
n1 = rect.BottomLeft; n2 = rect.BottomRight;
|
|
|
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
|
|
|
throw new Exception("No neighour corners found!");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-08 09:22:37 +08:00
|
|
|
|
private static double Distance(PointBase p1, PointBase p2)
|
2021-07-23 09:42:22 +08:00
|
|
|
|
{
|
2023-01-08 09:22:37 +08:00
|
|
|
|
return PointBase.Subtract(p1, p2).Length;
|
2021-07-23 09:42:22 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-14 21:52:05 +08:00
|
|
|
|
private static RectangleBase GetRectWithMargin(PointBase point, double margin)
|
2021-07-23 09:42:22 +08:00
|
|
|
|
{
|
2023-01-14 21:52:05 +08:00
|
|
|
|
RectangleBase rect = new RectangleBase(point.X, point.Y, 0, 0);
|
2021-07-23 09:42:22 +08:00
|
|
|
|
rect.Inflate(margin, margin);
|
|
|
|
|
|
|
|
|
|
|
|
return rect;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-14 21:52:05 +08:00
|
|
|
|
private static PointBase GetOffsetPoint(PointBase point, ConnectorOrientation orientation, RectangleBase rect, bool isInnerPoint = false)
|
2021-07-23 09:42:22 +08:00
|
|
|
|
{
|
2023-01-08 09:22:37 +08:00
|
|
|
|
PointBase offsetPoint = new PointBase();
|
2021-07-23 09:42:22 +08:00
|
|
|
|
if (isInnerPoint)
|
|
|
|
|
|
{
|
2023-01-14 21:52:05 +08:00
|
|
|
|
offsetPoint = new PointBase(point.X, point.Y);
|
2021-07-23 09:42:22 +08:00
|
|
|
|
return offsetPoint;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-14 21:52:05 +08:00
|
|
|
|
switch (orientation)
|
2021-07-23 09:42:22 +08:00
|
|
|
|
{
|
|
|
|
|
|
case ConnectorOrientation.Left:
|
2023-01-14 21:52:05 +08:00
|
|
|
|
offsetPoint = new PointBase(rect.Left, point.Y);
|
2021-07-23 09:42:22 +08:00
|
|
|
|
break;
|
|
|
|
|
|
case ConnectorOrientation.Top:
|
2023-01-14 21:52:05 +08:00
|
|
|
|
offsetPoint = new PointBase(point.X, rect.Top);
|
2021-07-23 09:42:22 +08:00
|
|
|
|
break;
|
|
|
|
|
|
case ConnectorOrientation.Right:
|
2023-01-14 21:52:05 +08:00
|
|
|
|
offsetPoint = new PointBase(rect.Right, point.Y);
|
2021-07-23 09:42:22 +08:00
|
|
|
|
break;
|
|
|
|
|
|
case ConnectorOrientation.Bottom:
|
2023-01-14 21:52:05 +08:00
|
|
|
|
offsetPoint = new PointBase(point.X, rect.Bottom);
|
2021-07-23 09:42:22 +08:00
|
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return offsetPoint;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static ConnectorOrientation GetOpositeOrientation(ConnectorOrientation connectorOrientation)
|
|
|
|
|
|
{
|
|
|
|
|
|
switch (connectorOrientation)
|
|
|
|
|
|
{
|
|
|
|
|
|
case ConnectorOrientation.Left:
|
|
|
|
|
|
return ConnectorOrientation.Right;
|
|
|
|
|
|
case ConnectorOrientation.Top:
|
|
|
|
|
|
return ConnectorOrientation.Bottom;
|
|
|
|
|
|
case ConnectorOrientation.Right:
|
|
|
|
|
|
return ConnectorOrientation.Left;
|
|
|
|
|
|
case ConnectorOrientation.Bottom:
|
|
|
|
|
|
return ConnectorOrientation.Top;
|
|
|
|
|
|
default:
|
|
|
|
|
|
return ConnectorOrientation.Top;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|