mirror of
https://gitee.com/akwkevin/aistudio.-wpf.-diagram
synced 2026-03-02 15:50:51 +08:00
使用PointBase代替Point
This commit is contained in:
@@ -335,8 +335,8 @@ namespace AIStudio.Wpf.DiagramApp.ViewModels
|
||||
foreach (var connectionVM in DiagramViewModel.Items.OfType<ConnectorViewModel>())
|
||||
{
|
||||
|
||||
FullyCreatedConnectorInfo sinkConnector = connectionVM.SinkConnectorInfo as FullyCreatedConnectorInfo;
|
||||
|
||||
FullyCreatedConnectorInfo sinkConnector = connectionVM.SinkConnectorInfoFully;
|
||||
|
||||
ConnectionItem connection = new ConnectionItem(
|
||||
connectionVM.SourceConnectorInfo.DataItem.Id,
|
||||
connectionVM.SourceConnectorInfo.Orientation,
|
||||
@@ -523,7 +523,9 @@ namespace AIStudio.Wpf.DiagramApp.ViewModels
|
||||
|
||||
foreach (var connectionVM in DiagramViewModel.Items.OfType<ConnectorViewModel>())
|
||||
{
|
||||
FullyCreatedConnectorInfo sinkConnector = connectionVM.SinkConnectorInfo as FullyCreatedConnectorInfo;
|
||||
if (connectionVM.IsFullConnection == false) continue;
|
||||
|
||||
FullyCreatedConnectorInfo sinkConnector = connectionVM.SinkConnectorInfoFully;
|
||||
|
||||
ConnectionItem connection = new ConnectionItem(
|
||||
connectionVM.SourceConnectorInfo.DataItem.Id,
|
||||
|
||||
@@ -126,9 +126,9 @@ namespace AIStudio.Wpf.Logical
|
||||
|
||||
if (arg is ConnectorViewModel connector)
|
||||
{
|
||||
if (connector.SinkConnectorInfo is FullyCreatedConnectorInfo fully)
|
||||
if (connector.IsFullConnection)
|
||||
{
|
||||
if (DiagramViewModel.Items.OfType<ConnectorViewModel>().Any(p => p.SinkConnectorInfo == fully))
|
||||
if (DiagramViewModel.Items.OfType<ConnectorViewModel>().Any(p => p.SinkConnectorInfo == connector.SinkConnectorInfoFully))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -58,9 +58,5 @@
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Routers\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -203,7 +203,7 @@ namespace AIStudio.Wpf.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(_viewModel, sourceDataItem, new PartCreatedConnectionInfo(point), VectorLineDrawMode);
|
||||
partialConnection = new ConnectorViewModel(_viewModel, sourceDataItem, new PartCreatedConnectionInfo(point.X, point.Y), VectorLineDrawMode);
|
||||
|
||||
_viewModel.DirectAddItemCommand.Execute(partialConnection);
|
||||
}
|
||||
@@ -225,7 +225,7 @@ namespace AIStudio.Wpf.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(_viewModel, sourceConnectorInfo, new PartCreatedConnectionInfo(point), VectorLineDrawMode);
|
||||
partialConnection = new ConnectorViewModel(_viewModel, sourceConnectorInfo, new PartCreatedConnectionInfo(point.X, point.Y), VectorLineDrawMode);
|
||||
_viewModel.DirectAddItemCommand.Execute(partialConnection);
|
||||
}
|
||||
}
|
||||
@@ -309,7 +309,7 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
{
|
||||
if (e.LeftButton == MouseButtonState.Pressed)
|
||||
{
|
||||
partialConnection.SinkConnectorInfo = new PartCreatedConnectionInfo(currentPoint);
|
||||
partialConnection.SinkConnectorInfo = new PartCreatedConnectionInfo(currentPoint.X, currentPoint.Y);
|
||||
HitTesting(currentPoint);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,9 +31,9 @@ namespace AIStudio.Wpf.DiagramDesigner.Controls
|
||||
if (designerItem is ConnectorViewModel connector)
|
||||
{
|
||||
designerItems.Add(connector.SourceConnectorInfo.DataItem);
|
||||
if (connector.SinkConnectorInfo is FullyCreatedConnectorInfo)
|
||||
if (connector.IsFullConnection)
|
||||
{
|
||||
designerItems.Add((connector.SinkConnectorInfo as FullyCreatedConnectorInfo).DataItem);
|
||||
designerItems.Add(connector.SinkConnectorInfoFully.DataItem);
|
||||
}
|
||||
if (designerItem.OutTextItem != null)
|
||||
{
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace AIStudio.Wpf.DiagramDesigner.Controls
|
||||
|
||||
void DragThumb_DragDelta(object sender, DragDeltaEventArgs e)
|
||||
{
|
||||
if (this.DataContext is PointInfoBase point)
|
||||
if (this.DataContext is ConnectorPoint point)
|
||||
{
|
||||
double minLeft = double.MaxValue;
|
||||
double minTop = double.MaxValue;
|
||||
|
||||
@@ -26,45 +26,53 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
PathGeometry pathGeometry = new PathGeometry();
|
||||
if (values[0] != null)
|
||||
{
|
||||
List<PointInfoBase> points = (List<PointInfoBase>)values[0];
|
||||
//if (values[0] != null)
|
||||
//{
|
||||
// List<ConnectorPoint> points = (List<ConnectorPoint>)values[0];
|
||||
|
||||
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++)
|
||||
{
|
||||
// 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);
|
||||
// }
|
||||
|
||||
LineSegment arc = new LineSegment(points[i], true);
|
||||
figure.Segments.Add(arc);
|
||||
}
|
||||
}
|
||||
// //贝塞尔曲线的简单连接
|
||||
// //var start = points[0];
|
||||
// //var end = points.Last();
|
||||
// //var width = end.X - start.X;
|
||||
// //var height = end.Y - start.Y;
|
||||
// //var ctrlPos = 0.382 * width + 0.309 * height;
|
||||
// //figure.Segments.Add(new BezierSegment(new Point(ctrlPos - 1, 1), new Point(width - ctrlPos + 1, height - 1), new Point(width - 1, height - 1), true));
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// for (int i = 0; i < points.Count; i++)
|
||||
// {
|
||||
|
||||
pathGeometry.Figures.Add(figure);
|
||||
}
|
||||
// LineSegment arc = new LineSegment(points[i], true);
|
||||
// figure.Segments.Add(arc);
|
||||
// }
|
||||
// }
|
||||
|
||||
// pathGeometry.Figures.Add(figure);
|
||||
//}
|
||||
return pathGeometry;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,13 +25,13 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
|
||||
{
|
||||
List<PointInfoBase> points = (List<PointInfoBase>)value;
|
||||
List<ConnectorPoint> points = (List<ConnectorPoint>)value;
|
||||
PointCollection pointCollection = new PointCollection();
|
||||
if (points != null)
|
||||
{
|
||||
foreach (var point in points)
|
||||
{
|
||||
pointCollection.Add(point);
|
||||
pointCollection.Add(new Point(point.X, point.Y));
|
||||
}
|
||||
}
|
||||
return pointCollection;
|
||||
|
||||
@@ -7,9 +7,13 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
public enum ConnectorOrientation
|
||||
{
|
||||
None = 0,
|
||||
Left = 1,
|
||||
Top = 2,
|
||||
Right = 3,
|
||||
Bottom = 4
|
||||
Top,
|
||||
TopRight,
|
||||
Right,
|
||||
BottomRight,
|
||||
Bottom,
|
||||
BottomLeft,
|
||||
Left,
|
||||
TopLeft
|
||||
}
|
||||
}
|
||||
|
||||
206
AIStudio.Wpf.DiagramDesigner/Geometry/BezierSpline.cs
Normal file
206
AIStudio.Wpf.DiagramDesigner/Geometry/BezierSpline.cs
Normal file
@@ -0,0 +1,206 @@
|
||||
using System;
|
||||
|
||||
namespace AIStudio.Wpf.DiagramDesigner.Geometry
|
||||
{
|
||||
/// <summary>
|
||||
/// Bezier Spline methods
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Modified: Peter Lee (peterlee.com.cn < at > gmail.com)
|
||||
/// Update: 2009-03-16
|
||||
///
|
||||
/// see also:
|
||||
/// Draw a smooth curve through a set of 2D points with Bezier primitives
|
||||
/// http://www.codeproject.com/KB/graphics/BezierSpline.aspx
|
||||
/// By Oleg V. Polikarpotchkin
|
||||
///
|
||||
/// Algorithm Descripition:
|
||||
///
|
||||
/// To make a sequence of individual Bezier curves to be a spline, we
|
||||
/// should calculate Bezier control points so that the spline curve
|
||||
/// has two continuous derivatives at knot points.
|
||||
///
|
||||
/// Note: `[]` denotes subscript
|
||||
/// `^` denotes supscript
|
||||
/// `'` denotes first derivative
|
||||
/// `''` denotes second derivative
|
||||
///
|
||||
/// A Bezier curve on a single interval can be expressed as:
|
||||
///
|
||||
/// B(t) = (1-t)^3 P0 + 3(1-t)^2 t P1 + 3(1-t)t^2 P2 + t^3 P3 (*)
|
||||
///
|
||||
/// where t is in [0,1], and
|
||||
/// 1. P0 - first knot point
|
||||
/// 2. P1 - first control point (close to P0)
|
||||
/// 3. P2 - second control point (close to P3)
|
||||
/// 4. P3 - second knot point
|
||||
///
|
||||
/// The first derivative of (*) is:
|
||||
///
|
||||
/// B'(t) = -3(1-t)^2 P0 + 3(3t^2–4t+1) P1 + 3(2–3t)t P2 + 3t^2 P3
|
||||
///
|
||||
/// The second derivative of (*) is:
|
||||
///
|
||||
/// B''(t) = 6(1-t) P0 + 6(3t-2) P1 + 6(1–3t) P2 + 6t P3
|
||||
///
|
||||
/// Considering a set of piecewise Bezier curves with n+1 points
|
||||
/// (Q[0..n]) and n subintervals, the (i-1)-th curve should connect
|
||||
/// to the i-th one:
|
||||
///
|
||||
/// Q[0] = P0[1],
|
||||
/// Q[1] = P0[2] = P3[1], ... , Q[i-1] = P0[i] = P3[i-1] (i = 1..n) (@)
|
||||
///
|
||||
/// At the i-th subinterval, the Bezier curve is:
|
||||
///
|
||||
/// B[i](t) = (1-t)^3 P0[i] + 3(1-t)^2 t P1[i] +
|
||||
/// 3(1-t)t^2 P2[i] + t^3 P3[i] (i = 1..n)
|
||||
///
|
||||
/// applying (@):
|
||||
///
|
||||
/// B[i](t) = (1-t)^3 Q[i-1] + 3(1-t)^2 t P1[i] +
|
||||
/// 3(1-t)t^2 P2[i] + t^3 Q[i] (i = 1..n) (i)
|
||||
///
|
||||
/// From (i), the first derivative at the i-th subinterval is:
|
||||
///
|
||||
/// B'[i](t) = -3(1-t)^2 Q[i-1] + 3(3t^2–4t+1) P1[i] +
|
||||
/// 3(2–3t)t P2[i] + 3t^2 Q[i] (i = 1..n)
|
||||
///
|
||||
/// Using the first derivative continuity condition:
|
||||
///
|
||||
/// B'[i-1](1) = B'[i](0)
|
||||
///
|
||||
/// we get:
|
||||
///
|
||||
/// P1[i] + P2[i-1] = 2Q[i-1] (i = 2..n) (1)
|
||||
///
|
||||
/// From (i), the second derivative at the i-th subinterval is:
|
||||
///
|
||||
/// B''[i](t) = 6(1-t) Q[i-1] + 6(3t-2) P1[i] +
|
||||
/// 6(1-3t) P2[i] + 6t Q[i] (i = 1..n)
|
||||
///
|
||||
/// Using the second derivative continuity condition:
|
||||
///
|
||||
/// B''[i-1](1) = B''[i](0)
|
||||
///
|
||||
/// we get:
|
||||
///
|
||||
/// P1[i-1] + 2P1[i] = P2[i] + 2P2[i-1] (i = 2..n) (2)
|
||||
///
|
||||
/// Then, using the so-called "natural conditions":
|
||||
///
|
||||
/// B''[1](0) = 0
|
||||
///
|
||||
/// B''[n](1) = 0
|
||||
///
|
||||
/// to the second derivative equations, and we get:
|
||||
///
|
||||
/// 2P1[1] - P2[1] = Q[0] (3)
|
||||
///
|
||||
/// 2P2[n] - P1[n] = Q[n] (4)
|
||||
///
|
||||
/// From (1)(2)(3)(4), we have 2n conditions for n first control points
|
||||
/// P1[1..n], and n second control points P2[1..n].
|
||||
///
|
||||
/// Eliminating P2[1..n], we get (be patient to get :-) a set of n
|
||||
/// equations for solving P1[1..n]:
|
||||
///
|
||||
/// 2P1[1] + P1[2] + = Q[0] + 2Q[1]
|
||||
/// P1[1] + 4P1[2] + P1[3] = 4Q[1] + 2Q[2]
|
||||
/// ...
|
||||
/// P1[i-1] + 4P1[i] + P1[i+1] = 4Q[i-1] + 2Q[i]
|
||||
/// ...
|
||||
/// P1[n-2] + 4P1[n-1] + P1[n] = 4Q[n-2] + 2Q[n-1]
|
||||
/// P1[n-1] + 3.5P1[n] = (8Q[n-1] + Q[n]) / 2
|
||||
///
|
||||
/// From this set of equations, P1[1..n] are easy but tedious to solve.
|
||||
/// </remarks>
|
||||
public static class BezierSpline
|
||||
{
|
||||
/// <summary>
|
||||
/// Get open-ended Bezier Spline Control Points.
|
||||
/// </summary>
|
||||
/// <param name="knots">Input Knot Bezier spline points.</param>
|
||||
/// <param name="firstControlPoints">Output First Control points array of knots.Length - 1 length.</param>
|
||||
/// <param name="secondControlPoints">Output Second Control points array of knots.Length - 1 length.</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="knots"/> parameter must be not null.</exception>
|
||||
/// <exception cref="ArgumentException"><paramref name="knots"/> array must containg at least two points.</exception>
|
||||
public static void GetCurveControlPoints(PointBase[] knots, out PointBase[] firstControlPoints, out PointBase[] secondControlPoints)
|
||||
{
|
||||
if (knots == null)
|
||||
throw new ArgumentNullException("knots");
|
||||
int n = knots.Length - 1;
|
||||
if (n < 1)
|
||||
throw new ArgumentException("At least two knot points required", "knots");
|
||||
if (n == 1)
|
||||
{ // Special case: Bezier curve should be a straight line.
|
||||
firstControlPoints = new PointBase[1];
|
||||
// 3P1 = 2P0 + P3
|
||||
firstControlPoints[0] = new PointBase((2 * knots[0].X + knots[1].X) / 3, (2 * knots[0].Y + knots[1].Y) / 3);
|
||||
|
||||
secondControlPoints = new PointBase[1];
|
||||
// P2 = 2P1 – P0
|
||||
secondControlPoints[0] = new PointBase(2 * firstControlPoints[0].X - knots[0].X, 2 * firstControlPoints[0].Y - knots[0].Y);
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate first Bezier control points
|
||||
// Right hand side vector
|
||||
double[] rhs = new double[n];
|
||||
|
||||
// Set right hand side X values
|
||||
for (int i = 1; i < n - 1; ++i)
|
||||
rhs[i] = 4 * knots[i].X + 2 * knots[i + 1].X;
|
||||
rhs[0] = knots[0].X + 2 * knots[1].X;
|
||||
rhs[n - 1] = (8 * knots[n - 1].X + knots[n].X) / 2.0;
|
||||
// Get first control points X-values
|
||||
double[] x = GetFirstControlPoints(rhs);
|
||||
|
||||
// Set right hand side Y values
|
||||
for (int i = 1; i < n - 1; ++i)
|
||||
rhs[i] = 4 * knots[i].Y + 2 * knots[i + 1].Y;
|
||||
rhs[0] = knots[0].Y + 2 * knots[1].Y;
|
||||
rhs[n - 1] = (8 * knots[n - 1].Y + knots[n].Y) / 2.0;
|
||||
// Get first control points Y-values
|
||||
double[] y = GetFirstControlPoints(rhs);
|
||||
|
||||
// Fill output arrays.
|
||||
firstControlPoints = new PointBase[n];
|
||||
secondControlPoints = new PointBase[n];
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
// First control point
|
||||
firstControlPoints[i] = new PointBase(x[i], y[i]);
|
||||
// Second control point
|
||||
if (i < n - 1)
|
||||
secondControlPoints[i] = new PointBase(2 * knots[i + 1].X - x[i + 1], 2 * knots[i + 1].Y - y[i + 1]);
|
||||
else
|
||||
secondControlPoints[i] = new PointBase((knots[n].X + x[n - 1]) / 2, (knots[n].Y + y[n - 1]) / 2);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Solves a tridiagonal system for one of coordinates (x or y) of first Bezier control points.
|
||||
/// </summary>
|
||||
/// <param name="rhs">Right hand side vector.</param>
|
||||
/// <returns>Solution vector.</returns>
|
||||
private static double[] GetFirstControlPoints(double[] rhs)
|
||||
{
|
||||
int n = rhs.Length;
|
||||
double[] x = new double[n]; // Solution vector.
|
||||
double[] tmp = new double[n]; // Temp workspace.
|
||||
|
||||
double b = 2.0;
|
||||
x[0] = rhs[0] / b;
|
||||
for (int i = 1; i < n; i++) // Decomposition and forward substitution.
|
||||
{
|
||||
tmp[i] = 1 / b;
|
||||
b = (i < n - 1 ? 4.0 : 3.5) - tmp[i];
|
||||
x[i] = (rhs[i] - x[i - 1]) / b;
|
||||
}
|
||||
for (int i = 1; i < n; i++)
|
||||
x[n - i - 1] -= tmp[n - i] * x[n - i]; // Backsubstitution.
|
||||
|
||||
return x;
|
||||
}
|
||||
}
|
||||
}
|
||||
60
AIStudio.Wpf.DiagramDesigner/Geometry/EllipseBase.cs
Normal file
60
AIStudio.Wpf.DiagramDesigner/Geometry/EllipseBase.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace AIStudio.Wpf.DiagramDesigner.Geometry
|
||||
{
|
||||
public class EllipseBase : IShape
|
||||
{
|
||||
public EllipseBase(double cx, double cy, double rx, double ry)
|
||||
{
|
||||
Cx = cx;
|
||||
Cy = cy;
|
||||
Rx = rx;
|
||||
Ry = ry;
|
||||
}
|
||||
|
||||
public double Cx { get; }
|
||||
public double Cy { get; }
|
||||
public double Rx { get; }
|
||||
public double Ry { get; }
|
||||
|
||||
public IEnumerable<PointBase> GetIntersectionsWithLine(LineBase line)
|
||||
{
|
||||
var a1 = line.Start;
|
||||
var a2 = line.End;
|
||||
var dir = new PointBase(line.End.X - line.Start.X, line.End.Y - line.Start.Y);
|
||||
var diff = a1.Substract(Cx, Cy);
|
||||
var mDir = new PointBase(dir.X / (Rx * Rx), dir.Y / (Ry * Ry));
|
||||
var mDiff = new PointBase(diff.X / (Rx * Rx), diff.Y / (Ry * Ry));
|
||||
|
||||
var a = dir.Dot(mDir);
|
||||
var b = dir.Dot(mDiff);
|
||||
var c = diff.Dot(mDiff) - 1.0;
|
||||
var d = b * b - a * c;
|
||||
|
||||
if (d > 0)
|
||||
{
|
||||
var root = Math.Sqrt(d);
|
||||
var ta = (-b - root) / a;
|
||||
var tb = (-b + root) / a;
|
||||
|
||||
if (ta >= 0 && 1 >= ta || tb >= 0 && 1 >= tb)
|
||||
{
|
||||
if (0 <= ta && ta <= 1)
|
||||
yield return a1.Lerp(a2, ta);
|
||||
|
||||
if (0 <= tb && tb <= 1)
|
||||
yield return a1.Lerp(a2, tb);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var t = -b / a;
|
||||
if (0 <= t && t <= 1)
|
||||
{
|
||||
yield return a1.Lerp(a2, t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
9
AIStudio.Wpf.DiagramDesigner/Geometry/IShape.cs
Normal file
9
AIStudio.Wpf.DiagramDesigner/Geometry/IShape.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace AIStudio.Wpf.DiagramDesigner.Geometry
|
||||
{
|
||||
public interface IShape
|
||||
{
|
||||
IEnumerable<PointBase> GetIntersectionsWithLine(LineBase line);
|
||||
}
|
||||
}
|
||||
43
AIStudio.Wpf.DiagramDesigner/Geometry/LineBase.cs
Normal file
43
AIStudio.Wpf.DiagramDesigner/Geometry/LineBase.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
namespace AIStudio.Wpf.DiagramDesigner.Geometry
|
||||
{
|
||||
public struct LineBase
|
||||
{
|
||||
public LineBase(PointBase start, PointBase end)
|
||||
{
|
||||
Start = start;
|
||||
End = end;
|
||||
}
|
||||
|
||||
public PointBase Start { get; }
|
||||
public PointBase End { get; }
|
||||
|
||||
public PointBase GetIntersection(LineBase line)
|
||||
{
|
||||
var pt1Dir = new PointBase(End.X - Start.X, End.Y - Start.Y);
|
||||
var pt2Dir = new PointBase(line.End.X - line.Start.X, line.End.Y - line.Start.Y);
|
||||
var det = (pt1Dir.X * pt2Dir.Y) - (pt1Dir.Y * pt2Dir.X);
|
||||
var deltaPt = new PointBase(line.Start.X - Start.X, line.Start.Y - Start.Y);
|
||||
var alpha = (deltaPt.X * pt2Dir.Y) - (deltaPt.Y * pt2Dir.X);
|
||||
var beta = (deltaPt.X * pt1Dir.Y) - (deltaPt.Y * pt1Dir.X);
|
||||
|
||||
if (det == 0 || alpha * det < 0 || beta * det < 0)
|
||||
return PointBase.Empty;
|
||||
|
||||
if (det > 0)
|
||||
{
|
||||
if (alpha > det || beta > det)
|
||||
return PointBase.Empty;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if (alpha < det || beta < det)
|
||||
return PointBase.Empty;
|
||||
}
|
||||
|
||||
return new PointBase(Start.X + (alpha * pt1Dir.X / det), Start.Y + (alpha * pt1Dir.Y / det));
|
||||
}
|
||||
|
||||
public override string ToString() => $"Line from {Start} to {End}";
|
||||
}
|
||||
}
|
||||
466
AIStudio.Wpf.DiagramDesigner/Geometry/PointBase.cs
Normal file
466
AIStudio.Wpf.DiagramDesigner/Geometry/PointBase.cs
Normal file
@@ -0,0 +1,466 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Windows;
|
||||
|
||||
namespace AIStudio.Wpf.DiagramDesigner.Geometry
|
||||
{
|
||||
[Serializable]
|
||||
public struct PointBase : IFormattable
|
||||
{
|
||||
public static PointBase Zero { get; } = new PointBase(0, 0);
|
||||
|
||||
public PointBase(double x, double y)
|
||||
{
|
||||
_x = x;
|
||||
_y = y;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 中间X
|
||||
/// </summary>
|
||||
internal double _x;
|
||||
public double X
|
||||
{
|
||||
get
|
||||
{
|
||||
return _x;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
_x = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 中间Y
|
||||
/// </summary>
|
||||
internal double _y;
|
||||
public double Y
|
||||
{
|
||||
get
|
||||
{
|
||||
return _y;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
_y = value;
|
||||
}
|
||||
}
|
||||
|
||||
public double Dot(PointBase other) => X * other.X + Y * other.Y;
|
||||
|
||||
public PointBase Lerp(PointBase other, double t)
|
||||
=> new PointBase(X * (1.0 - t) + other.X * t, Y * (1.0 - t) + other.Y * t);
|
||||
|
||||
// Maybe just make Points mutable?
|
||||
public PointBase Add(double value) => new PointBase(X + value, Y + value);
|
||||
public PointBase Add(double x, double y) => new PointBase(X + x, Y + y);
|
||||
|
||||
public PointBase Substract(double value) => new PointBase(X - value, Y - value);
|
||||
public PointBase Substract(double x, double y) => new PointBase(X - x, Y - y);
|
||||
|
||||
public double DistanceTo(PointBase other)
|
||||
=> Math.Sqrt(Math.Pow(X - other.X, 2) + Math.Pow(Y - other.Y, 2));
|
||||
|
||||
public void Deconstruct(out double x, out double y)
|
||||
{
|
||||
x = X;
|
||||
y = Y;
|
||||
}
|
||||
|
||||
//public static PointBase operator -(PointBase a, PointBase b)
|
||||
//{
|
||||
// return new PointBase(a.X - b.X, a.Y - b.Y);
|
||||
//}
|
||||
//public static PointBase operator +(PointBase a, PointBase b)
|
||||
//{
|
||||
// return new PointBase(a.X + b.X, a.Y + b.Y);
|
||||
//}
|
||||
|
||||
public static implicit operator PointBase(Point point)
|
||||
{
|
||||
return new PointBase(point.X, point.Y);
|
||||
}
|
||||
|
||||
public static implicit operator Point(PointBase pointInfoBase)
|
||||
{
|
||||
return new Point(pointInfoBase.X, pointInfoBase.Y);
|
||||
}
|
||||
|
||||
//public override string ToString() => $"PointBase(x={X}, y={Y})";
|
||||
|
||||
#region Statics
|
||||
|
||||
/// <summary>
|
||||
/// Empty - a static property which provides an Empty rectangle. X and Y are positive-infinity
|
||||
/// and Width and Height are negative infinity. This is the only situation where Width or
|
||||
/// Height can be negative.
|
||||
/// </summary>
|
||||
public static PointBase Empty
|
||||
{
|
||||
get
|
||||
{
|
||||
return s_empty;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Statics
|
||||
|
||||
#region Private Methods
|
||||
|
||||
static private PointBase CreateEmptyRect()
|
||||
{
|
||||
PointBase point = new PointBase();
|
||||
// We can't set these via the property setters because negatives widths
|
||||
// are rejected in those APIs.
|
||||
point._x = Double.PositiveInfinity;
|
||||
point._y = Double.PositiveInfinity;
|
||||
return point;
|
||||
}
|
||||
|
||||
#endregion Private Methods
|
||||
|
||||
|
||||
#region Private Fields
|
||||
|
||||
private readonly static PointBase s_empty = CreateEmptyRect();
|
||||
|
||||
#endregion Private Fields
|
||||
|
||||
#region Public Methods
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Compares two PointBase instances for exact equality.
|
||||
/// Note that double values can acquire error when operated upon, such that
|
||||
/// an exact comparison between two values which are logically equal may fail.
|
||||
/// Furthermore, using this equality operator, Double.NaN is not equal to itself.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// bool - true if the two PointBase instances are exactly equal, false otherwise
|
||||
/// </returns>
|
||||
/// <param name='point1'>The first PointBase to compare</param>
|
||||
/// <param name='point2'>The second PointBase to compare</param>
|
||||
public static bool operator ==(PointBase point1, PointBase point2)
|
||||
{
|
||||
return point1.X == point2.X &&
|
||||
point1.Y == point2.Y;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two PointBase instances for exact inequality.
|
||||
/// Note that double values can acquire error when operated upon, such that
|
||||
/// an exact comparison between two values which are logically equal may fail.
|
||||
/// Furthermore, using this equality operator, Double.NaN is not equal to itself.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// bool - true if the two PointBase instances are exactly unequal, false otherwise
|
||||
/// </returns>
|
||||
/// <param name='point1'>The first PointBase to compare</param>
|
||||
/// <param name='point2'>The second PointBase to compare</param>
|
||||
public static bool operator !=(PointBase point1, PointBase point2)
|
||||
{
|
||||
return !(point1 == point2);
|
||||
}
|
||||
/// <summary>
|
||||
/// Compares two PointBase instances for object equality. In this equality
|
||||
/// Double.NaN is equal to itself, unlike in numeric equality.
|
||||
/// Note that double values can acquire error when operated upon, such that
|
||||
/// an exact comparison between two values which
|
||||
/// are logically equal may fail.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// bool - true if the two PointBase instances are exactly equal, false otherwise
|
||||
/// </returns>
|
||||
/// <param name='point1'>The first PointBase to compare</param>
|
||||
/// <param name='point2'>The second PointBase to compare</param>
|
||||
public static bool Equals(PointBase point1, PointBase point2)
|
||||
{
|
||||
return point1.X.Equals(point2.X) &&
|
||||
point1.Y.Equals(point2.Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Equals - compares this PointBase with the passed in object. In this equality
|
||||
/// Double.NaN is equal to itself, unlike in numeric equality.
|
||||
/// Note that double values can acquire error when operated upon, such that
|
||||
/// an exact comparison between two values which
|
||||
/// are logically equal may fail.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// bool - true if the object is an instance of PointBase and if it's equal to "this".
|
||||
/// </returns>
|
||||
/// <param name='o'>The object to compare to "this"</param>
|
||||
public override bool Equals(object o)
|
||||
{
|
||||
if ((null == o) || !(o is PointBase))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
PointBase value = (PointBase)o;
|
||||
return PointBase.Equals(this, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Equals - compares this PointBase with the passed in object. In this equality
|
||||
/// Double.NaN is equal to itself, unlike in numeric equality.
|
||||
/// Note that double values can acquire error when operated upon, such that
|
||||
/// an exact comparison between two values which
|
||||
/// are logically equal may fail.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// bool - true if "value" is equal to "this".
|
||||
/// </returns>
|
||||
/// <param name='value'>The PointBase to compare to "this"</param>
|
||||
public bool Equals(PointBase value)
|
||||
{
|
||||
return PointBase.Equals(this, value);
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns the HashCode for this PointBase
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// int - the HashCode for this PointBase
|
||||
/// </returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
// Perform field-by-field XOR of HashCodes
|
||||
return X.GetHashCode() ^
|
||||
Y.GetHashCode();
|
||||
}
|
||||
|
||||
///// <summary>
|
||||
///// Parse - returns an instance converted from the provided string using
|
||||
///// the culture "en-US"
|
||||
///// <param name="source"> string with PointBase data </param>
|
||||
///// </summary>
|
||||
//public static PointBase Parse(string source)
|
||||
//{
|
||||
// IFormatProvider formatProvider = System.Windows.Markup.TypeConverterHelper.InvariantEnglishUS;
|
||||
|
||||
// TokenizerHelper th = new TokenizerHelper(source, formatProvider);
|
||||
|
||||
// PointBase value;
|
||||
|
||||
// String firstToken = th.NextTokenRequired();
|
||||
|
||||
// value = new PointBase(
|
||||
// Convert.ToDouble(firstToken, formatProvider),
|
||||
// Convert.ToDouble(th.NextTokenRequired(), formatProvider));
|
||||
|
||||
// // There should be no more tokens in this string.
|
||||
// th.LastTokenRequired();
|
||||
|
||||
// return value;
|
||||
//}
|
||||
|
||||
#endregion Public Methods
|
||||
|
||||
#region Internal Properties
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates a string representation of this object based on the current culture.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A string representation of this object.
|
||||
/// </returns>
|
||||
public override string ToString()
|
||||
{
|
||||
// Delegate to the internal method which implements all ToString calls.
|
||||
return ConvertToString(null /* format string */, null /* format provider */);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a string representation of this object based on the IFormatProvider
|
||||
/// passed in. If the provider is null, the CurrentCulture is used.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A string representation of this object.
|
||||
/// </returns>
|
||||
public string ToString(IFormatProvider provider)
|
||||
{
|
||||
// Delegate to the internal method which implements all ToString calls.
|
||||
return ConvertToString(null /* format string */, provider);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a string representation of this object based on the format string
|
||||
/// and IFormatProvider passed in.
|
||||
/// If the provider is null, the CurrentCulture is used.
|
||||
/// See the documentation for IFormattable for more information.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A string representation of this object.
|
||||
/// </returns>
|
||||
string IFormattable.ToString(string format, IFormatProvider provider)
|
||||
{
|
||||
// Delegate to the internal method which implements all ToString calls.
|
||||
return ConvertToString(format, provider);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a string representation of this object based on the format string
|
||||
/// and IFormatProvider passed in.
|
||||
/// If the provider is null, the CurrentCulture is used.
|
||||
/// See the documentation for IFormattable for more information.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A string representation of this object.
|
||||
/// </returns>
|
||||
internal string ConvertToString(string format, IFormatProvider provider)
|
||||
{
|
||||
// Helper to get the numeric list separator for a given culture.
|
||||
char separator = ',';
|
||||
return String.Format(provider,
|
||||
"{1:" + format + "}{0}{2:" + format + "}",
|
||||
separator,
|
||||
_x,
|
||||
_y);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endregion Internal Properties
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Offset - update the location by adding offsetX to X and offsetY to Y
|
||||
/// </summary>
|
||||
/// <param name="offsetX"> The offset in the x dimension </param>
|
||||
/// <param name="offsetY"> The offset in the y dimension </param>
|
||||
public void Offset(double offsetX, double offsetY)
|
||||
{
|
||||
_x += offsetX;
|
||||
_y += offsetY;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Operator PointBase + Vector
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// PointBase - The result of the addition
|
||||
/// </returns>
|
||||
/// <param name="point"> The PointBase to be added to the Vector </param>
|
||||
/// <param name="vector"> The Vectr to be added to the PointBase </param>
|
||||
public static PointBase operator +(PointBase point, VectorBase vector)
|
||||
{
|
||||
return new PointBase(point._x + vector._x, point._y + vector._y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add: PointBase + Vector
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// PointBase - The result of the addition
|
||||
/// </returns>
|
||||
/// <param name="point"> The PointBase to be added to the Vector </param>
|
||||
/// <param name="vector"> The Vector to be added to the PointBase </param>
|
||||
public static PointBase Add(PointBase point, VectorBase vector)
|
||||
{
|
||||
return new PointBase(point._x + vector._x, point._y + vector._y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Operator PointBase - Vector
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// PointBase - The result of the subtraction
|
||||
/// </returns>
|
||||
/// <param name="point"> The PointBase from which the Vector is subtracted </param>
|
||||
/// <param name="vector"> The Vector which is subtracted from the PointBase </param>
|
||||
public static PointBase operator -(PointBase point, VectorBase vector)
|
||||
{
|
||||
return new PointBase(point._x - vector._x, point._y - vector._y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subtract: PointBase - Vector
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// PointBase - The result of the subtraction
|
||||
/// </returns>
|
||||
/// <param name="point"> The PointBase from which the Vector is subtracted </param>
|
||||
/// <param name="vector"> The Vector which is subtracted from the PointBase </param>
|
||||
public static PointBase Subtract(PointBase point, VectorBase vector)
|
||||
{
|
||||
return new PointBase(point._x - vector._x, point._y - vector._y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Operator PointBase - PointBase
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Vector - The result of the subtraction
|
||||
/// </returns>
|
||||
/// <param name="point1"> The PointBase from which point2 is subtracted </param>
|
||||
/// <param name="point2"> The PointBase subtracted from point1 </param>
|
||||
public static VectorBase operator -(PointBase point1, PointBase point2)
|
||||
{
|
||||
return new VectorBase(point1._x - point2._x, point1._y - point2._y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subtract: PointBase - PointBase
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Vector - The result of the subtraction
|
||||
/// </returns>
|
||||
/// <param name="point1"> The PointBase from which point2 is subtracted </param>
|
||||
/// <param name="point2"> The PointBase subtracted from point1 </param>
|
||||
public static VectorBase Subtract(PointBase point1, PointBase point2)
|
||||
{
|
||||
return new VectorBase(point1._x - point2._x, point1._y - point2._y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Operator PointBase * Matrix
|
||||
/// </summary>
|
||||
//public static PointBase operator *(PointBase point, Matrix matrix)
|
||||
//{
|
||||
// return matrix.Transform(point);
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// Multiply: PointBase * Matrix
|
||||
/// </summary>
|
||||
//public static PointBase Multiply(PointBase point, Matrix matrix)
|
||||
//{
|
||||
// return matrix.Transform(point);
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// Explicit conversion to Size. Note that since Size cannot contain negative values,
|
||||
/// the resulting size will contains the absolute values of X and Y
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Size - A Size equal to this PointBase
|
||||
/// </returns>
|
||||
/// <param name="point"> PointBase - the PointBase to convert to a Size </param>
|
||||
public static explicit operator SizeBase(PointBase point)
|
||||
{
|
||||
return new SizeBase(Math.Abs(point._x), Math.Abs(point._y));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Explicit conversion to Vector
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Vector - A Vector equal to this PointBase
|
||||
/// </returns>
|
||||
/// <param name="point"> PointBase - the PointBase to convert to a Vector </param>
|
||||
public static explicit operator VectorBase(PointBase point)
|
||||
{
|
||||
return new VectorBase(point._x, point._y);
|
||||
}
|
||||
|
||||
#endregion Public Methods
|
||||
}
|
||||
}
|
||||
1075
AIStudio.Wpf.DiagramDesigner/Geometry/RectangleBase.cs
Normal file
1075
AIStudio.Wpf.DiagramDesigner/Geometry/RectangleBase.cs
Normal file
File diff suppressed because it is too large
Load Diff
28
AIStudio.Wpf.DiagramDesigner/Geometry/Shapes.cs
Normal file
28
AIStudio.Wpf.DiagramDesigner/Geometry/Shapes.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using AIStudio.Wpf.DiagramDesigner.Models;
|
||||
|
||||
namespace AIStudio.Wpf.DiagramDesigner.Geometry
|
||||
{
|
||||
public delegate IShape ShapeDefiner(DesignerItemViewModelBase node);
|
||||
|
||||
public static class Shapes
|
||||
{
|
||||
public static IShape Rectangle(DesignerItemViewModelBase node) => new RectangleBase(node.Position, node.Size);
|
||||
|
||||
public static IShape Circle(DesignerItemViewModelBase node)
|
||||
{
|
||||
var halfWidth = node.Size.Width / 2;
|
||||
var centerX = node.Position.X + halfWidth;
|
||||
var centerY = node.Position.Y + node.Size.Height / 2;
|
||||
return new EllipseBase(centerX, centerY, halfWidth, halfWidth);
|
||||
}
|
||||
|
||||
public static IShape Ellipse(DesignerItemViewModelBase node)
|
||||
{
|
||||
var halfWidth = node.Size.Width / 2;
|
||||
var halfHeight = node.Size.Height / 2;
|
||||
var centerX = node.Position.X + halfWidth;
|
||||
var centerY = node.Position.Y + halfHeight;
|
||||
return new EllipseBase(centerX, centerY, halfWidth, halfHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
385
AIStudio.Wpf.DiagramDesigner/Geometry/SizeBase.cs
Normal file
385
AIStudio.Wpf.DiagramDesigner/Geometry/SizeBase.cs
Normal file
@@ -0,0 +1,385 @@
|
||||
using System;
|
||||
using System.Windows;
|
||||
|
||||
namespace AIStudio.Wpf.DiagramDesigner.Geometry
|
||||
{
|
||||
[Serializable]
|
||||
public struct SizeBase : IFormattable
|
||||
{
|
||||
public static SizeBase Zero { get; } = new SizeBase(0, 0);
|
||||
|
||||
public SizeBase(double width, double height)
|
||||
{
|
||||
if (width < 0 || height < 0)
|
||||
{
|
||||
throw new System.ArgumentException("Size_WidthAndHeightCannotBeNegative");
|
||||
}
|
||||
|
||||
_width = width;
|
||||
_height = height;
|
||||
}
|
||||
|
||||
public bool IsEmpty
|
||||
{
|
||||
get
|
||||
{
|
||||
return _width < 0;
|
||||
}
|
||||
}
|
||||
|
||||
internal double _width;
|
||||
|
||||
public double Width
|
||||
{
|
||||
get
|
||||
{
|
||||
return _width;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (IsEmpty)
|
||||
{
|
||||
throw new System.InvalidOperationException("Size_CannotModifyEmptySize");
|
||||
}
|
||||
|
||||
if (value < 0)
|
||||
{
|
||||
throw new System.ArgumentException("Size_WidthCannotBeNegative");
|
||||
}
|
||||
|
||||
_width = value;
|
||||
}
|
||||
}
|
||||
|
||||
internal double _height;
|
||||
public double Height
|
||||
{
|
||||
get
|
||||
{
|
||||
return _height;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (IsEmpty)
|
||||
{
|
||||
throw new System.InvalidOperationException("Size_CannotModifyEmptySize");
|
||||
}
|
||||
|
||||
if (value < 0)
|
||||
{
|
||||
throw new System.ArgumentException("Size_HeightCannotBeNegative");
|
||||
}
|
||||
|
||||
_height = value;
|
||||
}
|
||||
}
|
||||
|
||||
public SizeBase Add(double value) => new SizeBase(Width + value, Height + value);
|
||||
|
||||
//public bool Equals(Size size) => size != null && Width == size.Width && Height == size.Height;
|
||||
|
||||
//public override string ToString() => $"Size(width={Width}, height={Height})";
|
||||
|
||||
public static implicit operator SizeBase(Size size)
|
||||
{
|
||||
return new SizeBase(size.Width, size.Height);
|
||||
}
|
||||
|
||||
public static implicit operator Size(SizeBase sizebase)
|
||||
{
|
||||
return new Size(sizebase.Width, sizebase.Height);
|
||||
}
|
||||
|
||||
#region Statics
|
||||
|
||||
/// <summary>
|
||||
/// Empty - a static property which provides an Empty size. Width and Height are
|
||||
/// negative-infinity. This is the only situation
|
||||
/// where size can be negative.
|
||||
/// </summary>
|
||||
public static SizeBase Empty
|
||||
{
|
||||
get
|
||||
{
|
||||
return s_empty;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Statics
|
||||
|
||||
#region Public Methods
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Compares two Size instances for exact equality.
|
||||
/// Note that double values can acquire error when operated upon, such that
|
||||
/// an exact comparison between two values which are logically equal may fail.
|
||||
/// Furthermore, using this equality operator, Double.NaN is not equal to itself.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// bool - true if the two Size instances are exactly equal, false otherwise
|
||||
/// </returns>
|
||||
/// <param name='size1'>The first Size to compare</param>
|
||||
/// <param name='size2'>The second Size to compare</param>
|
||||
public static bool operator ==(SizeBase size1, SizeBase size2)
|
||||
{
|
||||
return size1.Width == size2.Width &&
|
||||
size1.Height == size2.Height;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two Size instances for exact inequality.
|
||||
/// Note that double values can acquire error when operated upon, such that
|
||||
/// an exact comparison between two values which are logically equal may fail.
|
||||
/// Furthermore, using this equality operator, Double.NaN is not equal to itself.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// bool - true if the two Size instances are exactly unequal, false otherwise
|
||||
/// </returns>
|
||||
/// <param name='size1'>The first Size to compare</param>
|
||||
/// <param name='size2'>The second Size to compare</param>
|
||||
public static bool operator !=(SizeBase size1, SizeBase size2)
|
||||
{
|
||||
return !(size1 == size2);
|
||||
}
|
||||
/// <summary>
|
||||
/// Compares two Size instances for object equality. In this equality
|
||||
/// Double.NaN is equal to itself, unlike in numeric equality.
|
||||
/// Note that double values can acquire error when operated upon, such that
|
||||
/// an exact comparison between two values which
|
||||
/// are logically equal may fail.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// bool - true if the two Size instances are exactly equal, false otherwise
|
||||
/// </returns>
|
||||
/// <param name='size1'>The first Size to compare</param>
|
||||
/// <param name='size2'>The second Size to compare</param>
|
||||
public static bool Equals(SizeBase size1, SizeBase size2)
|
||||
{
|
||||
if (size1.IsEmpty)
|
||||
{
|
||||
return size2.IsEmpty;
|
||||
}
|
||||
else
|
||||
{
|
||||
return size1.Width.Equals(size2.Width) &&
|
||||
size1.Height.Equals(size2.Height);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Equals - compares this Size with the passed in object. In this equality
|
||||
/// Double.NaN is equal to itself, unlike in numeric equality.
|
||||
/// Note that double values can acquire error when operated upon, such that
|
||||
/// an exact comparison between two values which
|
||||
/// are logically equal may fail.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// bool - true if the object is an instance of Size and if it's equal to "this".
|
||||
/// </returns>
|
||||
/// <param name='o'>The object to compare to "this"</param>
|
||||
public override bool Equals(object o)
|
||||
{
|
||||
if ((null == o) || !(o is SizeBase))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
SizeBase value = (SizeBase)o;
|
||||
return SizeBase.Equals(this, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Equals - compares this Size with the passed in object. In this equality
|
||||
/// Double.NaN is equal to itself, unlike in numeric equality.
|
||||
/// Note that double values can acquire error when operated upon, such that
|
||||
/// an exact comparison between two values which
|
||||
/// are logically equal may fail.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// bool - true if "value" is equal to "this".
|
||||
/// </returns>
|
||||
/// <param name='value'>The Size to compare to "this"</param>
|
||||
public bool Equals(SizeBase value)
|
||||
{
|
||||
return SizeBase.Equals(this, value);
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns the HashCode for this Size
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// int - the HashCode for this Size
|
||||
/// </returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
if (IsEmpty)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Perform field-by-field XOR of HashCodes
|
||||
return Width.GetHashCode() ^
|
||||
Height.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse - returns an instance converted from the provided string using
|
||||
/// the culture "en-US"
|
||||
/// <param name="source"> string with Size data </param>
|
||||
/// </summary>
|
||||
//public static Size Parse(string source)
|
||||
//{
|
||||
// IFormatProvider formatProvider = System.Windows.Markup.TypeConverterHelper.InvariantEnglishUS;
|
||||
|
||||
// TokenizerHelper th = new TokenizerHelper(source, formatProvider);
|
||||
|
||||
// Size value;
|
||||
|
||||
// String firstToken = th.NextTokenRequired();
|
||||
|
||||
// // The token will already have had whitespace trimmed so we can do a
|
||||
// // simple string compare.
|
||||
// if (firstToken == "Empty")
|
||||
// {
|
||||
// value = Empty;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// value = new Size(
|
||||
// Convert.ToDouble(firstToken, formatProvider),
|
||||
// Convert.ToDouble(th.NextTokenRequired(), formatProvider));
|
||||
// }
|
||||
|
||||
// // There should be no more tokens in this string.
|
||||
// th.LastTokenRequired();
|
||||
|
||||
// return value;
|
||||
//}
|
||||
|
||||
#endregion Public Methods
|
||||
|
||||
#region Public Operators
|
||||
|
||||
/// <summary>
|
||||
/// Explicit conversion to Vector.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Vector - A Vector equal to this Size
|
||||
/// </returns>
|
||||
/// <param name="size"> Size - the Size to convert to a Vector </param>
|
||||
public static explicit operator VectorBase(SizeBase size)
|
||||
{
|
||||
return new VectorBase(size._width, size._height);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Explicit conversion to PointBase
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// PointBase - A PointBase equal to this Size
|
||||
/// </returns>
|
||||
/// <param name="size"> Size - the Size to convert to a PointBase </param>
|
||||
public static explicit operator PointBase(SizeBase size)
|
||||
{
|
||||
return new PointBase(size._width, size._height);
|
||||
}
|
||||
|
||||
#endregion Public Operators
|
||||
|
||||
#region Private Methods
|
||||
|
||||
static private SizeBase CreateEmptySize()
|
||||
{
|
||||
SizeBase size = new SizeBase();
|
||||
// We can't set these via the property setters because negatives widths
|
||||
// are rejected in those APIs.
|
||||
size._width = Double.NegativeInfinity;
|
||||
size._height = Double.NegativeInfinity;
|
||||
return size;
|
||||
}
|
||||
|
||||
#endregion Private Methods
|
||||
|
||||
#region Private Fields
|
||||
|
||||
private readonly static SizeBase s_empty = CreateEmptySize();
|
||||
|
||||
#endregion Private Fields
|
||||
|
||||
#region Internal Properties
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates a string representation of this object based on the current culture.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A string representation of this object.
|
||||
/// </returns>
|
||||
public override string ToString()
|
||||
{
|
||||
// Delegate to the internal method which implements all ToString calls.
|
||||
return ConvertToString(null /* format string */, null /* format provider */);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a string representation of this object based on the IFormatProvider
|
||||
/// passed in. If the provider is null, the CurrentCulture is used.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A string representation of this object.
|
||||
/// </returns>
|
||||
public string ToString(IFormatProvider provider)
|
||||
{
|
||||
// Delegate to the internal method which implements all ToString calls.
|
||||
return ConvertToString(null /* format string */, provider);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a string representation of this object based on the format string
|
||||
/// and IFormatProvider passed in.
|
||||
/// If the provider is null, the CurrentCulture is used.
|
||||
/// See the documentation for IFormattable for more information.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A string representation of this object.
|
||||
/// </returns>
|
||||
string IFormattable.ToString(string format, IFormatProvider provider)
|
||||
{
|
||||
// Delegate to the internal method which implements all ToString calls.
|
||||
return ConvertToString(format, provider);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a string representation of this object based on the format string
|
||||
/// and IFormatProvider passed in.
|
||||
/// If the provider is null, the CurrentCulture is used.
|
||||
/// See the documentation for IFormattable for more information.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A string representation of this object.
|
||||
/// </returns>
|
||||
internal string ConvertToString(string format, IFormatProvider provider)
|
||||
{
|
||||
if (IsEmpty)
|
||||
{
|
||||
return "Empty";
|
||||
}
|
||||
|
||||
// Helper to get the numeric list separator for a given culture.
|
||||
char separator = ',';
|
||||
return String.Format(provider,
|
||||
"{1:" + format + "}{0}{2:" + format + "}",
|
||||
separator,
|
||||
_width,
|
||||
_height);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endregion Internal Properties
|
||||
}
|
||||
}
|
||||
618
AIStudio.Wpf.DiagramDesigner/Geometry/VectorBase.cs
Normal file
618
AIStudio.Wpf.DiagramDesigner/Geometry/VectorBase.cs
Normal file
@@ -0,0 +1,618 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace AIStudio.Wpf.DiagramDesigner.Geometry
|
||||
{
|
||||
[Serializable]
|
||||
public struct VectorBase : IFormattable
|
||||
{
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Constructor which sets the vector's initial values
|
||||
/// </summary>
|
||||
/// <param name="x"> double - The initial X </param>
|
||||
/// <param name="y"> double - THe initial Y </param>
|
||||
public VectorBase(double x, double y)
|
||||
{
|
||||
_x = x;
|
||||
_y = y;
|
||||
}
|
||||
|
||||
#endregion Constructors
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Length Property - the length of this Vector
|
||||
/// </summary>
|
||||
public double Length
|
||||
{
|
||||
get
|
||||
{
|
||||
return Math.Sqrt(_x * _x + _y * _y);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LengthSquared Property - the squared length of this Vector
|
||||
/// </summary>
|
||||
public double LengthSquared
|
||||
{
|
||||
get
|
||||
{
|
||||
return _x * _x + _y * _y;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Normalize - Updates this Vector to maintain its direction, but to have a length
|
||||
/// of 1. This is equivalent to dividing this Vector by Length
|
||||
/// </summary>
|
||||
public void Normalize()
|
||||
{
|
||||
// Avoid overflow
|
||||
this /= Math.Max(Math.Abs(_x), Math.Abs(_y));
|
||||
this /= Length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// CrossProduct - Returns the cross product: vector1.X*vector2.Y - vector1.Y*vector2.X
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Returns the cross product: vector1.X*vector2.Y - vector1.Y*vector2.X
|
||||
/// </returns>
|
||||
/// <param name="vector1"> The first Vector </param>
|
||||
/// <param name="vector2"> The second Vector </param>
|
||||
public static double CrossProduct(VectorBase vector1, VectorBase vector2)
|
||||
{
|
||||
return vector1._x * vector2._y - vector1._y * vector2._x;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// AngleBetween - the angle between 2 vectors
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Returns the the angle in degrees between vector1 and vector2
|
||||
/// </returns>
|
||||
/// <param name="vector1"> The first Vector </param>
|
||||
/// <param name="vector2"> The second Vector </param>
|
||||
public static double AngleBetween(VectorBase vector1, VectorBase vector2)
|
||||
{
|
||||
double sin = vector1._x * vector2._y - vector2._x * vector1._y;
|
||||
double cos = vector1._x * vector2._x + vector1._y * vector2._y;
|
||||
|
||||
return Math.Atan2(sin, cos) * (180 / Math.PI);
|
||||
}
|
||||
|
||||
#endregion Public Methods
|
||||
|
||||
#region Public Operators
|
||||
/// <summary>
|
||||
/// Operator -Vector (unary negation)
|
||||
/// </summary>
|
||||
public static VectorBase operator -(VectorBase vector)
|
||||
{
|
||||
return new VectorBase(-vector._x, -vector._y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Negates the values of X and Y on this Vector
|
||||
/// </summary>
|
||||
public void Negate()
|
||||
{
|
||||
_x = -_x;
|
||||
_y = -_y;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Operator Vector + Vector
|
||||
/// </summary>
|
||||
public static VectorBase operator +(VectorBase vector1, VectorBase vector2)
|
||||
{
|
||||
return new VectorBase(vector1._x + vector2._x,
|
||||
vector1._y + vector2._y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add: Vector + Vector
|
||||
/// </summary>
|
||||
public static VectorBase Add(VectorBase vector1, VectorBase vector2)
|
||||
{
|
||||
return new VectorBase(vector1._x + vector2._x,
|
||||
vector1._y + vector2._y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Operator Vector - Vector
|
||||
/// </summary>
|
||||
public static VectorBase operator -(VectorBase vector1, VectorBase vector2)
|
||||
{
|
||||
return new VectorBase(vector1._x - vector2._x,
|
||||
vector1._y - vector2._y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subtract: Vector - Vector
|
||||
/// </summary>
|
||||
public static VectorBase Subtract(VectorBase vector1, VectorBase vector2)
|
||||
{
|
||||
return new VectorBase(vector1._x - vector2._x,
|
||||
vector1._y - vector2._y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Operator Vector + PointBase
|
||||
/// </summary>
|
||||
public static PointBase operator +(VectorBase vector, PointBase point)
|
||||
{
|
||||
return new PointBase(point._x + vector._x, point._y + vector._y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add: Vector + PointBase
|
||||
/// </summary>
|
||||
public static PointBase Add(VectorBase vector, PointBase point)
|
||||
{
|
||||
return new PointBase(point._x + vector._x, point._y + vector._y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Operator Vector * double
|
||||
/// </summary>
|
||||
public static VectorBase operator *(VectorBase vector, double scalar)
|
||||
{
|
||||
return new VectorBase(vector._x * scalar,
|
||||
vector._y * scalar);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiply: Vector * double
|
||||
/// </summary>
|
||||
public static VectorBase Multiply(VectorBase vector, double scalar)
|
||||
{
|
||||
return new VectorBase(vector._x * scalar,
|
||||
vector._y * scalar);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Operator double * Vector
|
||||
/// </summary>
|
||||
public static VectorBase operator *(double scalar, VectorBase vector)
|
||||
{
|
||||
return new VectorBase(vector._x * scalar,
|
||||
vector._y * scalar);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiply: double * Vector
|
||||
/// </summary>
|
||||
public static VectorBase Multiply(double scalar, VectorBase vector)
|
||||
{
|
||||
return new VectorBase(vector._x * scalar,
|
||||
vector._y * scalar);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Operator Vector / double
|
||||
/// </summary>
|
||||
public static VectorBase operator /(VectorBase vector, double scalar)
|
||||
{
|
||||
return vector * (1.0 / scalar);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiply: Vector / double
|
||||
/// </summary>
|
||||
public static VectorBase Divide(VectorBase vector, double scalar)
|
||||
{
|
||||
return vector * (1.0 / scalar);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Operator Vector * Matrix
|
||||
/// </summary>
|
||||
//public static Vector operator *(Vector vector, Matrix matrix)
|
||||
//{
|
||||
// return matrix.Transform(vector);
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// Multiply: Vector * Matrix
|
||||
/// </summary>
|
||||
//public static Vector Multiply(Vector vector, Matrix matrix)
|
||||
//{
|
||||
// return matrix.Transform(vector);
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// Operator Vector * Vector, interpreted as their dot product
|
||||
/// </summary>
|
||||
public static double operator *(VectorBase vector1, VectorBase vector2)
|
||||
{
|
||||
return vector1._x * vector2._x + vector1._y * vector2._y;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiply - Returns the dot product: vector1.X*vector2.X + vector1.Y*vector2.Y
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Returns the dot product: vector1.X*vector2.X + vector1.Y*vector2.Y
|
||||
/// </returns>
|
||||
/// <param name="vector1"> The first Vector </param>
|
||||
/// <param name="vector2"> The second Vector </param>
|
||||
public static double Multiply(VectorBase vector1, VectorBase vector2)
|
||||
{
|
||||
return vector1._x * vector2._x + vector1._y * vector2._y;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determinant - Returns the determinant det(vector1, vector2)
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Returns the determinant: vector1.X*vector2.Y - vector1.Y*vector2.X
|
||||
/// </returns>
|
||||
/// <param name="vector1"> The first Vector </param>
|
||||
/// <param name="vector2"> The second Vector </param>
|
||||
public static double Determinant(VectorBase vector1, VectorBase vector2)
|
||||
{
|
||||
return vector1._x * vector2._y - vector1._y * vector2._x;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Explicit conversion to Size. Note that since Size cannot contain negative values,
|
||||
/// the resulting size will contains the absolute values of X and Y
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Size - A Size equal to this Vector
|
||||
/// </returns>
|
||||
/// <param name="vector"> Vector - the Vector to convert to a Size </param>
|
||||
public static explicit operator SizeBase(VectorBase vector)
|
||||
{
|
||||
return new SizeBase(Math.Abs(vector._x), Math.Abs(vector._y));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Explicit conversion to PointBase
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// PointBase - A PointBase equal to this Vector
|
||||
/// </returns>
|
||||
/// <param name="vector"> Vector - the Vector to convert to a PointBase </param>
|
||||
public static explicit operator PointBase(VectorBase vector)
|
||||
{
|
||||
return new PointBase(vector._x, vector._y);
|
||||
}
|
||||
#endregion Public Operators
|
||||
|
||||
//------------------------------------------------------
|
||||
//
|
||||
// Public Methods
|
||||
//
|
||||
//------------------------------------------------------
|
||||
|
||||
#region Public Methods
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Compares two Vector instances for exact equality.
|
||||
/// Note that double values can acquire error when operated upon, such that
|
||||
/// an exact comparison between two values which are logically equal may fail.
|
||||
/// Furthermore, using this equality operator, Double.NaN is not equal to itself.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// bool - true if the two Vector instances are exactly equal, false otherwise
|
||||
/// </returns>
|
||||
/// <param name='vector1'>The first Vector to compare</param>
|
||||
/// <param name='vector2'>The second Vector to compare</param>
|
||||
public static bool operator ==(VectorBase vector1, VectorBase vector2)
|
||||
{
|
||||
return vector1.X == vector2.X &&
|
||||
vector1.Y == vector2.Y;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two Vector instances for exact inequality.
|
||||
/// Note that double values can acquire error when operated upon, such that
|
||||
/// an exact comparison between two values which are logically equal may fail.
|
||||
/// Furthermore, using this equality operator, Double.NaN is not equal to itself.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// bool - true if the two Vector instances are exactly unequal, false otherwise
|
||||
/// </returns>
|
||||
/// <param name='vector1'>The first Vector to compare</param>
|
||||
/// <param name='vector2'>The second Vector to compare</param>
|
||||
public static bool operator !=(VectorBase vector1, VectorBase vector2)
|
||||
{
|
||||
return !(vector1 == vector2);
|
||||
}
|
||||
/// <summary>
|
||||
/// Compares two Vector instances for object equality. In this equality
|
||||
/// Double.NaN is equal to itself, unlike in numeric equality.
|
||||
/// Note that double values can acquire error when operated upon, such that
|
||||
/// an exact comparison between two values which
|
||||
/// are logically equal may fail.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// bool - true if the two Vector instances are exactly equal, false otherwise
|
||||
/// </returns>
|
||||
/// <param name='vector1'>The first Vector to compare</param>
|
||||
/// <param name='vector2'>The second Vector to compare</param>
|
||||
public static bool Equals(VectorBase vector1, VectorBase vector2)
|
||||
{
|
||||
return vector1.X.Equals(vector2.X) &&
|
||||
vector1.Y.Equals(vector2.Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Equals - compares this Vector with the passed in object. In this equality
|
||||
/// Double.NaN is equal to itself, unlike in numeric equality.
|
||||
/// Note that double values can acquire error when operated upon, such that
|
||||
/// an exact comparison between two values which
|
||||
/// are logically equal may fail.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// bool - true if the object is an instance of Vector and if it's equal to "this".
|
||||
/// </returns>
|
||||
/// <param name='o'>The object to compare to "this"</param>
|
||||
public override bool Equals(object o)
|
||||
{
|
||||
if ((null == o) || !(o is VectorBase))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
VectorBase value = (VectorBase)o;
|
||||
return VectorBase.Equals(this, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Equals - compares this Vector with the passed in object. In this equality
|
||||
/// Double.NaN is equal to itself, unlike in numeric equality.
|
||||
/// Note that double values can acquire error when operated upon, such that
|
||||
/// an exact comparison between two values which
|
||||
/// are logically equal may fail.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// bool - true if "value" is equal to "this".
|
||||
/// </returns>
|
||||
/// <param name='value'>The Vector to compare to "this"</param>
|
||||
public bool Equals(VectorBase value)
|
||||
{
|
||||
return VectorBase.Equals(this, value);
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns the HashCode for this Vector
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// int - the HashCode for this Vector
|
||||
/// </returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
// Perform field-by-field XOR of HashCodes
|
||||
return X.GetHashCode() ^
|
||||
Y.GetHashCode();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse - returns an instance converted from the provided string using
|
||||
/// the culture "en-US"
|
||||
/// <param name="source"> string with Vector data </param>
|
||||
/// </summary>
|
||||
//public static Vector Parse(string source)
|
||||
//{
|
||||
// IFormatProvider formatProvider = System.Windows.Markup.TypeConverterHelper.InvariantEnglishUS;
|
||||
|
||||
// TokenizerHelper th = new TokenizerHelper(source, formatProvider);
|
||||
|
||||
// Vector value;
|
||||
|
||||
// String firstToken = th.NextTokenRequired();
|
||||
|
||||
// value = new Vector(
|
||||
// Convert.ToDouble(firstToken, formatProvider),
|
||||
// Convert.ToDouble(th.NextTokenRequired(), formatProvider));
|
||||
|
||||
// // There should be no more tokens in this string.
|
||||
// th.LastTokenRequired();
|
||||
|
||||
// return value;
|
||||
//}
|
||||
|
||||
#endregion Public Methods
|
||||
|
||||
//------------------------------------------------------
|
||||
//
|
||||
// Public Properties
|
||||
//
|
||||
//------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// X - double. Default value is 0.
|
||||
/// </summary>
|
||||
public double X
|
||||
{
|
||||
get
|
||||
{
|
||||
return _x;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
_x = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Y - double. Default value is 0.
|
||||
/// </summary>
|
||||
public double Y
|
||||
{
|
||||
get
|
||||
{
|
||||
return _y;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
_y = value;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Public Properties
|
||||
|
||||
//------------------------------------------------------
|
||||
//
|
||||
// Protected Methods
|
||||
//
|
||||
//------------------------------------------------------
|
||||
|
||||
#region Protected Methods
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endregion ProtectedMethods
|
||||
|
||||
//------------------------------------------------------
|
||||
//
|
||||
// Internal Methods
|
||||
//
|
||||
//------------------------------------------------------
|
||||
|
||||
#region Internal Methods
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endregion Internal Methods
|
||||
|
||||
//------------------------------------------------------
|
||||
//
|
||||
// Internal Properties
|
||||
//
|
||||
//------------------------------------------------------
|
||||
|
||||
#region Internal Properties
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates a string representation of this object based on the current culture.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A string representation of this object.
|
||||
/// </returns>
|
||||
public override string ToString()
|
||||
{
|
||||
// Delegate to the internal method which implements all ToString calls.
|
||||
return ConvertToString(null /* format string */, null /* format provider */);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a string representation of this object based on the IFormatProvider
|
||||
/// passed in. If the provider is null, the CurrentCulture is used.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A string representation of this object.
|
||||
/// </returns>
|
||||
public string ToString(IFormatProvider provider)
|
||||
{
|
||||
// Delegate to the internal method which implements all ToString calls.
|
||||
return ConvertToString(null /* format string */, provider);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a string representation of this object based on the format string
|
||||
/// and IFormatProvider passed in.
|
||||
/// If the provider is null, the CurrentCulture is used.
|
||||
/// See the documentation for IFormattable for more information.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A string representation of this object.
|
||||
/// </returns>
|
||||
string IFormattable.ToString(string format, IFormatProvider provider)
|
||||
{
|
||||
// Delegate to the internal method which implements all ToString calls.
|
||||
return ConvertToString(format, provider);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a string representation of this object based on the format string
|
||||
/// and IFormatProvider passed in.
|
||||
/// If the provider is null, the CurrentCulture is used.
|
||||
/// See the documentation for IFormattable for more information.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A string representation of this object.
|
||||
/// </returns>
|
||||
internal string ConvertToString(string format, IFormatProvider provider)
|
||||
{
|
||||
// Helper to get the numeric list separator for a given culture.
|
||||
char separator = ',';
|
||||
return String.Format(provider,
|
||||
"{1:" + format + "}{0}{2:" + format + "}",
|
||||
separator,
|
||||
_x,
|
||||
_y);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endregion Internal Properties
|
||||
|
||||
//------------------------------------------------------
|
||||
//
|
||||
// Dependency Properties
|
||||
//
|
||||
//------------------------------------------------------
|
||||
|
||||
#region Dependency Properties
|
||||
|
||||
|
||||
|
||||
#endregion Dependency Properties
|
||||
|
||||
//------------------------------------------------------
|
||||
//
|
||||
// Internal Fields
|
||||
//
|
||||
//------------------------------------------------------
|
||||
|
||||
#region Internal Fields
|
||||
|
||||
|
||||
internal double _x;
|
||||
internal double _y;
|
||||
|
||||
#endregion Internal Fields
|
||||
|
||||
|
||||
|
||||
#region Constructors
|
||||
|
||||
//------------------------------------------------------
|
||||
//
|
||||
// Constructors
|
||||
//
|
||||
//------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
|
||||
#endregion Constructors
|
||||
}
|
||||
}
|
||||
10
AIStudio.Wpf.DiagramDesigner/Helpers/DoubleExtensions.cs
Normal file
10
AIStudio.Wpf.DiagramDesigner/Helpers/DoubleExtensions.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using System;
|
||||
|
||||
namespace AIStudio.Wpf.DiagramDesigner
|
||||
{
|
||||
public static class DoubleExtensions
|
||||
{
|
||||
public static bool AlmostEqualTo(this double double1, double double2, double tolerance = 0.0001)
|
||||
=> Math.Abs(double1 - double2) < tolerance;
|
||||
}
|
||||
}
|
||||
9
AIStudio.Wpf.DiagramDesigner/Helpers/NumberExtensions.cs
Normal file
9
AIStudio.Wpf.DiagramDesigner/Helpers/NumberExtensions.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using System.Globalization;
|
||||
|
||||
namespace AIStudio.Wpf.DiagramDesigner
|
||||
{
|
||||
public static class NumberExtensions
|
||||
{
|
||||
public static string ToInvariantString(this double n) => n.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
using AIStudio.Wpf.DiagramDesigner.Geometry;
|
||||
|
||||
namespace AIStudio.Wpf.DiagramDesigner
|
||||
{
|
||||
public class PointHelper
|
||||
{
|
||||
public static Point GetPointForConnector(FullyCreatedConnectorInfo connector)
|
||||
public static PointBase GetPointForConnector(FullyCreatedConnectorInfo connector)
|
||||
{
|
||||
Point point = new Point();
|
||||
PointBase point = new PointBase();
|
||||
if (connector == null)
|
||||
{
|
||||
return point;
|
||||
}
|
||||
|
||||
if (connector.IsInnerPoint)
|
||||
{
|
||||
point = new Point(connector.DataItem.Left + connector.DataItem.ItemWidth * connector.XRatio,
|
||||
point = new PointBase(connector.DataItem.Left + connector.DataItem.ItemWidth * connector.XRatio,
|
||||
connector.DataItem.Top + connector.DataItem.ItemHeight * connector.YRatio);
|
||||
}
|
||||
else
|
||||
@@ -26,16 +23,16 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
switch (connector.Orientation)
|
||||
{
|
||||
case ConnectorOrientation.Top:
|
||||
point = new Point(connector.DataItem.Left + (connector.DataItem.ItemWidth / 2), connector.DataItem.Top);
|
||||
point = new PointBase(connector.DataItem.Left + (connector.DataItem.ItemWidth / 2), connector.DataItem.Top);
|
||||
break;
|
||||
case ConnectorOrientation.Bottom:
|
||||
point = new Point(connector.DataItem.Left + (connector.DataItem.ItemWidth / 2), (connector.DataItem.Top + connector.DataItem.ItemHeight));
|
||||
point = new PointBase(connector.DataItem.Left + (connector.DataItem.ItemWidth / 2), (connector.DataItem.Top + connector.DataItem.ItemHeight));
|
||||
break;
|
||||
case ConnectorOrientation.Right:
|
||||
point = new Point(connector.DataItem.Left + connector.DataItem.ItemWidth, connector.DataItem.Top + (connector.DataItem.ItemHeight / 2));
|
||||
point = new PointBase(connector.DataItem.Left + connector.DataItem.ItemWidth, connector.DataItem.Top + (connector.DataItem.ItemHeight / 2));
|
||||
break;
|
||||
case ConnectorOrientation.Left:
|
||||
point = new Point(connector.DataItem.Left, connector.DataItem.Top + (connector.DataItem.ItemHeight / 2));
|
||||
point = new PointBase(connector.DataItem.Left, connector.DataItem.Top + (connector.DataItem.ItemHeight / 2));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Windows;
|
||||
using AIStudio.Wpf.DiagramDesigner.Geometry;
|
||||
|
||||
namespace AIStudio.Wpf.DiagramDesigner
|
||||
{
|
||||
@@ -10,8 +10,8 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
{
|
||||
public double DesignerItemLeft { get; set; }
|
||||
public double DesignerItemTop { get; set; }
|
||||
public Size DesignerItemSize { get; set; }
|
||||
public Point Position { get; set; }
|
||||
public SizeBase DesignerItemSize { get; set; }
|
||||
public PointBase Position { get; set; }
|
||||
public ConnectorOrientation Orientation { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Windows;
|
||||
using AIStudio.Wpf.DiagramDesigner.Geometry;
|
||||
|
||||
namespace AIStudio.Wpf.DiagramDesigner
|
||||
{
|
||||
public class BoundaryPathFinder : IPathFinder
|
||||
{
|
||||
public List<PointInfoBase> UpdateConnectionPoints(IDiagramViewModel diagramViewModel, Point sourceA, Point sourceB, FullyCreatedConnectorInfo sourceConnectorInfo, ConnectorInfoBase sinkConnectorInfo)
|
||||
public List<ConnectorPoint> UpdateConnectionPoints(IDiagramViewModel diagramViewModel, PointBase sourceA, PointBase sourceB, FullyCreatedConnectorInfo sourceConnectorInfo, ConnectorInfoBase sinkConnectorInfo)
|
||||
{
|
||||
List<PointInfoBase> connectionPoints;
|
||||
List<ConnectorPoint> connectionPoints;
|
||||
var isFullConnection = sinkConnectorInfo is FullyCreatedConnectorInfo;
|
||||
|
||||
var points = new List<Point>()
|
||||
var points = new List<PointBase>()
|
||||
{
|
||||
sourceA,
|
||||
sourceB
|
||||
@@ -38,34 +37,34 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
((FullyCreatedConnectorInfo)sinkConnectorInfo).DataItem.ItemHeight,
|
||||
points[1]);
|
||||
|
||||
connectionPoints = PointInfoBase.ToList(GetConnectionLine(diagramViewModel, sourceInfo, sinkInfo, false, sourceConnectorInfo.IsInnerPoint));
|
||||
connectionPoints = ConnectorPoint.ToList(GetConnectionLine(diagramViewModel, sourceInfo, sinkInfo, false, sourceConnectorInfo.IsInnerPoint));
|
||||
//EndPoint = ConnectionPoints.Last();
|
||||
}
|
||||
else
|
||||
{
|
||||
connectionPoints = PointInfoBase.ToList(GetConnectionLine(diagramViewModel, sourceInfo, points[1], sourceConnectorInfo.Orientation, false, sourceConnectorInfo.IsInnerPoint));
|
||||
//EndPoint = new Point();
|
||||
connectionPoints = ConnectorPoint.ToList(GetConnectionLine(diagramViewModel, sourceInfo, points[1], sourceConnectorInfo.Orientation, false, sourceConnectorInfo.IsInnerPoint));
|
||||
//EndPoint = new PointBase();
|
||||
}
|
||||
|
||||
return connectionPoints;
|
||||
}
|
||||
|
||||
public ConnectorInfo ConnectorInfo(ConnectorOrientation orientation, double left, double top, double width, double height, Point position)
|
||||
public ConnectorInfo ConnectorInfo(ConnectorOrientation orientation, double left, double top, double width, double height, PointBase position)
|
||||
{
|
||||
return new ConnectorInfo()
|
||||
{
|
||||
Orientation = orientation,
|
||||
DesignerItemSize = new Size(width, height),
|
||||
DesignerItemSize = new SizeBase(width, height),
|
||||
DesignerItemLeft = left,
|
||||
DesignerItemTop = top,
|
||||
Position = position
|
||||
};
|
||||
}
|
||||
|
||||
public List<Point> GetConnectionLine(IDiagramViewModel diagramViewModel, ConnectorInfo source, ConnectorInfo sink, bool showLastLine, bool sourceInnerPoint = false)
|
||||
public List<PointBase> GetConnectionLine(IDiagramViewModel diagramViewModel, ConnectorInfo source, ConnectorInfo sink, bool showLastLine, bool sourceInnerPoint = false)
|
||||
{
|
||||
var points = new List<Point>();
|
||||
var ends = new List<Point> { source.Position, sink.Position };
|
||||
var points = new List<PointBase>();
|
||||
var ends = new List<PointBase> { source.Position, sink.Position };
|
||||
|
||||
points.Add(ends[0]);
|
||||
points.AddRange(GetMiddlePoints(source, sink, diagramViewModel.GridCellSize, diagramViewModel.GridMargin, true));
|
||||
@@ -75,10 +74,10 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
return res.ToList();
|
||||
}
|
||||
|
||||
public List<Point> GetConnectionLine(IDiagramViewModel diagramViewModel, ConnectorInfo source, Point sinkPoint, ConnectorOrientation preferredOrientation, bool showLastLine, bool isInnerPoint = false)
|
||||
public List<PointBase> GetConnectionLine(IDiagramViewModel diagramViewModel, ConnectorInfo source, PointBase sinkPoint, ConnectorOrientation preferredOrientation, bool showLastLine, bool isInnerPoint = false)
|
||||
{
|
||||
var points = new List<Point>();
|
||||
var ends = new List<Point> { source.Position, sinkPoint };
|
||||
var points = new List<PointBase>();
|
||||
var ends = new List<PointBase> { source.Position, sinkPoint };
|
||||
|
||||
points.Add(ends[0]);
|
||||
points.AddRange(GetMiddlePoints(source, new ConnectorInfo() { Orientation = ConnectorOrientation.Top, Position = sinkPoint }, diagramViewModel.GridCellSize, diagramViewModel.GridMargin, false));
|
||||
@@ -88,9 +87,9 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
return res.ToList();
|
||||
}
|
||||
|
||||
private IEnumerable<Point> GetMiddlePoints(ConnectorInfo source, ConnectorInfo sink, Size gridCellSize, double gridMargin, bool isFullConnection)
|
||||
private IEnumerable<PointBase> GetMiddlePoints(ConnectorInfo source, ConnectorInfo sink, SizeBase gridCellSize, double gridMargin, bool isFullConnection)
|
||||
{
|
||||
var points = new List<Point>();
|
||||
var points = new List<PointBase>();
|
||||
if (isFullConnection)
|
||||
{
|
||||
var p0 = GetFirstSegment(source.Orientation, source.Position, gridCellSize, gridMargin);
|
||||
@@ -100,8 +99,8 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
return points;
|
||||
|
||||
|
||||
var p2 = new Point(GetNearestCross(p0.X, p1.X), GetNearestCross(p0.Y, p1.Y));
|
||||
var p3 = new Point(GetNearestCross(p1.X, p0.X), GetNearestCross(p1.Y, p0.Y));
|
||||
var p2 = new PointBase(GetNearestCross(p0.X, p1.X), GetNearestCross(p0.Y, p1.Y));
|
||||
var p3 = new PointBase(GetNearestCross(p1.X, p0.X), GetNearestCross(p1.Y, p0.Y));
|
||||
if (p2 == p3)
|
||||
{
|
||||
points.Add(p0);
|
||||
@@ -113,7 +112,7 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
points.Add(p0);
|
||||
points.Add(p2);
|
||||
if (!(Math.Abs(p2.X - p3.X) < 0.0001) && !(Math.Abs(p2.Y - p3.Y) < 0.0001))
|
||||
points.Add(new Point(p2.X, p3.Y));
|
||||
points.Add(new PointBase(p2.X, p3.Y));
|
||||
points.Add(p3);
|
||||
points.Add(p1);
|
||||
}
|
||||
@@ -122,18 +121,18 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
return points;
|
||||
}
|
||||
|
||||
private Point GetFirstSegment(ConnectorOrientation orientation, Point point, Size cellSize, double margin)
|
||||
private PointBase GetFirstSegment(ConnectorOrientation orientation, PointBase point, SizeBase cellSize, double margin)
|
||||
{
|
||||
double x = (int)((point.X - margin) / cellSize.Width) + 0.5;
|
||||
double y = (int)((point.Y - margin) / cellSize.Height) + 0.5;
|
||||
if (orientation == ConnectorOrientation.Top)
|
||||
return new Point(x, y - 0.5);
|
||||
return new PointBase(x, y - 0.5);
|
||||
else if (orientation == ConnectorOrientation.Bottom)
|
||||
return new Point(x, y + 0.5);
|
||||
return new PointBase(x, y + 0.5);
|
||||
else if (orientation == ConnectorOrientation.Left)
|
||||
return new Point(x - 0.5, y);
|
||||
return new PointBase(x - 0.5, y);
|
||||
else
|
||||
return new Point(x + 0.5, y);
|
||||
return new PointBase(x + 0.5, y);
|
||||
}
|
||||
|
||||
public static double GetNearestCross(double a, double b)
|
||||
@@ -146,25 +145,25 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
return Math.Floor(a);
|
||||
}
|
||||
|
||||
public static Point SegmentMiddlePoint(Point p1, Point p2)
|
||||
public static PointBase SegmentMiddlePoint(PointBase p1, PointBase p2)
|
||||
{
|
||||
return new Point((p1.X + p2.X) / 2, (p1.Y + p2.Y) / 2);
|
||||
return new PointBase((p1.X + p2.X) / 2, (p1.Y + p2.Y) / 2);
|
||||
}
|
||||
|
||||
|
||||
private void DoScale(List<Point> points, Size cellSize, double margin)
|
||||
private void DoScale(List<PointBase> points, SizeBase cellSize, double margin)
|
||||
{
|
||||
for (int i = 0; i < points.Count; i++)
|
||||
{
|
||||
points[i] = new Point(points[i].X * cellSize.Width + margin,
|
||||
points[i] = new PointBase(points[i].X * cellSize.Width + margin,
|
||||
points[i].Y * cellSize.Height + margin);
|
||||
}
|
||||
}
|
||||
|
||||
private void DoShift(Point[] points)
|
||||
private void DoShift(PointBase[] points)
|
||||
{
|
||||
double left = new Point[] { points.FirstOrDefault(), points.LastOrDefault() }.Min(p => p.X);
|
||||
double top = new Point[] { points.FirstOrDefault(), points.LastOrDefault() }.Min(p => p.Y);
|
||||
double left = new PointBase[] { points.FirstOrDefault(), points.LastOrDefault() }.Min(p => p.X);
|
||||
double top = new PointBase[] { points.FirstOrDefault(), points.LastOrDefault() }.Min(p => p.Y);
|
||||
|
||||
for (int i = 0; i < points.Length; i++)
|
||||
{
|
||||
13
AIStudio.Wpf.DiagramDesigner/PathFinder/IPathFinder.cs
Normal file
13
AIStudio.Wpf.DiagramDesigner/PathFinder/IPathFinder.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using AIStudio.Wpf.DiagramDesigner.Geometry;
|
||||
|
||||
namespace AIStudio.Wpf.DiagramDesigner
|
||||
{
|
||||
public interface IPathFinder
|
||||
{
|
||||
List<ConnectorPoint> UpdateConnectionPoints(IDiagramViewModel diagramViewModel, PointBase sourceA, PointBase sourceB, FullyCreatedConnectorInfo sourceConnectorInfo, ConnectorInfoBase sinkConnectorInfo);
|
||||
List<PointBase> GetConnectionLine(IDiagramViewModel diagramViewModel, ConnectorInfo source, ConnectorInfo sink, bool showLastLine, bool sourceInnerPoint = false);
|
||||
List<PointBase> GetConnectionLine(IDiagramViewModel diagramViewModel, ConnectorInfo source, PointBase sinkPoint, ConnectorOrientation preferredOrientation, bool showLastLine, bool isInnerPoint = false);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using AIStudio.Wpf.DiagramDesigner.Geometry;
|
||||
|
||||
namespace AIStudio.Wpf.DiagramDesigner
|
||||
{
|
||||
@@ -15,16 +15,16 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
{
|
||||
private const int const_margin = 20;
|
||||
|
||||
public List<PointInfoBase> UpdateConnectionPoints(IDiagramViewModel diagramViewModel, Point sourceA, Point sourceB, FullyCreatedConnectorInfo sourceConnectorInfo, ConnectorInfoBase sinkConnectorInfo)
|
||||
public List<ConnectorPoint> UpdateConnectionPoints(IDiagramViewModel diagramViewModel, PointBase sourceA, PointBase sourceB, FullyCreatedConnectorInfo sourceConnectorInfo, ConnectorInfoBase sinkConnectorInfo)
|
||||
{
|
||||
List<PointInfoBase> connectionPoints;
|
||||
List<ConnectorPoint> connectionPoints;
|
||||
var isFullConnection = sinkConnectorInfo is FullyCreatedConnectorInfo;
|
||||
var area = new Rect(sourceA, sourceB);
|
||||
var area = new RectangleBase(sourceA, sourceB);
|
||||
|
||||
var points = new List<Point>()
|
||||
var points = new List<PointBase>()
|
||||
{
|
||||
new Point(sourceA.X < sourceB.X ? 0d : area.Width, sourceA.Y < sourceB.Y ? 0d : area.Height ),
|
||||
new Point(sourceA.X > sourceB.X ? 0d : area.Width, sourceA.Y > sourceB.Y ? 0d : area.Height)
|
||||
new PointBase(sourceA.X < sourceB.X ? 0d : area.Width, sourceA.Y < sourceB.Y ? 0d : area.Height ),
|
||||
new PointBase(sourceA.X > sourceB.X ? 0d : area.Width, sourceA.Y > sourceB.Y ? 0d : area.Height)
|
||||
};
|
||||
|
||||
ConnectorInfo sourceInfo = ConnectorInfo(sourceConnectorInfo.Orientation,
|
||||
@@ -45,44 +45,44 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
((FullyCreatedConnectorInfo)sinkConnectorInfo).DataItem.ItemHeight,
|
||||
points[1]);
|
||||
|
||||
connectionPoints = PointInfoBase.ToList(GetConnectionLine(diagramViewModel, sourceInfo, sinkInfo, false, sourceConnectorInfo.IsInnerPoint));
|
||||
connectionPoints = ConnectorPoint.ToList(GetConnectionLine(diagramViewModel, sourceInfo, sinkInfo, false, sourceConnectorInfo.IsInnerPoint));
|
||||
//EndPoint = ConnectionPoints.Last();
|
||||
}
|
||||
else
|
||||
{
|
||||
connectionPoints = PointInfoBase.ToList(GetConnectionLine(diagramViewModel, sourceInfo, points[1], sourceConnectorInfo.Orientation, false, sourceConnectorInfo.IsInnerPoint));
|
||||
//EndPoint = new Point();
|
||||
connectionPoints = ConnectorPoint.ToList(GetConnectionLine(diagramViewModel, sourceInfo, points[1], sourceConnectorInfo.Orientation, false, sourceConnectorInfo.IsInnerPoint));
|
||||
//EndPoint = new PointBase();
|
||||
}
|
||||
|
||||
return connectionPoints;
|
||||
}
|
||||
|
||||
public ConnectorInfo ConnectorInfo(ConnectorOrientation orientation, double left, double top, double width, double height, Point position)
|
||||
public ConnectorInfo ConnectorInfo(ConnectorOrientation orientation, double left, double top, double width, double height, PointBase position)
|
||||
{
|
||||
return new ConnectorInfo()
|
||||
{
|
||||
Orientation = orientation,
|
||||
DesignerItemSize = new Size(width, height),
|
||||
DesignerItemSize = new SizeBase(width, height),
|
||||
DesignerItemLeft = left,
|
||||
DesignerItemTop = top,
|
||||
Position = position
|
||||
};
|
||||
}
|
||||
|
||||
public List<Point> GetConnectionLine(IDiagramViewModel diagramViewModel, ConnectorInfo source, ConnectorInfo sink, bool showLastLine, bool sourceInnerPoint = false)
|
||||
public List<PointBase> GetConnectionLine(IDiagramViewModel diagramViewModel, ConnectorInfo source, ConnectorInfo sink, bool showLastLine, bool sourceInnerPoint = false)
|
||||
{
|
||||
List<Point> linePoints = new List<Point>();
|
||||
List<PointBase> linePoints = new List<PointBase>();
|
||||
int margin1 = sourceInnerPoint ? 0 : const_margin;
|
||||
int margin2 = const_margin;
|
||||
|
||||
Rect rectSource = GetRectWithMargin(source, margin1);
|
||||
Rect rectSink = GetRectWithMargin(sink, margin2);
|
||||
RectangleBase rectSource = GetRectWithMargin(source, margin1);
|
||||
RectangleBase rectSink = GetRectWithMargin(sink, margin2);
|
||||
|
||||
Point startPoint = GetOffsetPoint(source, rectSource, sourceInnerPoint);
|
||||
Point endPoint = GetOffsetPoint(sink, rectSink);
|
||||
PointBase startPoint = GetOffsetPoint(source, rectSource, sourceInnerPoint);
|
||||
PointBase endPoint = GetOffsetPoint(sink, rectSink);
|
||||
|
||||
linePoints.Add(startPoint);
|
||||
Point currentPoint = startPoint;
|
||||
PointBase currentPoint = startPoint;
|
||||
|
||||
if (!rectSink.Contains(currentPoint) && !rectSource.Contains(endPoint))
|
||||
{
|
||||
@@ -90,14 +90,14 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
{
|
||||
#region source node
|
||||
|
||||
if (IsPointVisible(currentPoint, endPoint, new Rect[] { rectSource, rectSink }))
|
||||
if (IsPointVisible(currentPoint, endPoint, new RectangleBase[] { rectSource, rectSink }))
|
||||
{
|
||||
linePoints.Add(endPoint);
|
||||
currentPoint = endPoint;
|
||||
break;
|
||||
}
|
||||
|
||||
Point neighbour = GetNearestVisibleNeighborSink(currentPoint, endPoint, sink, rectSource, rectSink);
|
||||
PointBase neighbour = GetNearestVisibleNeighborSink(currentPoint, endPoint, sink, rectSource, rectSink);
|
||||
if (!double.IsNaN(neighbour.X))
|
||||
{
|
||||
linePoints.Add(neighbour);
|
||||
@@ -109,7 +109,7 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
if (currentPoint == startPoint)
|
||||
{
|
||||
bool flag;
|
||||
Point n = GetNearestNeighborSource(source, endPoint, rectSource, rectSink, out flag, sourceInnerPoint);
|
||||
PointBase n = GetNearestNeighborSource(source, endPoint, rectSource, rectSink, out flag, sourceInnerPoint);
|
||||
if (linePoints.Contains(n))
|
||||
{
|
||||
break;
|
||||
@@ -117,9 +117,9 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
linePoints.Add(n);
|
||||
currentPoint = n;
|
||||
|
||||
if (!IsRectVisible(currentPoint, rectSink, new Rect[] { rectSource }))
|
||||
if (!IsRectVisible(currentPoint, rectSink, new RectangleBase[] { rectSource }))
|
||||
{
|
||||
Point n1, n2;
|
||||
PointBase n1, n2;
|
||||
GetOppositeCorners(source.Orientation, rectSource, out n1, out n2, sourceInnerPoint);
|
||||
if (flag)
|
||||
{
|
||||
@@ -131,7 +131,7 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
linePoints.Add(n2);
|
||||
currentPoint = n2;
|
||||
}
|
||||
if (!IsRectVisible(currentPoint, rectSink, new Rect[] { rectSource }))
|
||||
if (!IsRectVisible(currentPoint, rectSink, new RectangleBase[] { rectSource }))
|
||||
{
|
||||
if (flag)
|
||||
{
|
||||
@@ -152,13 +152,13 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
|
||||
else // from here on we jump to the sink node
|
||||
{
|
||||
Point n1, n2; // neighbour corner
|
||||
Point s1, s2; // opposite corner
|
||||
PointBase n1, n2; // neighbour corner
|
||||
PointBase s1, s2; // opposite corner
|
||||
GetNeighborCorners(sink.Orientation, rectSink, out s1, out s2);
|
||||
GetOppositeCorners(sink.Orientation, rectSink, out n1, out n2);
|
||||
|
||||
bool n1Visible = IsPointVisible(currentPoint, n1, new Rect[] { rectSource, rectSink });
|
||||
bool n2Visible = IsPointVisible(currentPoint, n2, new Rect[] { rectSource, rectSink });
|
||||
bool n1Visible = IsPointVisible(currentPoint, n1, new RectangleBase[] { rectSource, rectSink });
|
||||
bool n2Visible = IsPointVisible(currentPoint, n2, new RectangleBase[] { rectSource, rectSink });
|
||||
|
||||
if (n1Visible && n2Visible)
|
||||
{
|
||||
@@ -260,48 +260,48 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
linePoints.Add(endPoint);
|
||||
}
|
||||
|
||||
linePoints = OptimizeLinePoints(linePoints, new Rect[] { rectSource, rectSink }, source.Orientation, sink.Orientation);
|
||||
linePoints = OptimizeLinePoints(linePoints, new RectangleBase[] { rectSource, rectSink }, source.Orientation, sink.Orientation);
|
||||
|
||||
CheckPathEnd(source, sink, showLastLine, linePoints, sourceInnerPoint);
|
||||
|
||||
return linePoints;
|
||||
}
|
||||
|
||||
public List<Point> GetConnectionLine(IDiagramViewModel diagramViewModel, ConnectorInfo source, Point sinkPoint, ConnectorOrientation preferredOrientation, bool showLastLine, bool isInnerPoint = false)
|
||||
public List<PointBase> GetConnectionLine(IDiagramViewModel diagramViewModel, ConnectorInfo source, PointBase sinkPoint, ConnectorOrientation preferredOrientation, bool showLastLine, bool isInnerPoint = false)
|
||||
{
|
||||
List<Point> linePoints = new List<Point>();
|
||||
List<PointBase> linePoints = new List<PointBase>();
|
||||
int margin = isInnerPoint ? 0 : const_margin;
|
||||
|
||||
Rect rectSource = GetRectWithMargin(source, margin);
|
||||
Point startPoint = GetOffsetPoint(source, rectSource, isInnerPoint);
|
||||
Point endPoint = sinkPoint;
|
||||
RectangleBase rectSource = GetRectWithMargin(source, margin);
|
||||
PointBase startPoint = GetOffsetPoint(source, rectSource, isInnerPoint);
|
||||
PointBase endPoint = sinkPoint;
|
||||
|
||||
linePoints.Add(startPoint);
|
||||
Point currentPoint = startPoint;
|
||||
PointBase currentPoint = startPoint;
|
||||
|
||||
if (!rectSource.Contains(endPoint))
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (IsPointVisible(currentPoint, endPoint, new Rect[] { rectSource }))
|
||||
if (IsPointVisible(currentPoint, endPoint, new RectangleBase[] { rectSource }))
|
||||
{
|
||||
linePoints.Add(endPoint);
|
||||
break;
|
||||
}
|
||||
|
||||
bool sideFlag;
|
||||
Point n = GetNearestNeighborSource(source, endPoint, rectSource, out sideFlag, isInnerPoint);
|
||||
PointBase n = GetNearestNeighborSource(source, endPoint, rectSource, out sideFlag, isInnerPoint);
|
||||
linePoints.Add(n);
|
||||
currentPoint = n;
|
||||
|
||||
if (IsPointVisible(currentPoint, endPoint, new Rect[] { rectSource }))
|
||||
if (IsPointVisible(currentPoint, endPoint, new RectangleBase[] { rectSource }))
|
||||
{
|
||||
linePoints.Add(endPoint);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
Point n1, n2;
|
||||
PointBase n1, n2;
|
||||
GetOppositeCorners(source.Orientation, rectSource, out n1, out n2, isInnerPoint);
|
||||
if (sideFlag)
|
||||
linePoints.Add(n1);
|
||||
@@ -319,9 +319,9 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
}
|
||||
|
||||
if (preferredOrientation != ConnectorOrientation.None)
|
||||
linePoints = OptimizeLinePoints(linePoints, new Rect[] { rectSource }, source.Orientation, preferredOrientation);
|
||||
linePoints = OptimizeLinePoints(linePoints, new RectangleBase[] { rectSource }, source.Orientation, preferredOrientation);
|
||||
else
|
||||
linePoints = OptimizeLinePoints(linePoints, new Rect[] { rectSource }, source.Orientation, GetOpositeOrientation(source.Orientation));
|
||||
linePoints = OptimizeLinePoints(linePoints, new RectangleBase[] { rectSource }, source.Orientation, GetOpositeOrientation(source.Orientation));
|
||||
|
||||
if (!showLastLine)
|
||||
{
|
||||
@@ -331,9 +331,9 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
return linePoints;
|
||||
}
|
||||
|
||||
private static List<Point> OptimizeLinePoints(List<Point> linePoints, Rect[] rectangles, ConnectorOrientation sourceOrientation, ConnectorOrientation sinkOrientation)
|
||||
private static List<PointBase> OptimizeLinePoints(List<PointBase> linePoints, RectangleBase[] rectangles, ConnectorOrientation sourceOrientation, ConnectorOrientation sinkOrientation)
|
||||
{
|
||||
List<Point> points = new List<Point>();
|
||||
List<PointBase> points = new List<PointBase>();
|
||||
int cut = 0;
|
||||
|
||||
for (int i = 0; i < linePoints.Count; i++)
|
||||
@@ -377,8 +377,8 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
(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;
|
||||
points.Insert(j + 1, new Point(centerX, points[j].Y));
|
||||
points.Insert(j + 2, new Point(centerX, points[j + 2].Y));
|
||||
points.Insert(j + 1, new PointBase(centerX, points[j].Y));
|
||||
points.Insert(j + 2, new PointBase(centerX, points[j + 2].Y));
|
||||
if (points.Count - 1 > j + 3)
|
||||
points.RemoveAt(j + 3);
|
||||
return points;
|
||||
@@ -388,8 +388,8 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
(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;
|
||||
points.Insert(j + 1, new Point(points[j].X, centerY));
|
||||
points.Insert(j + 2, new Point(points[j + 2].X, centerY));
|
||||
points.Insert(j + 1, new PointBase(points[j].X, centerY));
|
||||
points.Insert(j + 2, new PointBase(points[j + 2].X, centerY));
|
||||
if (points.Count - 1 > j + 3)
|
||||
points.RemoveAt(j + 3);
|
||||
return points;
|
||||
@@ -398,14 +398,14 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
if ((orientationFrom == ConnectorOrientation.Left || orientationFrom == ConnectorOrientation.Right) &&
|
||||
(orientationTo == ConnectorOrientation.Top || orientationTo == ConnectorOrientation.Bottom))
|
||||
{
|
||||
points.Insert(j + 1, new Point(points[j + 1].X, points[j].Y));
|
||||
points.Insert(j + 1, new PointBase(points[j + 1].X, points[j].Y));
|
||||
return points;
|
||||
}
|
||||
|
||||
if ((orientationFrom == ConnectorOrientation.Top || orientationFrom == ConnectorOrientation.Bottom) &&
|
||||
(orientationTo == ConnectorOrientation.Left || orientationTo == ConnectorOrientation.Right))
|
||||
{
|
||||
points.Insert(j + 1, new Point(points[j].X, points[j + 1].Y));
|
||||
points.Insert(j + 1, new PointBase(points[j].X, points[j + 1].Y));
|
||||
return points;
|
||||
}
|
||||
}
|
||||
@@ -415,7 +415,7 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
return points;
|
||||
}
|
||||
|
||||
private static ConnectorOrientation GetOrientation(Point p1, Point p2)
|
||||
private static ConnectorOrientation GetOrientation(PointBase p1, PointBase p2)
|
||||
{
|
||||
if (p1.X == p2.X)
|
||||
{
|
||||
@@ -455,9 +455,9 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
}
|
||||
}
|
||||
|
||||
private static Point GetNearestNeighborSource(ConnectorInfo source, Point endPoint, Rect rectSource, Rect rectSink, out bool flag, bool isInnerPoint)
|
||||
private static PointBase GetNearestNeighborSource(ConnectorInfo source, PointBase endPoint, RectangleBase rectSource, RectangleBase rectSink, out bool flag, bool isInnerPoint)
|
||||
{
|
||||
Point n1, n2; // neighbors
|
||||
PointBase n1, n2; // neighbors
|
||||
GetNeighborCorners(source.Orientation, rectSource, out n1, out n2, isInnerPoint);
|
||||
|
||||
if (rectSink.Contains(n1))
|
||||
@@ -484,9 +484,9 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
}
|
||||
}
|
||||
|
||||
private static Point GetNearestNeighborSource(ConnectorInfo source, Point endPoint, Rect rectSource, out bool flag, bool isInnerPoint)
|
||||
private static PointBase GetNearestNeighborSource(ConnectorInfo source, PointBase endPoint, RectangleBase rectSource, out bool flag, bool isInnerPoint)
|
||||
{
|
||||
Point n1, n2; // neighbors
|
||||
PointBase n1, n2; // neighbors
|
||||
GetNeighborCorners(source.Orientation, rectSource, out n1, out n2, isInnerPoint);
|
||||
|
||||
if ((Distance(n1, endPoint) <= Distance(n2, endPoint)))
|
||||
@@ -501,13 +501,13 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
}
|
||||
}
|
||||
|
||||
private static Point GetNearestVisibleNeighborSink(Point currentPoint, Point endPoint, ConnectorInfo sink, Rect rectSource, Rect rectSink)
|
||||
private static PointBase GetNearestVisibleNeighborSink(PointBase currentPoint, PointBase endPoint, ConnectorInfo sink, RectangleBase rectSource, RectangleBase rectSink)
|
||||
{
|
||||
Point s1, s2; // neighbors on sink side
|
||||
PointBase s1, s2; // neighbors on sink side
|
||||
GetNeighborCorners(sink.Orientation, rectSink, out s1, out s2);
|
||||
|
||||
bool flag1 = IsPointVisible(currentPoint, s1, new Rect[] { rectSource, rectSink });
|
||||
bool flag2 = IsPointVisible(currentPoint, s2, new Rect[] { rectSource, rectSink });
|
||||
bool flag1 = IsPointVisible(currentPoint, s1, new RectangleBase[] { rectSource, rectSink });
|
||||
bool flag2 = IsPointVisible(currentPoint, s2, new RectangleBase[] { rectSource, rectSink });
|
||||
|
||||
if (flag1) // s1 visible
|
||||
{
|
||||
@@ -538,14 +538,14 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
}
|
||||
else // s1 and s2 not visible
|
||||
{
|
||||
return new Point(double.NaN, double.NaN);
|
||||
return new PointBase(double.NaN, double.NaN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsPointVisible(Point fromPoint, Point targetPoint, Rect[] rectangles)
|
||||
private static bool IsPointVisible(PointBase fromPoint, PointBase targetPoint, RectangleBase[] rectangles)
|
||||
{
|
||||
foreach (Rect rect in rectangles)
|
||||
foreach (RectangleBase rect in rectangles)
|
||||
{
|
||||
if (RectangleIntersectsLine(rect, fromPoint, targetPoint))
|
||||
return false;
|
||||
@@ -553,7 +553,7 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool IsRectVisible(Point fromPoint, Rect targetRect, Rect[] rectangles)
|
||||
private static bool IsRectVisible(PointBase fromPoint, RectangleBase targetRect, RectangleBase[] rectangles)
|
||||
{
|
||||
if (IsPointVisible(fromPoint, targetRect.TopLeft, rectangles))
|
||||
return true;
|
||||
@@ -570,13 +570,13 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool RectangleIntersectsLine(Rect rect, Point startPoint, Point endPoint)
|
||||
private static bool RectangleIntersectsLine(RectangleBase rect, PointBase startPoint, PointBase endPoint)
|
||||
{
|
||||
rect.Inflate(-1, -1);
|
||||
return rect.IntersectsWith(new Rect(startPoint, endPoint));
|
||||
return rect.IntersectsWith(new RectangleBase(startPoint, endPoint));
|
||||
}
|
||||
|
||||
private static void GetOppositeCorners(ConnectorOrientation orientation, Rect rect, out Point n1, out Point n2, bool isInnerPoint = false)
|
||||
private static void GetOppositeCorners(ConnectorOrientation orientation, RectangleBase rect, out PointBase n1, out PointBase n2, bool isInnerPoint = false)
|
||||
{
|
||||
if (isInnerPoint)
|
||||
{
|
||||
@@ -602,7 +602,7 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
}
|
||||
}
|
||||
|
||||
private static void GetNeighborCorners(ConnectorOrientation orientation, Rect rect, out Point n1, out Point n2, bool isInnerPoint = false)
|
||||
private static void GetNeighborCorners(ConnectorOrientation orientation, RectangleBase rect, out PointBase n1, out PointBase n2, bool isInnerPoint = false)
|
||||
{
|
||||
if (isInnerPoint)
|
||||
{
|
||||
@@ -628,14 +628,14 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
}
|
||||
}
|
||||
|
||||
private static double Distance(Point p1, Point p2)
|
||||
private static double Distance(PointBase p1, PointBase p2)
|
||||
{
|
||||
return Point.Subtract(p1, p2).Length;
|
||||
return PointBase.Subtract(p1, p2).Length;
|
||||
}
|
||||
|
||||
private static Rect GetRectWithMargin(ConnectorInfo connectorThumb, double margin)
|
||||
private static RectangleBase GetRectWithMargin(ConnectorInfo connectorThumb, double margin)
|
||||
{
|
||||
Rect rect = new Rect(connectorThumb.DesignerItemLeft,
|
||||
RectangleBase rect = new RectangleBase(connectorThumb.DesignerItemLeft,
|
||||
connectorThumb.DesignerItemTop,
|
||||
0,
|
||||
0);
|
||||
@@ -645,28 +645,28 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
return rect;
|
||||
}
|
||||
|
||||
private static Point GetOffsetPoint(ConnectorInfo connector, Rect rect, bool isInnerPoint = false)
|
||||
private static PointBase GetOffsetPoint(ConnectorInfo connector, RectangleBase rect, bool isInnerPoint = false)
|
||||
{
|
||||
Point offsetPoint = new Point();
|
||||
PointBase offsetPoint = new PointBase();
|
||||
if (isInnerPoint)
|
||||
{
|
||||
offsetPoint = new Point(connector.Position.X, connector.Position.Y);
|
||||
offsetPoint = new PointBase(connector.Position.X, connector.Position.Y);
|
||||
return offsetPoint;
|
||||
}
|
||||
|
||||
switch (connector.Orientation)
|
||||
{
|
||||
case ConnectorOrientation.Left:
|
||||
offsetPoint = new Point(rect.Left, connector.Position.Y);
|
||||
offsetPoint = new PointBase(rect.Left, connector.Position.Y);
|
||||
break;
|
||||
case ConnectorOrientation.Top:
|
||||
offsetPoint = new Point(connector.Position.X, rect.Top);
|
||||
offsetPoint = new PointBase(connector.Position.X, rect.Top);
|
||||
break;
|
||||
case ConnectorOrientation.Right:
|
||||
offsetPoint = new Point(rect.Right, connector.Position.Y);
|
||||
offsetPoint = new PointBase(rect.Right, connector.Position.Y);
|
||||
break;
|
||||
case ConnectorOrientation.Bottom:
|
||||
offsetPoint = new Point(connector.Position.X, rect.Bottom);
|
||||
offsetPoint = new PointBase(connector.Position.X, rect.Bottom);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -675,48 +675,48 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
return offsetPoint;
|
||||
}
|
||||
|
||||
private static void CheckPathEnd(ConnectorInfo source, ConnectorInfo sink, bool showLastLine, List<Point> linePoints, bool sourceInnerPoint)
|
||||
private static void CheckPathEnd(ConnectorInfo source, ConnectorInfo sink, bool showLastLine, List<PointBase> linePoints, bool sourceInnerPoint)
|
||||
{
|
||||
if (showLastLine)
|
||||
{
|
||||
Point startPoint = new Point(0, 0);
|
||||
Point endPoint = new Point(0, 0);
|
||||
PointBase startPoint = new PointBase(0, 0);
|
||||
PointBase endPoint = new PointBase(0, 0);
|
||||
double marginPath = 15;
|
||||
switch (source.Orientation)
|
||||
{
|
||||
case ConnectorOrientation.Left:
|
||||
startPoint = new Point(source.Position.X - marginPath, source.Position.Y);
|
||||
startPoint = new PointBase(source.Position.X - marginPath, source.Position.Y);
|
||||
break;
|
||||
case ConnectorOrientation.Top:
|
||||
startPoint = new Point(source.Position.X, source.Position.Y - marginPath);
|
||||
startPoint = new PointBase(source.Position.X, source.Position.Y - marginPath);
|
||||
break;
|
||||
case ConnectorOrientation.Right:
|
||||
startPoint = new Point(source.Position.X + marginPath, source.Position.Y);
|
||||
startPoint = new PointBase(source.Position.X + marginPath, source.Position.Y);
|
||||
break;
|
||||
case ConnectorOrientation.Bottom:
|
||||
startPoint = new Point(source.Position.X, source.Position.Y + marginPath);
|
||||
startPoint = new PointBase(source.Position.X, source.Position.Y + marginPath);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (sourceInnerPoint)
|
||||
{
|
||||
startPoint = new Point(source.Position.X, source.Position.Y);
|
||||
startPoint = new PointBase(source.Position.X, source.Position.Y);
|
||||
}
|
||||
|
||||
switch (sink.Orientation)
|
||||
{
|
||||
case ConnectorOrientation.Left:
|
||||
endPoint = new Point(sink.Position.X - marginPath, sink.Position.Y);
|
||||
endPoint = new PointBase(sink.Position.X - marginPath, sink.Position.Y);
|
||||
break;
|
||||
case ConnectorOrientation.Top:
|
||||
endPoint = new Point(sink.Position.X, sink.Position.Y - marginPath);
|
||||
endPoint = new PointBase(sink.Position.X, sink.Position.Y - marginPath);
|
||||
break;
|
||||
case ConnectorOrientation.Right:
|
||||
endPoint = new Point(sink.Position.X + marginPath, sink.Position.Y);
|
||||
endPoint = new PointBase(sink.Position.X + marginPath, sink.Position.Y);
|
||||
break;
|
||||
case ConnectorOrientation.Bottom:
|
||||
endPoint = new Point(sink.Position.X, sink.Position.Y + marginPath);
|
||||
endPoint = new PointBase(sink.Position.X, sink.Position.Y + marginPath);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using AIStudio.Wpf.DiagramDesigner.Geometry;
|
||||
|
||||
namespace AIStudio.Wpf.DiagramDesigner
|
||||
{
|
||||
public class StraightLinePathFinder : IPathFinder
|
||||
{
|
||||
public List<ConnectorPoint> UpdateConnectionPoints(IDiagramViewModel diagramViewModel, PointBase sourceA, PointBase sourceB, FullyCreatedConnectorInfo sourceConnectorInfo, ConnectorInfoBase sinkConnectorInfo)
|
||||
{
|
||||
var area = new RectangleBase(sourceA, sourceB);
|
||||
var connectionPoints = ConnectorPoint.ToList(new List<PointBase>()
|
||||
{
|
||||
|
||||
new PointBase(sourceA.X < sourceB.X ? 0d : area.Width, sourceA.Y < sourceB.Y ? 0d : area.Height ),
|
||||
new PointBase(sourceA.X > sourceB.X ? 0d : area.Width, sourceA.Y > sourceB.Y ? 0d : area.Height)
|
||||
});
|
||||
|
||||
return connectionPoints;
|
||||
}
|
||||
|
||||
public List<PointBase> GetConnectionLine(IDiagramViewModel diagramViewModel, ConnectorInfo source, ConnectorInfo sink, bool showLastLine, bool sourceInnerPoint = false)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public List<PointBase> GetConnectionLine(IDiagramViewModel diagramViewModel, ConnectorInfo source, PointBase sinkPoint, ConnectorOrientation preferredOrientation, bool showLastLine, bool isInnerPoint = false)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Windows;
|
||||
|
||||
namespace AIStudio.Wpf.DiagramDesigner
|
||||
{
|
||||
public interface IPathFinder
|
||||
{
|
||||
List<PointInfoBase> UpdateConnectionPoints(IDiagramViewModel diagramViewModel, Point sourceA, Point sourceB, FullyCreatedConnectorInfo sourceConnectorInfo, ConnectorInfoBase sinkConnectorInfo);
|
||||
List<Point> GetConnectionLine(IDiagramViewModel diagramViewModel, ConnectorInfo source, ConnectorInfo sink, bool showLastLine, bool sourceInnerPoint = false);
|
||||
List<Point> GetConnectionLine(IDiagramViewModel diagramViewModel, ConnectorInfo source, Point sinkPoint, ConnectorOrientation preferredOrientation, bool showLastLine, bool isInnerPoint = false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using AIStudio.Wpf.DiagramDesigner.Geometry;
|
||||
|
||||
namespace AIStudio.Wpf.DiagramDesigner
|
||||
{
|
||||
public class PathGeneratorResult
|
||||
{
|
||||
public PathGeneratorResult(string[] paths, double? sourceMarkerAngle = null, PointBase? sourceMarkerPosition = null,
|
||||
double? targetMarkerAngle = null, PointBase? targetMarkerPosition = null)
|
||||
{
|
||||
Paths = paths;
|
||||
SourceMarkerAngle = sourceMarkerAngle;
|
||||
SourceMarkerPosition = sourceMarkerPosition;
|
||||
TargetMarkerAngle = targetMarkerAngle;
|
||||
TargetMarkerPosition = targetMarkerPosition;
|
||||
}
|
||||
|
||||
public string[] Paths { get; }
|
||||
public double? SourceMarkerAngle { get; }
|
||||
public PointBase? SourceMarkerPosition { get; }
|
||||
public double? TargetMarkerAngle { get; }
|
||||
public PointBase? TargetMarkerPosition { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
using System;
|
||||
using AIStudio.Wpf.DiagramDesigner.Geometry;
|
||||
|
||||
namespace AIStudio.Wpf.DiagramDesigner
|
||||
{
|
||||
public static partial class PathGenerators
|
||||
{
|
||||
private const double _margin = 125;
|
||||
|
||||
public static PathGeneratorResult Smooth(IDiagramViewModel _, ConnectorViewModel link, PointBase[] route, PointBase source, PointBase target)
|
||||
{
|
||||
route = ConcatRouteAndSourceAndTarget(route, source, target);
|
||||
|
||||
if (route.Length > 2)
|
||||
return CurveThroughPoints(route, link);
|
||||
|
||||
route = GetRouteWithCurvePoints(link, route);
|
||||
double? sourceAngle = null;
|
||||
double? targetAngle = null;
|
||||
|
||||
sourceAngle = SourceMarkerAdjustement(route, (double)link.ColorViewModel.LeftArrowSizeStyle);
|
||||
targetAngle = TargetMarkerAdjustement(route, (double)link.ColorViewModel.RightArrowPathStyle);
|
||||
|
||||
var path = FormattableString.Invariant($"M {route[0].X} {route[0].Y} C {route[1].X} {route[1].Y}, {route[2].X} {route[2].Y}, {route[3].X} {route[3].Y}");
|
||||
return new PathGeneratorResult(new[] { path }, sourceAngle, route[0], targetAngle, route[route.Length - 1]);
|
||||
}
|
||||
|
||||
private static PathGeneratorResult CurveThroughPoints(PointBase[] route, ConnectorViewModel link)
|
||||
{
|
||||
double? sourceAngle = null;
|
||||
double? targetAngle = null;
|
||||
|
||||
sourceAngle = SourceMarkerAdjustement(route, (double)link.ColorViewModel.LeftArrowSizeStyle);
|
||||
targetAngle = TargetMarkerAdjustement(route, (double)link.ColorViewModel.RightArrowPathStyle);
|
||||
|
||||
Geometry.BezierSpline.GetCurveControlPoints(route, out var firstControlPoints, out var secondControlPoints);
|
||||
var paths = new string[firstControlPoints.Length];
|
||||
|
||||
for (var i = 0; i < firstControlPoints.Length; i++)
|
||||
{
|
||||
var cp1 = firstControlPoints[i];
|
||||
var cp2 = secondControlPoints[i];
|
||||
paths[i] = FormattableString.Invariant($"M {route[i].X} {route[i].Y} C {cp1.X} {cp1.Y}, {cp2.X} {cp2.Y}, {route[i + 1].X} {route[i + 1].Y}");
|
||||
}
|
||||
|
||||
// Todo: adjust marker positions based on closest control points
|
||||
return new PathGeneratorResult(paths, sourceAngle, route[0], targetAngle, route[route.Length - 1]);
|
||||
}
|
||||
|
||||
private static PointBase[] GetRouteWithCurvePoints(ConnectorViewModel link, PointBase[] route)
|
||||
{
|
||||
if (link.IsPortless)
|
||||
{
|
||||
if (Math.Abs(route[0].X - route[1].X) >= Math.Abs(route[0].Y - route[1].Y))
|
||||
{
|
||||
var cX = (route[0].X + route[1].X) / 2;
|
||||
return new[] { route[0], new PointBase(cX, route[0].Y), new PointBase(cX, route[1].Y), route[1] };
|
||||
}
|
||||
else
|
||||
{
|
||||
var cY = (route[0].Y + route[1].Y) / 2;
|
||||
return new[] { route[0], new PointBase(route[0].X, cY), new PointBase(route[1].X, cY), route[1] };
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var cX = (route[0].X + route[1].X) / 2;
|
||||
var cY = (route[0].Y + route[1].Y) / 2;
|
||||
var curvePointA = GetCurvePoint(route[0].X, route[0].Y, cX, cY, link.SourceConnectorInfo?.Orientation);
|
||||
var curvePointB = GetCurvePoint(route[1].X, route[1].Y, cX, cY, link.SinkConnectorInfo?.Orientation);
|
||||
return new[] { route[0], curvePointA, curvePointB, route[1] };
|
||||
}
|
||||
}
|
||||
|
||||
private static PointBase GetCurvePoint(double pX, double pY, double cX, double cY, ConnectorOrientation? alignment)
|
||||
{
|
||||
var margin = Math.Min(_margin, Math.Pow(Math.Pow(pX - cX, 2) + Math.Pow(pY - cY, 2), .5));
|
||||
switch (alignment)
|
||||
{
|
||||
case ConnectorOrientation.Top: return new PointBase(pX, Math.Min(pY - margin, cY));
|
||||
case ConnectorOrientation.Bottom: return new PointBase(pX, Math.Max(pY + margin, cY));
|
||||
case ConnectorOrientation.TopRight: return new PointBase(Math.Max(pX + margin, cX), Math.Min(pY - margin, cY));
|
||||
case ConnectorOrientation.BottomRight: return new PointBase(Math.Max(pX + margin, cX), Math.Max(pY + margin, cY));
|
||||
case ConnectorOrientation.Right: return new PointBase(Math.Max(pX + margin, cX), pY);
|
||||
case ConnectorOrientation.Left: return new PointBase(Math.Min(pX - margin, cX), pY);
|
||||
case ConnectorOrientation.BottomLeft: return new PointBase(Math.Min(pX - margin, cX), Math.Max(pY + margin, cY));
|
||||
case ConnectorOrientation.TopLeft: return new PointBase(Math.Min(pX - margin, cX), Math.Min(pY - margin, cY));
|
||||
default: return new PointBase(cX, cY);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using AIStudio.Wpf.DiagramDesigner.Geometry;
|
||||
|
||||
namespace AIStudio.Wpf.DiagramDesigner
|
||||
{
|
||||
public static partial class PathGenerators
|
||||
{
|
||||
public static PathGeneratorResult Straight(IDiagramViewModel _, ConnectorViewModel link, PointBase[] route, PointBase source, PointBase target)
|
||||
{
|
||||
route = ConcatRouteAndSourceAndTarget(route, source, target);
|
||||
double? sourceAngle = null;
|
||||
double? targetAngle = null;
|
||||
|
||||
sourceAngle = SourceMarkerAdjustement(route, (double)link.ColorViewModel.LeftArrowSizeStyle);
|
||||
targetAngle = TargetMarkerAdjustement(route, (double)link.ColorViewModel.RightArrowPathStyle);
|
||||
|
||||
var paths = new string[route.Length - 1];
|
||||
for (var i = 0; i < route.Length - 1; i++)
|
||||
{
|
||||
paths[i] = FormattableString.Invariant($"M {route[i].X} {route[i].Y} L {route[i + 1].X} {route[i + 1].Y}");
|
||||
}
|
||||
|
||||
return new PathGeneratorResult(paths, sourceAngle, route[0], targetAngle, route[route.Length - 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using AIStudio.Wpf.DiagramDesigner.Geometry;
|
||||
|
||||
namespace AIStudio.Wpf.DiagramDesigner
|
||||
{
|
||||
public static partial class PathGenerators
|
||||
{
|
||||
public static double SourceMarkerAdjustement(PointBase[] route, double markerWidth)
|
||||
{
|
||||
var angleInRadians = Math.Atan2(route[1].Y - route[0].Y, route[1].X - route[0].X) + Math.PI;
|
||||
var xChange = markerWidth * Math.Cos(angleInRadians);
|
||||
var yChange = markerWidth * Math.Sin(angleInRadians);
|
||||
route[0] = new PointBase(route[0].X - xChange, route[0].Y - yChange);
|
||||
return angleInRadians * 180 / Math.PI;
|
||||
}
|
||||
|
||||
public static double TargetMarkerAdjustement(PointBase[] route, double markerWidth)
|
||||
{
|
||||
var angleInRadians = Math.Atan2(route[route.Length - 1].Y - route[route.Length - 2].Y, route[route.Length - 1].X - route[route.Length - 2].X);
|
||||
var xChange = markerWidth * Math.Cos(angleInRadians);
|
||||
var yChange = markerWidth * Math.Sin(angleInRadians);
|
||||
route[route.Length - 1] = new PointBase(route[route.Length - 1].X - xChange, route[route.Length - 1].Y - yChange);
|
||||
return angleInRadians * 180 / Math.PI;
|
||||
}
|
||||
|
||||
public static PointBase[] ConcatRouteAndSourceAndTarget(PointBase[] route, PointBase source, PointBase target)
|
||||
{
|
||||
var result = new PointBase[route.Length + 2];
|
||||
result[0] = source;
|
||||
route.CopyTo(result, 1);
|
||||
result[route.Length - 1] = target;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Windows;
|
||||
|
||||
namespace AIStudio.Wpf.DiagramDesigner
|
||||
{
|
||||
public class StraightLinePathFinder : IPathFinder
|
||||
{
|
||||
|
||||
|
||||
public List<PointInfoBase> UpdateConnectionPoints(IDiagramViewModel diagramViewModel, Point sourceA, Point sourceB, FullyCreatedConnectorInfo sourceConnectorInfo, ConnectorInfoBase sinkConnectorInfo)
|
||||
{
|
||||
var area = new Rect(sourceA, sourceB);
|
||||
var connectionPoints = PointInfoBase.ToList(new List<Point>()
|
||||
{
|
||||
|
||||
new Point(sourceA.X < sourceB.X ? 0d : area.Width, sourceA.Y < sourceB.Y ? 0d : area.Height ),
|
||||
new Point(sourceA.X > sourceB.X ? 0d : area.Width, sourceA.Y > sourceB.Y ? 0d : area.Height)
|
||||
});
|
||||
|
||||
return connectionPoints;
|
||||
}
|
||||
|
||||
public List<Point> GetConnectionLine(IDiagramViewModel diagramViewModel, ConnectorInfo source, ConnectorInfo sink, bool showLastLine, bool sourceInnerPoint = false)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public List<Point> GetConnectionLine(IDiagramViewModel diagramViewModel, ConnectorInfo source, Point sinkPoint, ConnectorOrientation preferredOrientation, bool showLastLine, bool isInnerPoint = false)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
13
AIStudio.Wpf.DiagramDesigner/Routers/Routers.Normal.cs
Normal file
13
AIStudio.Wpf.DiagramDesigner/Routers/Routers.Normal.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using System.Linq;
|
||||
using AIStudio.Wpf.DiagramDesigner.Geometry;
|
||||
|
||||
namespace AIStudio.Wpf.DiagramDesigner
|
||||
{
|
||||
public static partial class Routers
|
||||
{
|
||||
public static PointBase[] Normal(IDiagramViewModel _, ConnectorViewModel link)
|
||||
{
|
||||
return link.Vertices.Select(v => (PointBase)v).ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
586
AIStudio.Wpf.DiagramDesigner/Routers/Routers.Orthogonal.cs
Normal file
586
AIStudio.Wpf.DiagramDesigner/Routers/Routers.Orthogonal.cs
Normal file
@@ -0,0 +1,586 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using AIStudio.Wpf.DiagramDesigner.Geometry;
|
||||
|
||||
// Implementation taken from the JS version: https://gist.github.com/menendezpoo/4a8894c152383b9d7a870c24a04447e4
|
||||
// Todo: Make it more c#, Benchmark A* vs Dijkstra, Add more options
|
||||
namespace AIStudio.Wpf.DiagramDesigner
|
||||
{
|
||||
public static partial class Routers
|
||||
{
|
||||
public static PointBase[] Orthogonal(IDiagramViewModel _, ConnectorViewModel link)
|
||||
{
|
||||
if (link.IsPortless)
|
||||
throw new Exception("Orthogonal router doesn't work with portless links yet");
|
||||
|
||||
if (link.IsFullConnection == false)
|
||||
return Normal(_, link);
|
||||
|
||||
var shapeMargin = 10;
|
||||
var globalBoundsMargin = 50;
|
||||
var spots = new List<PointBase>();
|
||||
var verticals = new List<double>();
|
||||
var horizontals = new List<double>();
|
||||
var sideA = link.SourceConnectorInfo.Orientation;
|
||||
var sideAVertical = IsVerticalSide(sideA);
|
||||
var sideB = link.SinkConnectorInfo.Orientation;
|
||||
var sideBVertical = IsVerticalSide(sideB);
|
||||
var originA = GetPortPositionBasedOnAlignment(link.SourceConnectorInfo);
|
||||
var originB = GetPortPositionBasedOnAlignment(link.SinkConnectorInfo);
|
||||
var shapeA = link.SourceConnectorInfo.DataItem.GetBounds(includePorts: true);
|
||||
var shapeB = link.SinkConnectorInfoFully.DataItem.GetBounds(includePorts: true);
|
||||
var inflatedA = shapeA.InflateRectangle(shapeMargin, shapeMargin);
|
||||
var inflatedB = shapeB.InflateRectangle(shapeMargin, shapeMargin);
|
||||
|
||||
if (inflatedA.Intersects(inflatedB))
|
||||
{
|
||||
shapeMargin = 0;
|
||||
inflatedA = shapeA;
|
||||
inflatedB = shapeB;
|
||||
}
|
||||
|
||||
// Curated bounds to stick to
|
||||
var bounds = inflatedA.UnionRectangle(inflatedB).InflateRectangle(globalBoundsMargin, globalBoundsMargin);
|
||||
|
||||
// Add edges to rulers
|
||||
verticals.Add(inflatedA.Left);
|
||||
verticals.Add(inflatedA.Right);
|
||||
horizontals.Add(inflatedA.Top);
|
||||
horizontals.Add(inflatedA.Bottom);
|
||||
verticals.Add(inflatedB.Left);
|
||||
verticals.Add(inflatedB.Right);
|
||||
horizontals.Add(inflatedB.Top);
|
||||
horizontals.Add(inflatedB.Bottom);
|
||||
|
||||
// Rulers at origins of shapes
|
||||
(sideAVertical ? verticals : horizontals).Add(sideAVertical ? originA.X : originA.Y);
|
||||
(sideBVertical ? verticals : horizontals).Add(sideBVertical ? originB.X : originB.Y);
|
||||
|
||||
// Points of shape antennas
|
||||
spots.Add(GetOriginSpot(originA, sideA, shapeMargin));
|
||||
spots.Add(GetOriginSpot(originB, sideB, shapeMargin));
|
||||
|
||||
// Sort rulers
|
||||
verticals.Sort();
|
||||
horizontals.Sort();
|
||||
|
||||
// Create grid
|
||||
var grid = RulersToGrid(verticals, horizontals, bounds);
|
||||
var gridPoints = GridToSpots(grid, new[] { inflatedA, inflatedB });
|
||||
|
||||
// Add to spots
|
||||
spots.AddRange(gridPoints);
|
||||
|
||||
// Create graph
|
||||
var graph = CreateGraph(spots);
|
||||
|
||||
// Origin and destination by extruding antennas
|
||||
var origin = ExtrudeCp(originA, shapeMargin, sideA);
|
||||
var destination = ExtrudeCp(originB, shapeMargin, sideB);
|
||||
|
||||
var path = ShortestPath(graph, origin, destination);
|
||||
if (path.Length > 0)
|
||||
{
|
||||
return SimplifyPath(path);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Normal(_, link);
|
||||
}
|
||||
}
|
||||
|
||||
private static PointBase GetOriginSpot(PointBase p, ConnectorOrientation alignment, double shapeMargin)
|
||||
{
|
||||
switch (alignment)
|
||||
{
|
||||
case ConnectorOrientation.Top: return p.Add(0, -shapeMargin);
|
||||
case ConnectorOrientation.Right: return p.Add(shapeMargin, 0);
|
||||
case ConnectorOrientation.Bottom: return p.Add(0, shapeMargin);
|
||||
case ConnectorOrientation.Left: return p.Add(-shapeMargin, 0);
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsVerticalSide(ConnectorOrientation alignment)
|
||||
=> alignment == ConnectorOrientation.Top || alignment == ConnectorOrientation.Bottom; // Add others
|
||||
|
||||
private static Grid RulersToGrid(List<double> verticals, List<double> horizontals, RectangleBase bounds)
|
||||
{
|
||||
var result = new Grid();
|
||||
verticals.Sort();
|
||||
horizontals.Sort();
|
||||
|
||||
var lastX = bounds.Left;
|
||||
var lastY = bounds.Top;
|
||||
var column = 0;
|
||||
var row = 0;
|
||||
|
||||
foreach (var y in horizontals)
|
||||
{
|
||||
foreach (var x in verticals)
|
||||
{
|
||||
result.Set(row, column++, new RectangleBase(lastX, lastY, x, y));
|
||||
lastX = x;
|
||||
}
|
||||
|
||||
// Last cell of the row
|
||||
result.Set(row, column, new RectangleBase(lastX, lastY, bounds.Right, y));
|
||||
lastX = bounds.Left;
|
||||
lastY = y;
|
||||
column = 0;
|
||||
row++;
|
||||
}
|
||||
|
||||
lastX = bounds.Left;
|
||||
|
||||
// Last fow of cells
|
||||
foreach (var x in verticals)
|
||||
{
|
||||
result.Set(row, column++, new RectangleBase(lastX, lastY, x, bounds.Bottom));
|
||||
lastX = x;
|
||||
}
|
||||
|
||||
// Last cell of last row
|
||||
result.Set(row, column, new RectangleBase(lastX, lastY, bounds.Right, bounds.Bottom));
|
||||
return result;
|
||||
}
|
||||
|
||||
private static List<PointBase> GridToSpots(Grid grid, RectangleBase[] obstacles)
|
||||
{
|
||||
bool obstacleCollision(PointBase p) => obstacles.Where(o => o.ContainsPoint(p)).Any();
|
||||
|
||||
var gridPoints = new List<PointBase>();
|
||||
foreach (var keyValuePair in grid.Data)
|
||||
{
|
||||
var row = keyValuePair.Key;
|
||||
var data = keyValuePair.Value;
|
||||
|
||||
var firstRow = row == 0;
|
||||
var lastRow = row == grid.Rows - 1;
|
||||
|
||||
foreach (var keyValuePair2 in data)
|
||||
{
|
||||
var col = keyValuePair2.Key;
|
||||
var r = keyValuePair2.Value;
|
||||
|
||||
var firstCol = col == 0;
|
||||
var lastCol = col == grid.Columns - 1;
|
||||
var nw = firstCol && firstRow;
|
||||
var ne = firstRow && lastCol;
|
||||
var se = lastRow && lastCol;
|
||||
var sw = lastRow && firstCol;
|
||||
|
||||
if (nw || ne || se || sw)
|
||||
{
|
||||
gridPoints.Add(r.NorthWest);
|
||||
gridPoints.Add(r.NorthEast);
|
||||
gridPoints.Add(r.SouthWest);
|
||||
gridPoints.Add(r.SouthEast);
|
||||
}
|
||||
else if (firstRow)
|
||||
{
|
||||
gridPoints.Add(r.NorthWest);
|
||||
gridPoints.Add(r.North);
|
||||
gridPoints.Add(r.NorthEast);
|
||||
}
|
||||
else if (lastRow)
|
||||
{
|
||||
gridPoints.Add(r.SouthEast);
|
||||
gridPoints.Add(r.South);
|
||||
gridPoints.Add(r.SouthWest);
|
||||
}
|
||||
else if (firstCol)
|
||||
{
|
||||
gridPoints.Add(r.NorthWest);
|
||||
gridPoints.Add(r.West);
|
||||
gridPoints.Add(r.SouthWest);
|
||||
}
|
||||
else if (lastCol)
|
||||
{
|
||||
gridPoints.Add(r.NorthEast);
|
||||
gridPoints.Add(r.East);
|
||||
gridPoints.Add(r.SouthEast);
|
||||
}
|
||||
else
|
||||
{
|
||||
gridPoints.Add(r.NorthWest);
|
||||
gridPoints.Add(r.North);
|
||||
gridPoints.Add(r.NorthEast);
|
||||
gridPoints.Add(r.East);
|
||||
gridPoints.Add(r.SouthEast);
|
||||
gridPoints.Add(r.South);
|
||||
gridPoints.Add(r.SouthWest);
|
||||
gridPoints.Add(r.West);
|
||||
gridPoints.Add(r.Center);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reduce repeated points and filter out those who touch shapes
|
||||
return ReducePoints(gridPoints).Where(p => !obstacleCollision(p)).ToList();
|
||||
}
|
||||
|
||||
private static IEnumerable<PointBase> ReducePoints(List<PointBase> points)
|
||||
{
|
||||
var map = new Dictionary<double, List<double>>();
|
||||
foreach (var p in points)
|
||||
{
|
||||
(var x, var y) = p;
|
||||
if (!map.ContainsKey(y)) map.Add(y, new List<double>());
|
||||
var arr = map[y];
|
||||
|
||||
if (!arr.Contains(x)) arr.Add(x);
|
||||
}
|
||||
|
||||
foreach (var keyValuePair in map)
|
||||
{
|
||||
var y = keyValuePair.Key;
|
||||
var xs = keyValuePair.Value;
|
||||
|
||||
foreach (var x in xs)
|
||||
{
|
||||
yield return new PointBase(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static PointGraph CreateGraph(List<PointBase> spots)
|
||||
{
|
||||
var hotXs = new List<double>();
|
||||
var hotYs = new List<double>();
|
||||
var graph = new PointGraph();
|
||||
|
||||
spots.ForEach(p => {
|
||||
(var x, var y) = p;
|
||||
if (!hotXs.Contains(x)) hotXs.Add(x);
|
||||
if (!hotYs.Contains(y)) hotYs.Add(y);
|
||||
graph.Add(p);
|
||||
});
|
||||
|
||||
hotXs.Sort();
|
||||
hotYs.Sort();
|
||||
|
||||
for (var i = 0; i < hotYs.Count; i++)
|
||||
{
|
||||
for (var j = 0; j < hotXs.Count; j++)
|
||||
{
|
||||
var b = new PointBase(hotXs[j], hotYs[i]);
|
||||
if (!graph.Has(b)) continue;
|
||||
|
||||
if (j > 0)
|
||||
{
|
||||
var a = new PointBase(hotXs[j - 1], hotYs[i]);
|
||||
|
||||
if (graph.Has(a))
|
||||
{
|
||||
graph.Connect(a, b);
|
||||
graph.Connect(b, a);
|
||||
}
|
||||
}
|
||||
|
||||
if (i > 0)
|
||||
{
|
||||
var a = new PointBase(hotXs[j], hotYs[i - 1]);
|
||||
|
||||
if (graph.Has(a))
|
||||
{
|
||||
graph.Connect(a, b);
|
||||
graph.Connect(b, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return graph;
|
||||
}
|
||||
|
||||
private static PointBase ExtrudeCp(PointBase p, double margin, ConnectorOrientation alignment)
|
||||
{
|
||||
switch (alignment)
|
||||
{
|
||||
case ConnectorOrientation.Top: return p.Add(0, -margin);
|
||||
case ConnectorOrientation.Right: return p.Add(margin, 0);
|
||||
case ConnectorOrientation.Bottom: return p.Add(0, margin);
|
||||
case ConnectorOrientation.Left: return p.Add(-margin, 0);
|
||||
default: throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
private static PointBase[] ShortestPath(PointGraph graph, PointBase origin, PointBase destination)
|
||||
{
|
||||
var originNode = graph.Get(origin);
|
||||
var destinationNode = graph.Get(destination);
|
||||
|
||||
if (originNode == null || destinationNode == null)
|
||||
throw new Exception("Origin node or Destination node not found");
|
||||
|
||||
graph.CalculateShortestPathFromSource(graph, originNode);
|
||||
return destinationNode.ShortestPath.Select(n => n.Data).ToArray();
|
||||
}
|
||||
|
||||
private static PointBase[] SimplifyPath(PointBase[] points)
|
||||
{
|
||||
if (points.Length <= 2)
|
||||
{
|
||||
return points;
|
||||
}
|
||||
|
||||
var r = new List<PointBase>() { points[0] };
|
||||
for (var i = 1; i < points.Length; i++)
|
||||
{
|
||||
var cur = points[i];
|
||||
if (i == (points.Length - 1))
|
||||
{
|
||||
r.Add(cur);
|
||||
break;
|
||||
}
|
||||
|
||||
var prev = points[i - 1];
|
||||
var next = points[i + 1];
|
||||
var bend = GetBend(prev, cur, next);
|
||||
|
||||
if (bend != "none")
|
||||
{
|
||||
r.Add(cur);
|
||||
}
|
||||
}
|
||||
|
||||
return r.ToArray();
|
||||
}
|
||||
|
||||
private static string GetBend(PointBase a, PointBase b, PointBase c)
|
||||
{
|
||||
var equalX = a.X == b.X && b.X == c.X;
|
||||
var equalY = a.Y == b.Y && b.Y == c.Y;
|
||||
var segment1Horizontal = a.Y == b.Y;
|
||||
var segment1Vertical = a.X == b.X;
|
||||
var segment2Horizontal = b.Y == c.Y;
|
||||
var segment2Vertical = b.X == c.X;
|
||||
|
||||
if (equalX || equalY)
|
||||
{
|
||||
return "none";
|
||||
}
|
||||
|
||||
if (
|
||||
!(segment1Vertical || segment1Horizontal) ||
|
||||
!(segment2Vertical || segment2Horizontal)
|
||||
)
|
||||
{
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
if (segment1Horizontal && segment2Vertical)
|
||||
{
|
||||
return c.Y > b.Y ? "s" : "n";
|
||||
|
||||
}
|
||||
else if (segment1Vertical && segment2Horizontal)
|
||||
{
|
||||
return c.X > b.X ? "e" : "w";
|
||||
}
|
||||
|
||||
throw new Exception("Nope");
|
||||
}
|
||||
|
||||
class Grid
|
||||
{
|
||||
public Grid()
|
||||
{
|
||||
Data = new Dictionary<double, Dictionary<double, RectangleBase>>();
|
||||
}
|
||||
|
||||
public Dictionary<double, Dictionary<double, RectangleBase>> Data
|
||||
{
|
||||
get;
|
||||
}
|
||||
public double Rows
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
public double Columns
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
|
||||
public void Set(double row, double column, RectangleBase rectangle)
|
||||
{
|
||||
Rows = Math.Max(Rows, row + 1);
|
||||
Columns = Math.Max(Columns, column + 1);
|
||||
|
||||
if (!Data.ContainsKey(row))
|
||||
{
|
||||
Data.Add(row, new Dictionary<double, RectangleBase>());
|
||||
}
|
||||
|
||||
Data[row].Add(column, rectangle);
|
||||
}
|
||||
|
||||
public RectangleBase Get(double row, double column)
|
||||
{
|
||||
if (!Data.ContainsKey(row))
|
||||
return RectangleBase.Empty;
|
||||
|
||||
if (!Data[row].ContainsKey(column))
|
||||
return RectangleBase.Empty;
|
||||
|
||||
return Data[row][column];
|
||||
}
|
||||
|
||||
public RectangleBase[] Rectangles() => Data.SelectMany(r => r.Value.Values).ToArray();
|
||||
}
|
||||
|
||||
class PointGraph
|
||||
{
|
||||
public readonly Dictionary<string, Dictionary<string, PointNode>> _index = new Dictionary<string, Dictionary<string, PointNode>>();
|
||||
|
||||
public void Add(PointBase p)
|
||||
{
|
||||
(var x, var y) = p;
|
||||
var xs = x.ToInvariantString();
|
||||
var ys = y.ToInvariantString();
|
||||
|
||||
if (!_index.ContainsKey(xs))
|
||||
_index.Add(xs, new Dictionary<string, PointNode>());
|
||||
|
||||
if (!_index[xs].ContainsKey(ys))
|
||||
_index[xs].Add(ys, new PointNode(p));
|
||||
}
|
||||
|
||||
private PointNode GetLowestDistanceNode(HashSet<PointNode> unsettledNodes)
|
||||
{
|
||||
PointNode lowestDistanceNode = null;
|
||||
var lowestDistance = double.MaxValue;
|
||||
foreach (var node in unsettledNodes)
|
||||
{
|
||||
var nodeDistance = node.Distance;
|
||||
if (nodeDistance < lowestDistance)
|
||||
{
|
||||
lowestDistance = nodeDistance;
|
||||
lowestDistanceNode = node;
|
||||
}
|
||||
}
|
||||
|
||||
return lowestDistanceNode;
|
||||
}
|
||||
|
||||
public PointGraph CalculateShortestPathFromSource(PointGraph graph, PointNode source)
|
||||
{
|
||||
source.Distance = 0;
|
||||
var settledNodes = new HashSet<PointNode>();
|
||||
var unsettledNodes = new HashSet<PointNode>
|
||||
{
|
||||
source
|
||||
};
|
||||
|
||||
while (unsettledNodes.Count != 0)
|
||||
{
|
||||
var currentNode = GetLowestDistanceNode(unsettledNodes);
|
||||
unsettledNodes.Remove(currentNode);
|
||||
|
||||
foreach (var keyValuePair in currentNode.AdjacentNodes)
|
||||
{
|
||||
var adjacentNode = keyValuePair.Key;
|
||||
var edgeWeight = keyValuePair.Value;
|
||||
if (!settledNodes.Contains(adjacentNode))
|
||||
{
|
||||
CalculateMinimumDistance(adjacentNode, edgeWeight, currentNode);
|
||||
unsettledNodes.Add(adjacentNode);
|
||||
}
|
||||
|
||||
}
|
||||
settledNodes.Add(currentNode);
|
||||
}
|
||||
|
||||
return graph;
|
||||
}
|
||||
|
||||
private void CalculateMinimumDistance(PointNode evaluationNode, double edgeWeight, PointNode sourceNode)
|
||||
{
|
||||
var sourceDistance = sourceNode.Distance;
|
||||
var comingDirection = InferPathDirection(sourceNode);
|
||||
var goingDirection = DirectionOfNodes(sourceNode, evaluationNode);
|
||||
var changingDirection = comingDirection != null && goingDirection != null && comingDirection != goingDirection;
|
||||
var extraWeigh = changingDirection ? Math.Pow(edgeWeight + 1, 2) : 0;
|
||||
|
||||
if (sourceDistance + edgeWeight + extraWeigh < evaluationNode.Distance)
|
||||
{
|
||||
evaluationNode.Distance = sourceDistance + edgeWeight + extraWeigh;
|
||||
var shortestPath = new List<PointNode>();
|
||||
shortestPath.AddRange(sourceNode.ShortestPath);
|
||||
shortestPath.Add(sourceNode);
|
||||
evaluationNode.ShortestPath = shortestPath;
|
||||
}
|
||||
}
|
||||
|
||||
private char? DirectionOf(PointBase a, PointBase b)
|
||||
{
|
||||
if (a.X == b.X) return 'h';
|
||||
else if (a.Y == b.Y) return 'v';
|
||||
return null;
|
||||
}
|
||||
|
||||
private char? DirectionOfNodes(PointNode a, PointNode b) => DirectionOf(a.Data, b.Data);
|
||||
|
||||
private char? InferPathDirection(PointNode node)
|
||||
{
|
||||
if (node.ShortestPath.Count == 0)
|
||||
return null;
|
||||
|
||||
return DirectionOfNodes(node.ShortestPath[node.ShortestPath.Count - 1], node);
|
||||
}
|
||||
|
||||
public void Connect(PointBase a, PointBase b)
|
||||
{
|
||||
var nodeA = Get(a);
|
||||
var nodeB = Get(b);
|
||||
|
||||
if (nodeA == null || nodeB == null)
|
||||
return;
|
||||
|
||||
nodeA.AdjacentNodes.Add(nodeB, a.DistanceTo(b));
|
||||
}
|
||||
|
||||
public bool Has(PointBase p)
|
||||
{
|
||||
(var x, var y) = p;
|
||||
var xs = x.ToInvariantString();
|
||||
var ys = y.ToInvariantString();
|
||||
return _index.ContainsKey(xs) && _index[xs].ContainsKey(ys);
|
||||
}
|
||||
|
||||
public PointNode Get(PointBase p)
|
||||
{
|
||||
(var x, var y) = p;
|
||||
var xs = x.ToInvariantString();
|
||||
var ys = y.ToInvariantString();
|
||||
|
||||
if (_index.ContainsKey(xs) && _index[xs].ContainsKey(ys))
|
||||
return _index[xs][ys];
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class PointNode
|
||||
{
|
||||
public PointNode(PointBase data)
|
||||
{
|
||||
Data = data;
|
||||
}
|
||||
|
||||
public PointBase Data
|
||||
{
|
||||
get;
|
||||
}
|
||||
public double Distance { get; set; } = double.MaxValue;
|
||||
public List<PointNode> ShortestPath { get; set; } = new List<PointNode>();
|
||||
public Dictionary<PointNode, double> AdjacentNodes { get; set; } = new Dictionary<PointNode, double>();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
32
AIStudio.Wpf.DiagramDesigner/Routers/Routers.Utils.cs
Normal file
32
AIStudio.Wpf.DiagramDesigner/Routers/Routers.Utils.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using AIStudio.Wpf.DiagramDesigner.Geometry;
|
||||
using AIStudio.Wpf.DiagramDesigner.Models;
|
||||
|
||||
namespace AIStudio.Wpf.DiagramDesigner
|
||||
{
|
||||
public static partial class Routers
|
||||
{
|
||||
public static PointBase GetPortPositionBasedOnAlignment(ConnectorInfoBase port)
|
||||
{
|
||||
var pt = port.Position;
|
||||
switch (port.Orientation)
|
||||
{
|
||||
case ConnectorOrientation.Top:
|
||||
return new PointBase(pt.X + port.ConnectorWidth / 2, pt.Y);
|
||||
case ConnectorOrientation.TopRight:
|
||||
return new PointBase(pt.X + port.ConnectorWidth, pt.Y);
|
||||
case ConnectorOrientation.Right:
|
||||
return new PointBase(pt.X + port.ConnectorWidth, pt.Y + port.ConnectorHeight / 2);
|
||||
case ConnectorOrientation.BottomRight:
|
||||
return new PointBase(pt.X + port.ConnectorWidth, pt.Y + port.ConnectorHeight);
|
||||
case ConnectorOrientation.Bottom:
|
||||
return new PointBase(pt.X + port.ConnectorWidth / 2, pt.Y + port.ConnectorHeight);
|
||||
case ConnectorOrientation.BottomLeft:
|
||||
return new PointBase(pt.X, pt.Y + port.ConnectorHeight);
|
||||
case ConnectorOrientation.Left:
|
||||
return new PointBase(pt.X, pt.Y + port.ConnectorHeight / 2);
|
||||
default:
|
||||
return pt;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -540,12 +540,12 @@
|
||||
<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}}" />-->
|
||||
<Path x:Name="poly" Stroke="{Binding ColorViewModel.LineColor,Converter={StaticResource ColorBrushConverter}}"
|
||||
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>
|
||||
@@ -554,7 +554,7 @@
|
||||
<Binding Path="VectorLineDrawMode"/>
|
||||
</MultiBinding>
|
||||
</Path.Data>
|
||||
</Path>
|
||||
</Path>-->
|
||||
<Path x:Name="rightarrow"
|
||||
Data="{Binding ColorViewModel.RightArrowPathStyle,Converter={StaticResource ArrowPathConverter}}"
|
||||
Visibility="{Binding Path=IsFullConnection, Converter={x:Static s:BoolToVisibilityConverter.Instance}}"
|
||||
|
||||
@@ -106,7 +106,7 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
}
|
||||
}
|
||||
|
||||
private double _lineWidth = 1;
|
||||
private double _lineWidth = 1d;
|
||||
public double LineWidth
|
||||
{
|
||||
get
|
||||
|
||||
@@ -4,12 +4,17 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using AIStudio.Wpf.DiagramDesigner.Geometry;
|
||||
|
||||
namespace AIStudio.Wpf.DiagramDesigner
|
||||
{
|
||||
public abstract class ConnectorInfoBase : BindableBase
|
||||
{
|
||||
|
||||
public virtual PointBase Position
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public ConnectorInfoBase(ConnectorOrientation orientation)
|
||||
{
|
||||
this.Orientation = orientation;
|
||||
@@ -23,7 +28,10 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
private ConnectorOrientation _orientation;
|
||||
public ConnectorOrientation Orientation
|
||||
{
|
||||
get { return _orientation; }
|
||||
get
|
||||
{
|
||||
return _orientation;
|
||||
}
|
||||
set
|
||||
{
|
||||
SetProperty(ref _orientation, value);
|
||||
@@ -33,15 +41,27 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
private double connectorWidth = 8;
|
||||
public double ConnectorWidth
|
||||
{
|
||||
get { return connectorWidth; }
|
||||
set { connectorWidth = value; }
|
||||
get
|
||||
{
|
||||
return connectorWidth;
|
||||
}
|
||||
set
|
||||
{
|
||||
connectorWidth = value;
|
||||
}
|
||||
}
|
||||
|
||||
private double connectorHeight = 8;
|
||||
public double ConnectorHeight
|
||||
{
|
||||
get { return connectorHeight; }
|
||||
set { connectorHeight = value; }
|
||||
get
|
||||
{
|
||||
return connectorHeight;
|
||||
}
|
||||
set
|
||||
{
|
||||
connectorHeight = value;
|
||||
}
|
||||
}
|
||||
|
||||
private IColorViewModel _colorViewModel;
|
||||
@@ -68,6 +88,6 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
{
|
||||
SetProperty(ref _connectorValue, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,14 +4,13 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using AIStudio.Wpf.DiagramDesigner.Geometry;
|
||||
|
||||
namespace AIStudio.Wpf.DiagramDesigner
|
||||
{
|
||||
public class PointInfoBase : BindableBase
|
||||
public class ConnectorPoint : BindableBase
|
||||
{
|
||||
public static PointInfoBase Zero { get; } = new PointInfoBase(0, 0);
|
||||
|
||||
public PointInfoBase()
|
||||
public ConnectorPoint()
|
||||
{
|
||||
ColorViewModel = new ColorViewModel()
|
||||
{
|
||||
@@ -20,18 +19,21 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
};
|
||||
}
|
||||
|
||||
public PointInfoBase(Point point) : this()
|
||||
public ConnectorPoint(PointBase point) : this()
|
||||
{
|
||||
X = point.X;
|
||||
Y = point.Y;
|
||||
}
|
||||
|
||||
public PointInfoBase(double x, double y) : this()
|
||||
public ConnectorPoint(double x, double y) : this()
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 中间X
|
||||
/// </summary>
|
||||
private double _x;
|
||||
public double X
|
||||
{
|
||||
@@ -48,6 +50,9 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 中间Y
|
||||
/// </summary>
|
||||
private double _y;
|
||||
public double Y
|
||||
{
|
||||
@@ -64,6 +69,9 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 边界Left
|
||||
/// </summary>
|
||||
public double Left
|
||||
{
|
||||
get
|
||||
@@ -72,6 +80,9 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 边界Top
|
||||
/// </summary>
|
||||
public double Top
|
||||
{
|
||||
get
|
||||
@@ -80,6 +91,15 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
}
|
||||
}
|
||||
|
||||
public PointBase Position
|
||||
{
|
||||
get
|
||||
{
|
||||
return new PointBase(Left, Top);
|
||||
}
|
||||
}
|
||||
public PointBase MiddlePosition => new PointBase(Left + ConnectorWidth / 2, Top + ConnectorHeight / 2);
|
||||
|
||||
private double connectorWidth = 8;
|
||||
public double ConnectorWidth
|
||||
{
|
||||
@@ -105,53 +125,32 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
{
|
||||
SetProperty(ref _colorViewModel, value);
|
||||
}
|
||||
}
|
||||
|
||||
public static ConnectorPoint operator -(ConnectorPoint a, ConnectorPoint b)
|
||||
{
|
||||
return new ConnectorPoint(a.X - b.X, a.Y - b.Y);
|
||||
}
|
||||
public static ConnectorPoint operator +(ConnectorPoint a, ConnectorPoint b)
|
||||
{
|
||||
return new ConnectorPoint(a.X + b.X, a.Y + b.Y);
|
||||
}
|
||||
|
||||
public double Dot(PointInfoBase other) => X * other.X + Y * other.Y;
|
||||
|
||||
public PointInfoBase Lerp(PointInfoBase other, double t)
|
||||
=> new PointInfoBase(X * (1.0 - t) + other.X * t, Y * (1.0 - t) + other.Y * t);
|
||||
|
||||
// Maybe just make Points mutable?
|
||||
public PointInfoBase Add(double value) => new PointInfoBase(X + value, Y + value);
|
||||
public PointInfoBase Add(double x, double y) => new PointInfoBase(X + x, Y + y);
|
||||
|
||||
public PointInfoBase Substract(double value) => new PointInfoBase(X - value, Y - value);
|
||||
public PointInfoBase Substract(double x, double y) => new PointInfoBase(X - x, Y - y);
|
||||
|
||||
public double DistanceTo(PointInfoBase other)
|
||||
=> Math.Sqrt(Math.Pow(X - other.X, 2) + Math.Pow(Y - other.Y, 2));
|
||||
|
||||
public void Deconstruct(out double x, out double y)
|
||||
public static implicit operator ConnectorPoint(PointBase point)
|
||||
{
|
||||
x = X;
|
||||
y = Y;
|
||||
}
|
||||
|
||||
public static PointInfoBase operator -(PointInfoBase a, PointInfoBase b)
|
||||
{
|
||||
return new Point(a.X - b.X, a.Y - b.Y);
|
||||
}
|
||||
public static PointInfoBase operator +(PointInfoBase a, PointInfoBase b)
|
||||
{
|
||||
return new Point(a.X + b.X, a.Y + b.Y);
|
||||
return new ConnectorPoint(point);
|
||||
}
|
||||
|
||||
public static implicit operator PointInfoBase(Point point)
|
||||
public static implicit operator PointBase(ConnectorPoint pointInfoBase)
|
||||
{
|
||||
return new PointInfoBase(point);
|
||||
return new PointBase(pointInfoBase.X, pointInfoBase.Y);
|
||||
}
|
||||
|
||||
public static implicit operator Point(PointInfoBase pointInfoBase)
|
||||
public static List<ConnectorPoint> ToList(List<PointBase> lst)
|
||||
{
|
||||
return new Point(pointInfoBase.X, pointInfoBase.Y);
|
||||
return lst.Select(p => (ConnectorPoint)p).ToList();
|
||||
}
|
||||
|
||||
public static List<PointInfoBase> ToList(List<Point> lst)
|
||||
{
|
||||
return lst.Select(p => (PointInfoBase)p).ToList();
|
||||
}
|
||||
|
||||
public override string ToString() => $"PointInfoBase(x={X}, y={Y})";
|
||||
public override string ToString() => $"ConnectorPoint(x={X}, y={Y})";
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,9 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
//using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using AIStudio.Wpf.DiagramDesigner.Geometry;
|
||||
using AIStudio.Wpf.DiagramDesigner.Helpers;
|
||||
|
||||
namespace AIStudio.Wpf.DiagramDesigner
|
||||
@@ -32,7 +33,7 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
|
||||
public override SelectableDesignerItemBase ToXmlObject()
|
||||
{
|
||||
if (SinkConnectorInfo is FullyCreatedConnectorInfo sinkConnector)
|
||||
if (IsFullConnection)
|
||||
{
|
||||
ConnectionItem connection = new ConnectionItem(
|
||||
SourceConnectorInfo.DataItem.Id,
|
||||
@@ -41,12 +42,12 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
GetXRatioFromConnector(SourceConnectorInfo),
|
||||
GetYRatioFromConnector(SourceConnectorInfo),
|
||||
SourceConnectorInfo.IsInnerPoint,
|
||||
sinkConnector.DataItem.Id,
|
||||
sinkConnector.Orientation,
|
||||
sinkConnector.DataItem.GetType(),
|
||||
GetXRatioFromConnector(sinkConnector),
|
||||
GetYRatioFromConnector(sinkConnector),
|
||||
sinkConnector.IsInnerPoint,
|
||||
SinkConnectorInfoFully.DataItem.Id,
|
||||
SinkConnectorInfoFully.Orientation,
|
||||
SinkConnectorInfoFully.DataItem.GetType(),
|
||||
GetXRatioFromConnector(SinkConnectorInfoFully),
|
||||
GetYRatioFromConnector(SinkConnectorInfoFully),
|
||||
SinkConnectorInfoFully.IsInnerPoint,
|
||||
this);
|
||||
|
||||
return connection;
|
||||
@@ -67,16 +68,8 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
get; set;
|
||||
}
|
||||
|
||||
public bool IsFullConnection
|
||||
{
|
||||
get
|
||||
{
|
||||
return _sinkConnectorInfo is FullyCreatedConnectorInfo;
|
||||
}
|
||||
}
|
||||
|
||||
private Point _sourceA;
|
||||
public Point SourceA
|
||||
private PointBase _sourceA;
|
||||
public PointBase SourceA
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -91,8 +84,8 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
}
|
||||
}
|
||||
|
||||
private Point _sourceB;
|
||||
public Point SourceB
|
||||
private PointBase _sourceB;
|
||||
public PointBase SourceB
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -107,8 +100,8 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
}
|
||||
}
|
||||
|
||||
private List<PointInfoBase> _connectionPoints;
|
||||
public List<PointInfoBase> ConnectionPoints
|
||||
private List<ConnectorPoint> _connectionPoints;
|
||||
public List<ConnectorPoint> ConnectionPoints
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -128,8 +121,8 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
}
|
||||
}
|
||||
|
||||
private Point _startPoint;
|
||||
public Point StartPoint
|
||||
private PointBase _startPoint;
|
||||
public PointBase StartPoint
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -141,8 +134,8 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
}
|
||||
}
|
||||
|
||||
private Point _endPoint;
|
||||
public Point EndPoint
|
||||
private PointBase _endPoint;
|
||||
public PointBase EndPoint
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -154,8 +147,8 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
}
|
||||
}
|
||||
|
||||
private Rect _area;
|
||||
public Rect Area
|
||||
private RectangleBase _area;
|
||||
public RectangleBase Area
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -163,7 +156,7 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
}
|
||||
private set
|
||||
{
|
||||
Rect oldarea = _area;
|
||||
RectangleBase oldarea = _area;
|
||||
if (SetProperty(ref _area, value))
|
||||
{
|
||||
UpdateConnectionPoints();
|
||||
@@ -177,6 +170,10 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
get; set;
|
||||
}
|
||||
|
||||
//待完善这两处
|
||||
public List<LinkVertexModel> Vertices { get; } = new List<LinkVertexModel>();
|
||||
public List<LinkLabelModel> Labels { get; set; } = new List<LinkLabelModel>();
|
||||
|
||||
public virtual Dictionary<string, string> PropertiesSetting
|
||||
{
|
||||
get
|
||||
@@ -188,13 +185,13 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
}
|
||||
}
|
||||
|
||||
public ConnectorInfo ConnectorInfo(ConnectorOrientation orientation, double left, double top, double width, double height, Point position)
|
||||
public ConnectorInfo ConnectorInfo(ConnectorOrientation orientation, double left, double top, double width, double height, PointBase position)
|
||||
{
|
||||
|
||||
return new ConnectorInfo()
|
||||
{
|
||||
Orientation = orientation,
|
||||
DesignerItemSize = new Size(width, height),
|
||||
DesignerItemSize = new SizeBase(width, height),
|
||||
DesignerItemLeft = left,
|
||||
DesignerItemTop = top,
|
||||
Position = position
|
||||
@@ -237,12 +234,46 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
}
|
||||
else
|
||||
{
|
||||
SourceB = ((PartCreatedConnectionInfo)SinkConnectorInfo).CurrentLocation;
|
||||
SourceB = SinkConnectorInfoPart.Position;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public FullyCreatedConnectorInfo SinkConnectorInfoFully
|
||||
{
|
||||
get
|
||||
{
|
||||
return SinkConnectorInfo as FullyCreatedConnectorInfo;
|
||||
}
|
||||
}
|
||||
|
||||
public PartCreatedConnectionInfo SinkConnectorInfoPart
|
||||
{
|
||||
get
|
||||
{
|
||||
return SinkConnectorInfo as PartCreatedConnectionInfo;
|
||||
}
|
||||
}
|
||||
|
||||
public ConnectorPoint OnGoingPosition
|
||||
{
|
||||
get
|
||||
{
|
||||
return SinkConnectorInfoPart?.Position;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsFullConnection
|
||||
{
|
||||
get
|
||||
{
|
||||
return SinkConnectorInfoFully != null;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsPortless => SourceConnectorInfo?.DataItem?.Connectors?.Count() == 0;
|
||||
|
||||
public double GetXRatioFromConnector(FullyCreatedConnectorInfo info)
|
||||
{
|
||||
if (info.IsInnerPoint)
|
||||
@@ -291,7 +322,7 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
|
||||
private void UpdateArea()
|
||||
{
|
||||
Area = new Rect(SourceA, SourceB);
|
||||
Area = new RectangleBase(SourceA, SourceB);
|
||||
}
|
||||
|
||||
private void UpdateConnectionPoints()
|
||||
@@ -300,6 +331,9 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
StartPoint = ConnectionPoints.First();
|
||||
EndPoint = ConnectionPoints.Last();
|
||||
|
||||
//var router = Routers.Normal(Parent, this);
|
||||
//var pathGenerator = PathGenerators.Smooth(Parent, this, router, SourceA, SourceB);
|
||||
|
||||
}
|
||||
|
||||
private void ConnectorViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
@@ -311,9 +345,9 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
case "Left":
|
||||
case "Top":
|
||||
SourceA = PointHelper.GetPointForConnector(this.SourceConnectorInfo);
|
||||
if (this.SinkConnectorInfo is FullyCreatedConnectorInfo)
|
||||
if (IsFullConnection)
|
||||
{
|
||||
SourceB = PointHelper.GetPointForConnector((FullyCreatedConnectorInfo)this.SinkConnectorInfo);
|
||||
SourceB = PointHelper.GetPointForConnector(this.SinkConnectorInfoFully);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -365,6 +399,7 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
private void DeleteConnection(object args)
|
||||
{
|
||||
if (this.Parent is IDiagramViewModel)
|
||||
@@ -414,17 +449,46 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
}
|
||||
}
|
||||
|
||||
public void OutTextItemLocation(Rect oldArea, Rect newArea)
|
||||
public void OutTextItemLocation(RectangleBase oldArea, RectangleBase newArea)
|
||||
{
|
||||
if (this.OutTextItem is TextDesignerItemViewModel text)
|
||||
{
|
||||
var oldpoint = new Point(oldArea.Left + oldArea.Width / 2, oldArea.Top + oldArea.Height / 2);
|
||||
var newpoint = new Point(newArea.Left + newArea.Width / 2, newArea.Top + newArea.Height / 2);
|
||||
var oldpoint = new PointBase(oldArea.Left + oldArea.Width / 2, oldArea.Top + oldArea.Height / 2);
|
||||
var newpoint = new PointBase(newArea.Left + newArea.Width / 2, newArea.Top + newArea.Height / 2);
|
||||
|
||||
text.Left = text.Left + newpoint.X - oldpoint.X;
|
||||
text.Top = text.Top + newpoint.Y - oldpoint.Y;
|
||||
}
|
||||
}
|
||||
|
||||
//private (PointInfoBase source, PointInfoBase target) FindConnectionPoints(PointInfoBase[] route)
|
||||
//{
|
||||
// if (IsPortless) // Portless
|
||||
// {
|
||||
// if (SourceConnectorInfo.DataItem == null || (IsFullConnection && SinkConnectorInfoFully.DataItem == null))
|
||||
// return (null, null);
|
||||
|
||||
// var sourceCenter = SourceConnectorInfo.DataItem.GetBounds().Center;
|
||||
// var targetCenter = SinkConnectorInfoFully?.DataItem?.GetBounds().Center ?? SinkConnectorInfoFully.Position;
|
||||
// var firstPt = route.Length > 0 ? route[0] : targetCenter;
|
||||
// var secondPt = route.Length > 0 ? route[0] : sourceCenter;
|
||||
// var sourceLine = new Line(firstPt, sourceCenter);
|
||||
// var targetLine = new Line(secondPt, targetCenter);
|
||||
// var sourceIntersections = Link.SourceNode.GetShape().GetIntersectionsWithLine(sourceLine);
|
||||
// var targetIntersections = Link.TargetNode.GetShape().GetIntersectionsWithLine(targetLine);
|
||||
// var sourceIntersection = GetClosestPointTo(sourceIntersections, firstPt);
|
||||
// var targetIntersection = GetClosestPointTo(targetIntersections, secondPt);
|
||||
// return (sourceIntersection ?? sourceCenter, targetIntersection ?? targetCenter);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// if (!Link.SourcePort.Initialized || Link.TargetPort?.Initialized == false)
|
||||
// return (null, null);
|
||||
|
||||
// var source = GetPortPositionBasedOnAlignment(Link.SourcePort, Link.SourceMarker);
|
||||
// var target = GetPortPositionBasedOnAlignment(Link.TargetPort, Link.TargetMarker);
|
||||
// return (source, target ?? Link.OnGoingPosition);
|
||||
// }
|
||||
//}
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ using System.Windows;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using AIStudio.Wpf.DiagramDesigner.Models;
|
||||
using AIStudio.Wpf.DiagramDesigner.Geometry;
|
||||
|
||||
namespace AIStudio.Wpf.DiagramDesigner
|
||||
{
|
||||
@@ -85,6 +86,10 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
get { return (connectors != null && connectors.Count >= 4) ? connectors[3] : null; }
|
||||
}
|
||||
|
||||
public ShapeDefiner ShapeDefiner
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
private string _icon;
|
||||
[CanDo]
|
||||
@@ -133,16 +138,16 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
}
|
||||
|
||||
[CanDo]
|
||||
public Point ItemWidthHeight
|
||||
public SizeBase Size
|
||||
{
|
||||
get
|
||||
{
|
||||
return new Point(ItemWidth, ItemHeight);
|
||||
return new SizeBase(ItemWidth, ItemHeight);
|
||||
}
|
||||
set
|
||||
{
|
||||
ItemWidth = value.X;
|
||||
ItemHeight = value.Y;
|
||||
ItemWidth = value.Width;
|
||||
ItemHeight = value.Height;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,14 +216,22 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
{
|
||||
SetProperty(ref _top, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[CanDo]
|
||||
public Point TopLeft
|
||||
public PointBase Position
|
||||
{
|
||||
get
|
||||
{
|
||||
return new Point(Left, Top);
|
||||
return new PointBase(Left, Top);
|
||||
}
|
||||
}
|
||||
|
||||
[CanDo]
|
||||
public PointBase TopLeft
|
||||
{
|
||||
get
|
||||
{
|
||||
return new PointBase(Left, Top);
|
||||
}
|
||||
set
|
||||
{
|
||||
@@ -386,17 +399,37 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
|
||||
public void RaiseTopLeft()
|
||||
{
|
||||
this.RaisePropertyChanged(nameof(TopLeft), new Point(GetOldValue<double>(nameof(Left)), GetOldValue<double>(nameof(Top))), TopLeft);
|
||||
this.RaisePropertyChanged(nameof(TopLeft), new PointBase(GetOldValue<double>(nameof(Left)), GetOldValue<double>(nameof(Top))), TopLeft);
|
||||
}
|
||||
|
||||
public void RaiseItemWidthHeight()
|
||||
{
|
||||
this.RaisePropertyChanged(nameof(ItemWidthHeight), new Point(GetOldValue<double>(nameof(ItemWidth)), GetOldValue<double>(nameof(ItemHeight))), ItemWidthHeight);
|
||||
this.RaisePropertyChanged(nameof(Size), new SizeBase(GetOldValue<double>(nameof(ItemWidth)), GetOldValue<double>(nameof(ItemHeight))), Size);
|
||||
}
|
||||
|
||||
public void RaiseAngle()
|
||||
{
|
||||
this.RaisePropertyChanged(nameof(Angle), GetOldValue<double>(nameof(Angle)), Angle);
|
||||
}
|
||||
|
||||
public RectangleBase GetBounds(bool includePorts = false)
|
||||
{
|
||||
if (!includePorts)
|
||||
return new RectangleBase(Left, Top, ItemWidth, ItemHeight);
|
||||
|
||||
var leftPort = LeftConnector;
|
||||
var topPort = TopConnector;
|
||||
var rightPort = RightConnector;
|
||||
var bottomPort = BottomConnector;
|
||||
|
||||
var left = leftPort == null ? Left: Math.Min(Left, leftPort.Position.X);
|
||||
var top = topPort == null ? Top : Math.Min(Left, topPort.Position.Y);
|
||||
var right = rightPort == null ? Left + ItemWidth :
|
||||
Math.Max(rightPort.Position.X + rightPort.ConnectorWidth, Left + ItemWidth);
|
||||
var bottom = bottomPort == null ? Top + ItemHeight :
|
||||
Math.Max(bottomPort.Position.Y + bottomPort.ConnectorHeight, Top + ItemHeight);
|
||||
|
||||
return new RectangleBase(left, top, right, bottom);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ using System.Runtime.InteropServices;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using AIStudio.Wpf.DiagramDesigner.Geometry;
|
||||
using AIStudio.Wpf.DiagramDesigner.Helpers;
|
||||
using AIStudio.Wpf.DiagramDesigner.Models;
|
||||
using Newtonsoft.Json;
|
||||
@@ -324,9 +325,9 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
}
|
||||
}
|
||||
|
||||
private Point _currentPoint;
|
||||
private System.Windows.Point _currentPoint;
|
||||
[Browsable(false)]
|
||||
public Point CurrentPoint
|
||||
public System.Windows.Point CurrentPoint
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -1393,7 +1394,7 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
select item).FirstOrDefault();
|
||||
|
||||
DesignerItemViewModelBase sinkItem = (from item in selectedDesignerItems
|
||||
where item.Id == ((connection.SinkConnectorInfo as FullyCreatedConnectorInfo).DataItem).Id
|
||||
where item.Id == connection.SinkConnectorInfoFully?.DataItem?.Id
|
||||
select item).FirstOrDefault();
|
||||
|
||||
if (sourceItem != null &&
|
||||
@@ -1423,15 +1424,15 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
|
||||
OffsetX = 10;
|
||||
OffsetY = 10;
|
||||
Clipboard.Clear();
|
||||
Clipboard.SetData(DataFormats.Serializable, json);
|
||||
System.Windows.Clipboard.Clear();
|
||||
System.Windows.Clipboard.SetData(System.Windows.DataFormats.Serializable, json);
|
||||
}
|
||||
|
||||
private void ExecutePasteCommand(object parameter)
|
||||
{
|
||||
if (Clipboard.ContainsData(DataFormats.Serializable))
|
||||
if (System.Windows.Clipboard.ContainsData(System.Windows.DataFormats.Serializable))
|
||||
{
|
||||
String clipboardData = Clipboard.GetData(DataFormats.Serializable) as String;
|
||||
String clipboardData = System.Windows.Clipboard.GetData(System.Windows.DataFormats.Serializable) as String;
|
||||
|
||||
if (String.IsNullOrEmpty(clipboardData))
|
||||
return;
|
||||
@@ -1512,7 +1513,7 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
MessageBox.Show(e.StackTrace, e.Message, MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
System.Windows.MessageBox.Show(e.StackTrace, e.Message, System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1683,7 +1684,7 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
where item.ParentId == Guid.Empty
|
||||
select item;
|
||||
|
||||
Rect rect = GetBoundingRectangle(items);
|
||||
RectangleBase rect = GetBoundingRectangle(items);
|
||||
|
||||
GroupDesignerItemViewModel groupItem = new GroupDesignerItemViewModel();
|
||||
groupItem.IsGroup = true;
|
||||
@@ -1752,7 +1753,7 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
}
|
||||
}
|
||||
|
||||
public Rect GetBoundingRectangle(IEnumerable<DesignerItemViewModelBase> items)
|
||||
public RectangleBase GetBoundingRectangle(IEnumerable<DesignerItemViewModelBase> items)
|
||||
{
|
||||
double x1 = Double.MaxValue;
|
||||
double y1 = Double.MaxValue;
|
||||
@@ -1768,7 +1769,7 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
y2 = Math.Max(item.Top + item.ItemHeight, y2);
|
||||
}
|
||||
|
||||
return new Rect(new Point(x1, y1), new Point(x2, y2));
|
||||
return new RectangleBase(new PointBase(x1, y1), new PointBase(x2, y2));
|
||||
}
|
||||
|
||||
#region 用于wpf大小与物理像素之间转换
|
||||
@@ -3,11 +3,19 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Windows;
|
||||
using AIStudio.Wpf.DiagramDesigner.Geometry;
|
||||
|
||||
namespace AIStudio.Wpf.DiagramDesigner
|
||||
{
|
||||
public class FullyCreatedConnectorInfo : ConnectorInfoBase
|
||||
{
|
||||
public override PointBase Position
|
||||
{
|
||||
get
|
||||
{
|
||||
return PointHelper.GetPointForConnector(this);
|
||||
}
|
||||
}
|
||||
|
||||
private List<CinchMenuItem> menuOptions;
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
namespace AIStudio.Wpf.DiagramDesigner
|
||||
{
|
||||
public class LinkLabelModel : ConnectorPoint
|
||||
{
|
||||
public LinkLabelModel(ConnectorViewModel parent, string id, string content, double? distance = null, ConnectorPoint offset = null)
|
||||
{
|
||||
Parent = parent;
|
||||
Content = content;
|
||||
Distance = distance;
|
||||
Offset = offset;
|
||||
}
|
||||
|
||||
public LinkLabelModel(ConnectorViewModel parent, string content, double? distance = null, ConnectorPoint offset = null)
|
||||
{
|
||||
Parent = parent;
|
||||
Content = content;
|
||||
Distance = distance;
|
||||
Offset = offset;
|
||||
}
|
||||
|
||||
public ConnectorViewModel Parent { get; }
|
||||
public string Content { get; set; }
|
||||
/// <summary>
|
||||
/// 3 types of values are possible:
|
||||
/// <para>- A number between 0 and 1: Position relative to the link's length</para>
|
||||
/// <para>- A positive number, greater than 1: Position away from the start</para>
|
||||
/// <para>- A negative number, less than 0: Position away from the end</para>
|
||||
/// </summary>
|
||||
public double? Distance { get; set; }
|
||||
public ConnectorPoint Offset { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using AIStudio.Wpf.DiagramDesigner.Geometry;
|
||||
|
||||
namespace AIStudio.Wpf.DiagramDesigner
|
||||
{
|
||||
public class LinkVertexModel : ConnectorPoint
|
||||
{
|
||||
public LinkVertexModel(ConnectorViewModel parent, PointBase? position = null)
|
||||
{
|
||||
Parent = parent;
|
||||
X = position?.X ?? 0;
|
||||
Y = position?.Y ?? 0;
|
||||
}
|
||||
|
||||
public ConnectorViewModel Parent
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Windows;
|
||||
using AIStudio.Wpf.DiagramDesigner.Geometry;
|
||||
|
||||
namespace AIStudio.Wpf.DiagramDesigner
|
||||
{
|
||||
public class PartCreatedConnectionInfo : ConnectorInfoBase
|
||||
{
|
||||
private PointBase position;
|
||||
public override PointBase Position
|
||||
{
|
||||
get
|
||||
{
|
||||
return position;
|
||||
}
|
||||
}
|
||||
|
||||
public PartCreatedConnectionInfo(double X, double Y) : base(ConnectorOrientation.None)
|
||||
{
|
||||
this.position = new PointBase(X, Y);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -188,8 +188,8 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
get; set;
|
||||
}
|
||||
void ClearSelectedItems();
|
||||
bool BelongToSameGroup(IGroupable item1, IGroupable item2);
|
||||
Rect GetBoundingRectangle(IEnumerable<DesignerItemViewModelBase> items);
|
||||
//bool BelongToSameGroup(IGroupable item1, IGroupable item2);
|
||||
//Rectangle GetBoundingRectangle(IEnumerable<DesignerItemViewModelBase> items);
|
||||
void UpdateZIndex();
|
||||
|
||||
bool IsReadOnly
|
||||
@@ -241,7 +241,7 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
get; set;
|
||||
}
|
||||
|
||||
Point CurrentPoint
|
||||
System.Windows.Point CurrentPoint
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Windows;
|
||||
|
||||
namespace AIStudio.Wpf.DiagramDesigner
|
||||
{
|
||||
public class PartCreatedConnectionInfo : ConnectorInfoBase
|
||||
{
|
||||
public Point CurrentLocation { get; private set; }
|
||||
|
||||
public PartCreatedConnectionInfo(Point currentLocation) : base(ConnectorOrientation.None)
|
||||
{
|
||||
this.CurrentLocation = currentLocation;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using AIStudio.Wpf.DiagramDesigner;
|
||||
using AIStudio.Wpf.DiagramDesigner.Geometry;
|
||||
using AIStudio.Wpf.Flowchart.Models;
|
||||
|
||||
namespace AIStudio.Wpf.Flowchart.Controls
|
||||
@@ -33,12 +34,12 @@ namespace AIStudio.Wpf.Flowchart.Controls
|
||||
_diagramViewModel = new DiagramViewModel();
|
||||
_diagramViewModel.SetScreenScale();
|
||||
_diagramViewModel.ShowGrid = true;
|
||||
_diagramViewModel.GridCellSize = new Size(125 / _diagramViewModel.ScreenScale, 125 / _diagramViewModel.ScreenScale);
|
||||
_diagramViewModel.GridCellSize = new SizeBase(125 / _diagramViewModel.ScreenScale, 125 / _diagramViewModel.ScreenScale);
|
||||
_diagramViewModel.GridMargin = 0d;
|
||||
_diagramViewModel.CellHorizontalAlignment = CellHorizontalAlignment.Center;
|
||||
_diagramViewModel.CellVerticalAlignment = CellVerticalAlignment.Center;
|
||||
_diagramViewModel.PageSizeType = PageSizeType.Custom;
|
||||
_diagramViewModel.PageSize = new Size(double.NaN, double.NaN);
|
||||
_diagramViewModel.PageSize = new SizeBase(double.NaN, double.NaN);
|
||||
_diagramViewModel.ColorViewModel = new ColorViewModel() { LineWidth = 2 };
|
||||
//_diagramViewModel.DrawModeViewModel = new DrawModeViewModel() { VectorLineDrawMode = DrawMode.BoundaryConnectingLine };
|
||||
|
||||
|
||||
@@ -33,7 +33,10 @@ namespace AIStudio.Wpf.Flowchart
|
||||
private static List<SelectOption> _users;
|
||||
public static List<SelectOption> Users
|
||||
{
|
||||
get { return _users; }
|
||||
get
|
||||
{
|
||||
return _users;
|
||||
}
|
||||
set
|
||||
{
|
||||
_users = value;
|
||||
@@ -43,7 +46,10 @@ namespace AIStudio.Wpf.Flowchart
|
||||
private static List<SelectOption> _roles;
|
||||
public static List<SelectOption> Roles
|
||||
{
|
||||
get { return _roles; }
|
||||
get
|
||||
{
|
||||
return _roles;
|
||||
}
|
||||
set
|
||||
{
|
||||
_roles = value;
|
||||
@@ -67,20 +73,22 @@ namespace AIStudio.Wpf.Flowchart
|
||||
{
|
||||
foreach (var edge in connectors)
|
||||
{
|
||||
if (edge.IsFullConnection == false) continue;
|
||||
|
||||
var source = oASteps.FirstOrDefault(p => p.BottomConnector == edge.SourceConnectorInfo || p.LeftConnector == edge.SourceConnectorInfo || p.RightConnector == edge.SourceConnectorInfo);
|
||||
if (source != null)
|
||||
{
|
||||
if (source.Kind == NodeKinds.Decide)
|
||||
{
|
||||
source.SelectNextStep.Add((edge.SinkConnectorInfo as FullyCreatedConnectorInfo).DataItem.Id.ToString(), "data.Flag" + edge.Text);
|
||||
source.SelectNextStep.Add(edge.SinkConnectorInfoFully.DataItem.Id.ToString(), "data.Flag" + edge.Text);
|
||||
}
|
||||
else if (source.Kind == NodeKinds.COBegin)
|
||||
{
|
||||
source.SelectNextStep.Add((edge.SinkConnectorInfo as FullyCreatedConnectorInfo).DataItem.Id.ToString(), "True");
|
||||
source.SelectNextStep.Add(edge.SinkConnectorInfoFully.DataItem.Id.ToString(), "True");
|
||||
}
|
||||
else
|
||||
{
|
||||
source.NextStepId = (edge.SinkConnectorInfo as FullyCreatedConnectorInfo).DataItem.Id.ToString();
|
||||
source.NextStepId = edge.SinkConnectorInfoFully.DataItem.Id.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -126,7 +134,7 @@ namespace AIStudio.Wpf.Flowchart
|
||||
var nodes = InitStep(oASteps, nextstepid);
|
||||
nodes.Insert(0, oAStartStep);
|
||||
|
||||
FlowNodes.Add(viewModel, nodes);
|
||||
FlowNodes.Add(viewModel, nodes);
|
||||
|
||||
Approve(oAStartStep, 100);
|
||||
}
|
||||
@@ -213,7 +221,7 @@ namespace AIStudio.Wpf.Flowchart
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
SetStatus(flowNode, status, remark);
|
||||
@@ -287,8 +295,8 @@ namespace AIStudio.Wpf.Flowchart
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
catch (Exception ex)
|
||||
{
|
||||
}
|
||||
}
|
||||
//如果表达式错了,就按第一个处理
|
||||
@@ -372,6 +380,6 @@ namespace AIStudio.Wpf.Flowchart
|
||||
public static void DisposeData(IDiagramViewModel viewModel)
|
||||
{
|
||||
FlowNodes.Remove(viewModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,10 +75,10 @@ namespace AIStudio.Wpf.Flowchart.Models
|
||||
diagramLink.Width = linkModel.ColorViewModel.LineWidth;
|
||||
diagramLink.Label = linkModel.Text;
|
||||
|
||||
if (linkModel.SinkConnectorInfo is FullyCreatedConnectorInfo sinkConnector)
|
||||
if (linkModel.IsFullConnection)
|
||||
{
|
||||
diagramLink.SourceId = linkModel.SourceConnectorInfo.DataItem.Id.ToString();
|
||||
diagramLink.TargetId = sinkConnector.DataItem.Id.ToString();
|
||||
diagramLink.TargetId = linkModel.SinkConnectorInfoFully.DataItem.Id.ToString();
|
||||
|
||||
//线条形状与箭头待处理
|
||||
//diagramLink.Router = baseLinkModel.Router?.Method.Name;
|
||||
@@ -91,7 +91,7 @@ namespace AIStudio.Wpf.Flowchart.Models
|
||||
diagramLink.Type = diagramLink.GetType().Name;
|
||||
|
||||
diagramLink.SourcePortAlignment = linkModel.SourceConnectorInfo.Orientation.ToString();
|
||||
diagramLink.TargetPortAlignment = sinkConnector.Orientation.ToString();
|
||||
diagramLink.TargetPortAlignment = linkModel.SinkConnectorInfoFully.Orientation.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -49,11 +49,13 @@ namespace AIStudio.Wpf.SFC
|
||||
|
||||
foreach (var edge in connectors)
|
||||
{
|
||||
if (edge.IsFullConnection == false) continue;
|
||||
|
||||
var source = nodes.FirstOrDefault(p => p.BottomConnector == edge.SourceConnectorInfo || p.LeftConnector == edge.SourceConnectorInfo || p.RightConnector == edge.SourceConnectorInfo || p.TopConnector == edge.SourceConnectorInfo);
|
||||
if (source != null)
|
||||
{
|
||||
source.NextNode.Add((edge.SinkConnectorInfo as FullyCreatedConnectorInfo).DataItem as SFCNode);
|
||||
((edge.SinkConnectorInfo as FullyCreatedConnectorInfo).DataItem as SFCNode).PreNode.Add(source);
|
||||
source.NextNode.Add(edge.SinkConnectorInfoFully.DataItem as SFCNode);
|
||||
(edge.SinkConnectorInfoFully.DataItem as SFCNode).PreNode.Add(source);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user