mirror of
https://gitee.com/akwkevin/aistudio.-wpf.-diagram
synced 2026-04-08 10:16:36 +08:00
使用PointBase代替Point
This commit is contained in:
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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user