Adjust project structure
This commit is contained in:
1 parent
d38912f5da
commit
e3c436f939
2111 files changed
+14028
-47199
No files matched your search
@@ -0,0 +1,357 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows;
|
||||
|
||||
namespace HandyControl.Expression.Drawing
|
||||
{
|
||||
internal static class BezierCurveFlattener
|
||||
{
|
||||
public const double StandardFlatteningTolerance = 0.25;
|
||||
|
||||
private static void DoCubicForwardDifferencing(Point[] controlPoints, double leftParameter, double rightParameter, double inverseErrorTolerance, ICollection<Point> resultPolyline, ICollection<double> resultParameters)
|
||||
{
|
||||
double num14;
|
||||
var num = controlPoints[1].X - controlPoints[0].X;
|
||||
var num2 = controlPoints[1].Y - controlPoints[0].Y;
|
||||
var num3 = controlPoints[2].X - controlPoints[1].X;
|
||||
var num4 = controlPoints[2].Y - controlPoints[1].Y;
|
||||
var num5 = controlPoints[3].X - controlPoints[2].X;
|
||||
var num6 = controlPoints[3].Y - controlPoints[2].Y;
|
||||
var num7 = num3 - num;
|
||||
var num8 = num4 - num2;
|
||||
var num9 = num5 - num3;
|
||||
var num10 = num6 - num4;
|
||||
var num11 = num9 - num7;
|
||||
var num12 = num10 - num8;
|
||||
var vector = controlPoints[3].Subtract(controlPoints[0]);
|
||||
var length = vector.Length;
|
||||
if (!MathHelper.IsVerySmall(length))
|
||||
num14 = Math.Max(0.0,
|
||||
Math.Max(Math.Abs((num7 * vector.Y - num8 * vector.X) / length),
|
||||
Math.Abs((num9 * vector.Y - num10 * vector.X) / length)));
|
||||
else
|
||||
num14 = Math.Max(0.0,
|
||||
Math.Max(GeometryHelper.Distance(controlPoints[1], controlPoints[0]),
|
||||
GeometryHelper.Distance(controlPoints[2], controlPoints[0])));
|
||||
uint num15 = 0;
|
||||
if (num14 > 0.0)
|
||||
{
|
||||
var d = num14 * inverseErrorTolerance;
|
||||
num15 = d < 2147483647.0 ? Log4UnsignedInt32((uint) (d + 0.5)) : Log4Double(d);
|
||||
}
|
||||
|
||||
var exp = (int) -num15;
|
||||
var num18 = exp + exp;
|
||||
var num19 = num18 + exp;
|
||||
var num20 = MathHelper.DoubleFromMantissaAndExponent(3.0 * num7, num18);
|
||||
var num21 = MathHelper.DoubleFromMantissaAndExponent(3.0 * num8, num18);
|
||||
var num22 = MathHelper.DoubleFromMantissaAndExponent(6.0 * num11, num19);
|
||||
var num23 = MathHelper.DoubleFromMantissaAndExponent(6.0 * num12, num19);
|
||||
var num24 = MathHelper.DoubleFromMantissaAndExponent(3.0 * num, exp) + num20 +
|
||||
0.16666666666666666 * num22;
|
||||
var num25 = MathHelper.DoubleFromMantissaAndExponent(3.0 * num2, exp) + num21 +
|
||||
0.16666666666666666 * num23;
|
||||
var num26 = 2.0 * num20 + num22;
|
||||
var num27 = 2.0 * num21 + num23;
|
||||
var x = controlPoints[0].X;
|
||||
var y = controlPoints[0].Y;
|
||||
var item = new Point(0.0, 0.0);
|
||||
var num30 = 1 << (int) num15;
|
||||
var num31 = num30 > 0 ? (rightParameter - leftParameter) / num30 : 0.0;
|
||||
var num32 = leftParameter;
|
||||
for (var i = 1; i < num30; i++)
|
||||
{
|
||||
x += num24;
|
||||
y += num25;
|
||||
item.X = x;
|
||||
item.Y = y;
|
||||
resultPolyline.Add(item);
|
||||
num32 += num31;
|
||||
resultParameters?.Add(num32);
|
||||
num24 += num26;
|
||||
num25 += num27;
|
||||
num26 += num22;
|
||||
num27 += num23;
|
||||
}
|
||||
}
|
||||
|
||||
private static void DoCubicMidpointSubdivision(Point[] controlPoints, uint depth, double leftParameter, double rightParameter, double inverseErrorTolerance, ICollection<Point> resultPolyline, ICollection<double> resultParameters)
|
||||
{
|
||||
Point[] pointArray = { controlPoints[0], controlPoints[1], controlPoints[2], controlPoints[3] };
|
||||
var pointArray2 = new Point[4];
|
||||
pointArray2[3] = pointArray[3];
|
||||
pointArray[3] = GeometryHelper.Midpoint(pointArray[3], pointArray[2]);
|
||||
pointArray[2] = GeometryHelper.Midpoint(pointArray[2], pointArray[1]);
|
||||
pointArray[1] = GeometryHelper.Midpoint(pointArray[1], pointArray[0]);
|
||||
pointArray2[2] = pointArray[3];
|
||||
pointArray[3] = GeometryHelper.Midpoint(pointArray[3], pointArray[2]);
|
||||
pointArray[2] = GeometryHelper.Midpoint(pointArray[2], pointArray[1]);
|
||||
pointArray2[1] = pointArray[3];
|
||||
pointArray[3] = GeometryHelper.Midpoint(pointArray[3], pointArray[2]);
|
||||
pointArray2[0] = pointArray[3];
|
||||
depth--;
|
||||
var num = (leftParameter + rightParameter) * 0.5;
|
||||
if (depth > 0)
|
||||
{
|
||||
DoCubicMidpointSubdivision(pointArray, depth, leftParameter, num, inverseErrorTolerance,
|
||||
resultPolyline, resultParameters);
|
||||
resultPolyline.Add(pointArray2[0]);
|
||||
resultParameters?.Add(num);
|
||||
DoCubicMidpointSubdivision(pointArray2, depth, num, rightParameter, inverseErrorTolerance,
|
||||
resultPolyline, resultParameters);
|
||||
}
|
||||
else
|
||||
{
|
||||
DoCubicForwardDifferencing(pointArray, leftParameter, num, inverseErrorTolerance,
|
||||
resultPolyline, resultParameters);
|
||||
resultPolyline.Add(pointArray2[0]);
|
||||
resultParameters?.Add(num);
|
||||
DoCubicForwardDifferencing(pointArray2, num, rightParameter, inverseErrorTolerance,
|
||||
resultPolyline, resultParameters);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EnsureErrorTolerance(ref double errorTolerance)
|
||||
{
|
||||
if (errorTolerance <= 0.0)
|
||||
{
|
||||
errorTolerance = 0.25;
|
||||
}
|
||||
}
|
||||
|
||||
public static void FlattenCubic(Point[] controlPoints, double errorTolerance, ICollection<Point> resultPolyline, bool skipFirstPoint, ICollection<double> resultParameters = null)
|
||||
{
|
||||
if (resultPolyline == null) throw new ArgumentNullException(nameof(resultPolyline));
|
||||
if (controlPoints == null) throw new ArgumentNullException(nameof(controlPoints));
|
||||
if (controlPoints.Length != 4) throw new ArgumentOutOfRangeException(nameof(controlPoints));
|
||||
EnsureErrorTolerance(ref errorTolerance);
|
||||
if (!skipFirstPoint)
|
||||
{
|
||||
resultPolyline.Add(controlPoints[0]);
|
||||
resultParameters?.Add(0.0);
|
||||
}
|
||||
|
||||
if (IsCubicChordMonotone(controlPoints, errorTolerance * errorTolerance))
|
||||
{
|
||||
var flattener = new AdaptiveForwardDifferencingCubicFlattener(controlPoints, errorTolerance,
|
||||
errorTolerance, true);
|
||||
var p = new Point();
|
||||
var u = 0.0;
|
||||
while (flattener.Next(ref p, ref u))
|
||||
{
|
||||
resultPolyline.Add(p);
|
||||
resultParameters?.Add(u);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var x = controlPoints[3].X - controlPoints[2].X + controlPoints[1].X - controlPoints[0].X;
|
||||
var y = controlPoints[3].Y - controlPoints[2].Y + controlPoints[1].Y - controlPoints[0].Y;
|
||||
var num4 = 1.0 / errorTolerance;
|
||||
var depth = Log8UnsignedInt32((uint) (MathHelper.Hypotenuse(x, y) * num4 + 0.5));
|
||||
if (depth > 0) depth--;
|
||||
if (depth > 0)
|
||||
DoCubicMidpointSubdivision(controlPoints, depth, 0.0, 1.0, 0.75 * num4, resultPolyline,
|
||||
resultParameters);
|
||||
else
|
||||
DoCubicForwardDifferencing(controlPoints, 0.0, 1.0, 0.75 * num4, resultPolyline,
|
||||
resultParameters);
|
||||
}
|
||||
|
||||
resultPolyline.Add(controlPoints[3]);
|
||||
resultParameters?.Add(1.0);
|
||||
}
|
||||
|
||||
public static void FlattenQuadratic(Point[] controlPoints, double errorTolerance, ICollection<Point> resultPolyline, bool skipFirstPoint, ICollection<double> resultParameters = null)
|
||||
{
|
||||
if (resultPolyline == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(resultPolyline));
|
||||
}
|
||||
if (controlPoints == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(controlPoints));
|
||||
}
|
||||
if (controlPoints.Length != 3)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(controlPoints));
|
||||
}
|
||||
EnsureErrorTolerance(ref errorTolerance);
|
||||
Point[] pointArray = { controlPoints[0], GeometryHelper.Lerp(controlPoints[0], controlPoints[1], 0.66666666666666663), GeometryHelper.Lerp(controlPoints[1], controlPoints[2], 0.33333333333333331), controlPoints[2] };
|
||||
FlattenCubic(pointArray, errorTolerance, resultPolyline, skipFirstPoint, resultParameters);
|
||||
}
|
||||
|
||||
private static bool IsCubicChordMonotone(Point[] controlPoints, double squaredTolerance)
|
||||
{
|
||||
double num = GeometryHelper.SquaredDistance(controlPoints[0], controlPoints[3]);
|
||||
if (num <= squaredTolerance)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Vector lhs = controlPoints[3].Subtract(controlPoints[0]);
|
||||
Vector rhs = controlPoints[1].Subtract(controlPoints[0]);
|
||||
double num2 = GeometryHelper.Dot(lhs, rhs);
|
||||
if ((num2 < 0.0) || (num2 > num))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Vector vector3 = controlPoints[2].Subtract(controlPoints[0]);
|
||||
double num3 = GeometryHelper.Dot(lhs, vector3);
|
||||
if ((num3 < 0.0) || (num3 > num))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (num2 > num3)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static uint Log4Double(double d)
|
||||
{
|
||||
uint num = 0;
|
||||
while (d > 1.0)
|
||||
{
|
||||
d *= 0.25;
|
||||
num++;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
private static uint Log4UnsignedInt32(uint i)
|
||||
{
|
||||
uint num = 0;
|
||||
while (i > 0)
|
||||
{
|
||||
i = i >> 2;
|
||||
num++;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
private static uint Log8UnsignedInt32(uint i)
|
||||
{
|
||||
uint num = 0;
|
||||
while (i > 0)
|
||||
{
|
||||
i = i >> 3;
|
||||
num++;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
private class AdaptiveForwardDifferencingCubicFlattener
|
||||
{
|
||||
private double _aX;
|
||||
|
||||
private double _aY;
|
||||
|
||||
private double _bX;
|
||||
|
||||
private double _bY;
|
||||
|
||||
private double _cX;
|
||||
|
||||
private double _cY;
|
||||
|
||||
private readonly double _distanceTolerance;
|
||||
|
||||
private readonly bool _doParameters;
|
||||
|
||||
private double _dParameter;
|
||||
|
||||
private double _dX;
|
||||
|
||||
private double _dY;
|
||||
|
||||
private readonly double _flatnessTolerance;
|
||||
|
||||
private int _numSteps;
|
||||
|
||||
private double _parameter;
|
||||
|
||||
internal AdaptiveForwardDifferencingCubicFlattener(Point[] controlPoints,
|
||||
double flatnessTolerance, double distanceTolerance, bool doParameters)
|
||||
{
|
||||
_numSteps = 1;
|
||||
_dParameter = 1.0;
|
||||
_flatnessTolerance = 3.0 * flatnessTolerance;
|
||||
_distanceTolerance = distanceTolerance;
|
||||
_doParameters = doParameters;
|
||||
_aX = -controlPoints[0].X + 3.0 * (controlPoints[1].X - controlPoints[2].X) +
|
||||
controlPoints[3].X;
|
||||
_aY = -controlPoints[0].Y + 3.0 * (controlPoints[1].Y - controlPoints[2].Y) +
|
||||
controlPoints[3].Y;
|
||||
_bX = 3.0 * (controlPoints[0].X - 2.0 * controlPoints[1].X + controlPoints[2].X);
|
||||
_bY = 3.0 * (controlPoints[0].Y - 2.0 * controlPoints[1].Y + controlPoints[2].Y);
|
||||
_cX = 3.0 * (-controlPoints[0].X + controlPoints[1].X);
|
||||
_cY = 3.0 * (-controlPoints[0].Y + controlPoints[1].Y);
|
||||
_dX = controlPoints[0].X;
|
||||
_dY = controlPoints[0].Y;
|
||||
}
|
||||
|
||||
private void DoubleStepSize()
|
||||
{
|
||||
_aX *= 8.0;
|
||||
_aY *= 8.0;
|
||||
_bX *= 4.0;
|
||||
_bY *= 4.0;
|
||||
_cX += _cX;
|
||||
_cY += _cY;
|
||||
if (_doParameters) _dParameter *= 2.0;
|
||||
_numSteps = _numSteps >> 1;
|
||||
}
|
||||
|
||||
private void HalveStepSize()
|
||||
{
|
||||
_aX *= 0.125;
|
||||
_aY *= 0.125;
|
||||
_bX *= 0.25;
|
||||
_bY *= 0.25;
|
||||
_cX *= 0.5;
|
||||
_cY *= 0.5;
|
||||
if (_doParameters) _dParameter *= 0.5;
|
||||
_numSteps = _numSteps << 1;
|
||||
}
|
||||
|
||||
private void IncrementDifferencesAndParameter()
|
||||
{
|
||||
_dX = _aX + _bX + _cX + _dX;
|
||||
_dY = _aY + _bY + _cY + _dY;
|
||||
_cX = _aX + _aX + _aX + _bX + _bX + _cX;
|
||||
_cY = _aY + _aY + _aY + _bY + _bY + _cY;
|
||||
_bX = _aX + _aX + _aX + _bX;
|
||||
_bY = _aY + _aY + _aY + _bY;
|
||||
_numSteps--;
|
||||
_parameter += _dParameter;
|
||||
}
|
||||
|
||||
private bool MustSubdivide(double flatnessTolerance)
|
||||
{
|
||||
var num = -(_aY + _bY + _cY);
|
||||
var num2 = _aX + _bX + _cX;
|
||||
var num3 = Math.Abs(num) + Math.Abs(num2);
|
||||
if (num3 <= _distanceTolerance) return false;
|
||||
num3 *= flatnessTolerance;
|
||||
if (Math.Abs(_cX * num + _cY * num2) <= num3 &&
|
||||
Math.Abs((_bX + _cX + _cX) * num + (_bY + _cY + _cY) * num2) <= num3) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ReSharper disable once RedundantAssignment
|
||||
internal bool Next(ref Point p, ref double u)
|
||||
{
|
||||
while (MustSubdivide(_flatnessTolerance)) HalveStepSize();
|
||||
if ((_numSteps & 1) == 0)
|
||||
while (_numSteps > 1 && !MustSubdivide(_flatnessTolerance * 0.25))
|
||||
DoubleStepSize();
|
||||
IncrementDifferencesAndParameter();
|
||||
p.X = _dX;
|
||||
p.Y = _dY;
|
||||
u = _parameter;
|
||||
return _numSteps != 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows;
|
||||
|
||||
namespace HandyControl.Expression.Drawing
|
||||
{
|
||||
internal static class CommonExtensions
|
||||
{
|
||||
public static void AddRange<T>(this ICollection<T> collection, IEnumerable<T> newItems)
|
||||
{
|
||||
if (collection == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(collection));
|
||||
}
|
||||
|
||||
if (collection is List<T> list)
|
||||
{
|
||||
list.AddRange(newItems);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var local in newItems)
|
||||
{
|
||||
collection.Add(local);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool SetIfDifferent(this DependencyObject dependencyObject, DependencyProperty dependencyProperty, object value)
|
||||
{
|
||||
if (!Equals(dependencyObject.GetValue(dependencyProperty), value))
|
||||
{
|
||||
dependencyObject.SetValue(dependencyProperty, value);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool EnsureListCount<T>(this IList<T> list, int count, Func<T> factory = null)
|
||||
{
|
||||
if (list == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(list));
|
||||
}
|
||||
if (count < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(count));
|
||||
}
|
||||
if (!list.EnsureListCountAtLeast(count, factory))
|
||||
{
|
||||
if (list.Count <= count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (list is List<T> list2)
|
||||
{
|
||||
list2.RemoveRange(count, list.Count - count);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = list.Count - 1; i >= count; i--)
|
||||
{
|
||||
list.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool EnsureListCountAtLeast<T>(this IList<T> list, int count, Func<T> factory = null)
|
||||
{
|
||||
if (list == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(list));
|
||||
}
|
||||
if (count < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(count));
|
||||
}
|
||||
if (list.Count >= count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((list is List<T> list2) && (factory == null))
|
||||
{
|
||||
list2.AddRange(new T[count - list.Count]);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = list.Count; i < count; i++)
|
||||
{
|
||||
list.Add((factory == null) ? default(T) : factory());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool ClearIfSet(this DependencyObject dependencyObject, DependencyProperty dependencyProperty)
|
||||
{
|
||||
if (dependencyObject.ReadLocalValue(dependencyProperty) != DependencyProperty.UnsetValue)
|
||||
{
|
||||
dependencyObject.ClearValue(dependencyProperty);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void RemoveLast<T>(this IList<T> list)
|
||||
{
|
||||
list.RemoveAt(list.Count - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using System.CodeDom.Compiler;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Resources;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace HandyControl.Expression.Drawing
|
||||
{
|
||||
[CompilerGenerated]
|
||||
[GeneratedCode("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||
[DebuggerNonUserCode]
|
||||
internal class ExceptionStringTable
|
||||
{
|
||||
private static ResourceManager ResourceMan;
|
||||
|
||||
[EditorBrowsable(EditorBrowsableState.Advanced)]
|
||||
internal static CultureInfo Culture { get; set; }
|
||||
|
||||
[EditorBrowsable(EditorBrowsableState.Advanced)]
|
||||
internal static ResourceManager ResourceManager
|
||||
{
|
||||
get
|
||||
{
|
||||
if (ReferenceEquals(ResourceMan, null))
|
||||
{
|
||||
var manager = new ResourceManager("Microsoft.Expression.Drawing.ExceptionStringTable",
|
||||
typeof(ExceptionStringTable).Assembly);
|
||||
ResourceMan = manager;
|
||||
}
|
||||
|
||||
return ResourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
internal static string TypeNotSupported =>
|
||||
ResourceManager.GetString("TypeNotSupported", Culture);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace HandyControl.Expression.Drawing
|
||||
{
|
||||
public static class GeometryHelper
|
||||
{
|
||||
internal static void ApplyTransform(this IList<Point> points, GeneralTransform transform)
|
||||
{
|
||||
for (var i = 0; i < points.Count; i++) points[i] = transform.Transform(points[i]);
|
||||
}
|
||||
|
||||
internal static Rect Bounds(this Size size)
|
||||
{
|
||||
return new Rect(0.0, 0.0, size.Width, size.Height);
|
||||
}
|
||||
|
||||
internal static Point Center(this Rect rect)
|
||||
{
|
||||
return new Point(rect.X + rect.Width / 2.0, rect.Y + rect.Height / 2.0);
|
||||
}
|
||||
|
||||
internal static Vector Subtract(this Point lhs, Point rhs)
|
||||
{
|
||||
return new Vector(lhs.X - rhs.X, lhs.Y - rhs.Y);
|
||||
}
|
||||
|
||||
internal static Thickness Subtract(this Rect lhs, Rect rhs)
|
||||
{
|
||||
return new Thickness(rhs.Left - lhs.Left,
|
||||
rhs.Top - lhs.Top, lhs.Right - rhs.Right,
|
||||
lhs.Bottom - rhs.Bottom);
|
||||
}
|
||||
|
||||
internal static Point Lerp(Point pointA, Point pointB, double alpha)
|
||||
{
|
||||
return new Point(
|
||||
MathHelper.Lerp(pointA.X, pointB.X, alpha),
|
||||
MathHelper.Lerp(pointA.Y, pointB.Y, alpha));
|
||||
}
|
||||
|
||||
internal static Vector Lerp(Vector vectorA, Vector vectorB, double alpha)
|
||||
{
|
||||
return new Vector(MathHelper.Lerp(vectorA.X, vectorB.X, alpha),
|
||||
MathHelper.Lerp(vectorA.Y, vectorB.Y, alpha));
|
||||
}
|
||||
|
||||
internal static double Distance(Point lhs, Point rhs)
|
||||
{
|
||||
var num = lhs.X - rhs.X;
|
||||
var num2 = lhs.Y - rhs.Y;
|
||||
return Math.Sqrt(num * num + num2 * num2);
|
||||
}
|
||||
|
||||
internal static Rect Resize(this Rect rect, double ratio)
|
||||
{
|
||||
return rect.Resize(ratio, ratio);
|
||||
}
|
||||
|
||||
internal static Rect Resize(this Rect rect, double ratioX, double ratioY)
|
||||
{
|
||||
var point = rect.Center();
|
||||
var width = rect.Width * ratioX;
|
||||
var height = rect.Height * ratioY;
|
||||
return new Rect(point.X - width / 2.0, point.Y - height / 2.0, width, height);
|
||||
}
|
||||
|
||||
internal static Rect GetStretchBound(Rect logicalBound, Stretch stretch, Size aspectRatio)
|
||||
{
|
||||
if (stretch == Stretch.None) stretch = Stretch.Fill;
|
||||
if (stretch == Stretch.Fill || !aspectRatio.HasValidArea()) return logicalBound;
|
||||
var point = logicalBound.Center();
|
||||
if (stretch == Stretch.Uniform)
|
||||
{
|
||||
if (aspectRatio.Width * logicalBound.Height < logicalBound.Width * aspectRatio.Height)
|
||||
logicalBound.Width = logicalBound.Height * aspectRatio.Width / aspectRatio.Height;
|
||||
else
|
||||
logicalBound.Height = logicalBound.Width * aspectRatio.Height / aspectRatio.Width;
|
||||
}
|
||||
else if (stretch == Stretch.UniformToFill)
|
||||
{
|
||||
if (aspectRatio.Width * logicalBound.Height < logicalBound.Width * aspectRatio.Height)
|
||||
logicalBound.Height = logicalBound.Width * aspectRatio.Height / aspectRatio.Width;
|
||||
else
|
||||
logicalBound.Width = logicalBound.Height * aspectRatio.Width / aspectRatio.Height;
|
||||
}
|
||||
|
||||
return new Rect(point.X - logicalBound.Width / 2.0, point.Y - logicalBound.Height / 2.0,
|
||||
logicalBound.Width, logicalBound.Height);
|
||||
}
|
||||
|
||||
internal static Point GetArcPoint(double degree)
|
||||
{
|
||||
var a = degree * 3.1415926535897931 / 180.0;
|
||||
return new Point(0.5 + 0.5 * Math.Sin(a), 0.5 - 0.5 * Math.Cos(a));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
internal static Point GetArcPoint(double degree, Rect bound)
|
||||
{
|
||||
var arcPoint = GetArcPoint(degree);
|
||||
return RelativeToAbsolutePoint(bound, arcPoint);
|
||||
}
|
||||
|
||||
internal static Point RelativeToAbsolutePoint(Rect bound, Point relative)
|
||||
{
|
||||
return new Point(bound.X + relative.X * bound.Width, bound.Y + relative.Y * bound.Height);
|
||||
}
|
||||
|
||||
internal static double SquaredDistance(Point lhs, Point rhs)
|
||||
{
|
||||
var num = lhs.X - rhs.X;
|
||||
var num2 = lhs.Y - rhs.Y;
|
||||
return num * num + num2 * num2;
|
||||
}
|
||||
|
||||
internal static Point Midpoint(Point lhs, Point rhs)
|
||||
{
|
||||
return new Point((lhs.X + rhs.X) / 2.0, (lhs.Y + rhs.Y) / 2.0);
|
||||
}
|
||||
|
||||
internal static bool HasValidArea(this Size size)
|
||||
{
|
||||
return size.Width > 0.0 && size.Height > 0.0 && !double.IsInfinity(size.Width) &&
|
||||
!double.IsInfinity(size.Height);
|
||||
}
|
||||
|
||||
internal static Rect Inflate(Rect rect, double offset)
|
||||
{
|
||||
return Inflate(rect, new Thickness(offset));
|
||||
}
|
||||
|
||||
internal static Rect Inflate(Rect rect, Thickness thickness)
|
||||
{
|
||||
var width = rect.Width + thickness.Left + thickness.Right;
|
||||
var height = rect.Height + thickness.Top + thickness.Bottom;
|
||||
var x = rect.X - thickness.Left;
|
||||
if (width < 0.0)
|
||||
{
|
||||
x += width / 2.0;
|
||||
width = 0.0;
|
||||
}
|
||||
|
||||
var y = rect.Y - thickness.Top;
|
||||
if (height < 0.0)
|
||||
{
|
||||
y += height / 2.0;
|
||||
height = 0.0;
|
||||
}
|
||||
|
||||
return new Rect(x, y, width, height);
|
||||
}
|
||||
|
||||
internal static double Dot(Point lhs, Point rhs)
|
||||
{
|
||||
return lhs.X * rhs.X + lhs.Y * rhs.Y;
|
||||
}
|
||||
|
||||
internal static double Dot(Vector lhs, Vector rhs)
|
||||
{
|
||||
return lhs.X * rhs.X + lhs.Y * rhs.Y;
|
||||
}
|
||||
|
||||
internal static Point Plus(this Point lhs, Point rhs)
|
||||
{
|
||||
return new Point(lhs.X + rhs.X, lhs.Y + rhs.Y);
|
||||
}
|
||||
|
||||
internal static Point Minus(this Point lhs, Point rhs)
|
||||
{
|
||||
return new Point(lhs.X - rhs.X, lhs.Y - rhs.Y);
|
||||
}
|
||||
|
||||
internal static Vector Normal(Point lhs, Point rhs)
|
||||
{
|
||||
return new Vector(lhs.Y - rhs.Y, rhs.X - lhs.X).Normalized();
|
||||
}
|
||||
|
||||
internal static Vector Normalized(this Vector vector)
|
||||
{
|
||||
var vector2 = new Vector(vector.X, vector.Y);
|
||||
var length = vector2.Length;
|
||||
if (MathHelper.IsVerySmall(length)) return new Vector(0.0, 1.0);
|
||||
return vector2 / length;
|
||||
}
|
||||
|
||||
internal static double Determinant(Point lhs, Point rhs)
|
||||
{
|
||||
return lhs.X * rhs.Y - lhs.Y * rhs.X;
|
||||
}
|
||||
|
||||
internal static bool EnsureSegmentType<T>(out T result, IList<PathSegment> list, int index,
|
||||
Func<T> factory) where T : PathSegment
|
||||
{
|
||||
result = list[index] as T;
|
||||
if (result == null)
|
||||
{
|
||||
list[index] = result = factory();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static bool EnsureGeometryType<T>(out T result, ref Geometry value, Func<T> factory)
|
||||
where T : Geometry
|
||||
{
|
||||
result = value as T;
|
||||
if (result == null)
|
||||
{
|
||||
value = result = factory();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Windows;
|
||||
|
||||
namespace HandyControl.Expression.Drawing
|
||||
{
|
||||
internal class MarchLocation
|
||||
{
|
||||
public double After { get; private set; }
|
||||
|
||||
public double Before { get; private set; }
|
||||
|
||||
public int Index { get; private set; }
|
||||
|
||||
public double Ratio { get; private set; }
|
||||
|
||||
public MarchStopReason Reason { get; private set; }
|
||||
|
||||
public double Remain { get; private set; }
|
||||
|
||||
public static MarchLocation Create(MarchStopReason reason, int index, double before, double after,
|
||||
double remain)
|
||||
{
|
||||
var num = before + after;
|
||||
return new MarchLocation
|
||||
{
|
||||
Reason = reason,
|
||||
Index = index,
|
||||
Remain = remain,
|
||||
Before = MathHelper.EnsureRange(before, 0.0, num),
|
||||
After = MathHelper.EnsureRange(after, 0.0, num),
|
||||
Ratio = MathHelper.EnsureRange(MathHelper.SafeDivide(before, num, 0.0), 0.0, 1.0)
|
||||
};
|
||||
}
|
||||
|
||||
public double GetArcLength(IList<double> accumulatedLengths)
|
||||
{
|
||||
return MathHelper.Lerp(accumulatedLengths[Index], accumulatedLengths[Index + 1], Ratio);
|
||||
}
|
||||
|
||||
public Vector GetNormal(PolylineData polyline, double cornerRadius = 0.0)
|
||||
{
|
||||
return polyline.SmoothNormal(Index, Ratio, cornerRadius);
|
||||
}
|
||||
|
||||
public Point GetPoint(IList<Point> points)
|
||||
{
|
||||
return GeometryHelper.Lerp(points[Index], points[Index + 1], Ratio);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace HandyControl.Expression.Drawing
|
||||
{
|
||||
internal enum MarchStopReason
|
||||
{
|
||||
CompleteStep,
|
||||
CompletePolyline,
|
||||
CornerPoint
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
using System;
|
||||
|
||||
namespace HandyControl.Expression.Drawing
|
||||
{
|
||||
internal static class MathHelper
|
||||
{
|
||||
public static bool AreClose(double value1, double value2) =>
|
||||
// ReSharper disable once CompareOfFloatsByEqualityOperator
|
||||
value1 == value2 || IsVerySmall(value1 - value2);
|
||||
|
||||
public static double Lerp(double x, double y, double alpha) => x * (1.0 - alpha) + y * alpha;
|
||||
|
||||
public static bool IsVerySmall(double value) => Math.Abs(value) < 1E-06;
|
||||
|
||||
public static bool IsFiniteDouble(double x) => !double.IsInfinity(x) && !double.IsNaN(x);
|
||||
|
||||
public static double DoubleFromMantissaAndExponent(double x, int exp) => x * Math.Pow(2.0, exp);
|
||||
|
||||
public static bool GreaterThan(double value1, double value2) => value1 > value2 && !AreClose(value1, value2);
|
||||
|
||||
public static bool GreaterThanOrClose(double value1, double value2)
|
||||
{
|
||||
if (value1 <= value2)
|
||||
{
|
||||
return AreClose(value1, value2);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static double Hypotenuse(double x, double y) => Math.Sqrt(x * x + y * y);
|
||||
|
||||
public static bool LessThan(double value1, double value2) => value1 < value2 && !AreClose(value1, value2);
|
||||
|
||||
public static bool LessThanOrClose(double value1, double value2)
|
||||
{
|
||||
if (value1 >= value2)
|
||||
{
|
||||
return AreClose(value1, value2);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static double EnsureRange(double value, double? min, double? max)
|
||||
{
|
||||
if (min.HasValue && value < min.Value)
|
||||
{
|
||||
return min.Value;
|
||||
}
|
||||
if (max.HasValue && value > max.Value)
|
||||
{
|
||||
return max.Value;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public static double SafeDivide(double lhs, double rhs, double fallback)
|
||||
{
|
||||
if (!IsVerySmall(rhs))
|
||||
{
|
||||
return lhs / rhs;
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace HandyControl.Expression.Drawing
|
||||
{
|
||||
internal static class PathFigureHelper
|
||||
{
|
||||
public static IEnumerable<PathSegmentData> AllSegments(this PathFigure figure)
|
||||
{
|
||||
if (figure != null && figure.Segments.Count > 0)
|
||||
{
|
||||
var startPoint = figure.StartPoint;
|
||||
// ReSharper disable once GenericEnumeratorNotDisposed
|
||||
var enumerator = figure.Segments.GetEnumerator();
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
var current = enumerator.Current;
|
||||
var lastPoint = current.GetLastPoint();
|
||||
yield return new PathSegmentData(startPoint, current);
|
||||
startPoint = lastPoint;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static void ApplyTransform(this PathFigure figure, GeneralTransform transform)
|
||||
{
|
||||
figure.StartPoint = transform.Transform(figure.StartPoint);
|
||||
for (var i = 0; i < figure.Segments.Count; i++)
|
||||
{
|
||||
var segment =
|
||||
PathSegmentHelper.ApplyTransform(figure.Segments[i], figure.StartPoint, transform);
|
||||
if (!Equals(figure.Segments[i], segment)) figure.Segments[i] = segment;
|
||||
}
|
||||
}
|
||||
|
||||
internal static void FlattenFigure(PathFigure figure, IList<Point> points, double tolerance,
|
||||
bool removeRepeat)
|
||||
{
|
||||
if (figure == null) throw new ArgumentNullException(nameof(figure));
|
||||
if (points == null) throw new ArgumentNullException(nameof(points));
|
||||
if (tolerance < 0.0) throw new ArgumentOutOfRangeException(nameof(tolerance));
|
||||
var list = removeRepeat ? new List<Point>() : points;
|
||||
list.Add(figure.StartPoint);
|
||||
foreach (var data in figure.AllSegments())
|
||||
data.PathSegment.FlattenSegment(list, data.StartPoint, tolerance);
|
||||
if (figure.IsClosed) list.Add(figure.StartPoint);
|
||||
if (removeRepeat && list.Count > 0)
|
||||
{
|
||||
points.Add(list[0]);
|
||||
for (var i = 1; i < list.Count; i++)
|
||||
if (!MathHelper.IsVerySmall(GeometryHelper.SquaredDistance(points.Last(), list[i])))
|
||||
points.Add(list[i]);
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool SyncEllipseFigure(PathFigure figure, Rect bounds, SweepDirection sweepDirection,
|
||||
bool isFilled = true)
|
||||
{
|
||||
var flag = false;
|
||||
var pointArray = new Point[2];
|
||||
var size = new Size(bounds.Width / 2.0, bounds.Height / 2.0);
|
||||
var point = bounds.Center();
|
||||
if (size.Width > size.Height)
|
||||
{
|
||||
pointArray[0] = new Point(bounds.Left, point.Y);
|
||||
pointArray[1] = new Point(bounds.Right, point.Y);
|
||||
}
|
||||
else
|
||||
{
|
||||
pointArray[0] = new Point(point.X, bounds.Top);
|
||||
pointArray[1] = new Point(point.X, bounds.Bottom);
|
||||
}
|
||||
|
||||
flag |= figure.SetIfDifferent(PathFigure.IsClosedProperty, true);
|
||||
flag |= figure.SetIfDifferent(PathFigure.IsFilledProperty, isFilled);
|
||||
flag |= figure.SetIfDifferent(PathFigure.StartPointProperty, pointArray[0]);
|
||||
flag |= figure.Segments.EnsureListCount(2, () => new ArcSegment());
|
||||
flag |= GeometryHelper.EnsureSegmentType(out var segment, figure.Segments, 0, () => new ArcSegment());
|
||||
flag |= segment.SetIfDifferent(ArcSegment.PointProperty, pointArray[1]);
|
||||
flag |= segment.SetIfDifferent(ArcSegment.SizeProperty, size);
|
||||
flag |= segment.SetIfDifferent(ArcSegment.IsLargeArcProperty, false);
|
||||
flag |= segment.SetIfDifferent(ArcSegment.SweepDirectionProperty, sweepDirection);
|
||||
flag |= GeometryHelper.EnsureSegmentType(out segment, figure.Segments, 1, () => new ArcSegment());
|
||||
flag |= segment.SetIfDifferent(ArcSegment.PointProperty, pointArray[0]);
|
||||
flag |= segment.SetIfDifferent(ArcSegment.SizeProperty, size);
|
||||
flag |= segment.SetIfDifferent(ArcSegment.IsLargeArcProperty, false);
|
||||
return flag | segment.SetIfDifferent(ArcSegment.SweepDirectionProperty, sweepDirection);
|
||||
}
|
||||
|
||||
internal static bool SyncPolylineFigure(PathFigure figure, IList<Point> points, bool isClosed,
|
||||
bool isFilled = true)
|
||||
{
|
||||
if (figure == null) throw new ArgumentNullException(nameof(figure));
|
||||
var flag = false;
|
||||
if (points == null || points.Count == 0)
|
||||
{
|
||||
flag |= figure.ClearIfSet(PathFigure.StartPointProperty);
|
||||
flag |= figure.Segments.EnsureListCount(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
flag |= figure.SetIfDifferent(PathFigure.StartPointProperty, points[0]);
|
||||
flag |= figure.Segments.EnsureListCount(1, () => new PolyLineSegment());
|
||||
flag |= PathSegmentHelper.SyncPolylineSegment(figure.Segments, 0, points, 1,
|
||||
points.Count - 1);
|
||||
}
|
||||
|
||||
flag |= figure.SetIfDifferent(PathFigure.IsClosedProperty, isClosed);
|
||||
return flag | figure.SetIfDifferent(PathFigure.IsFilledProperty, isFilled);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace HandyControl.Expression.Drawing
|
||||
{
|
||||
internal static class PathGeometryHelper
|
||||
{
|
||||
public static bool IsStroked(this PathSegment pathSegment) => pathSegment.IsStroked;
|
||||
|
||||
public static PathGeometry AsPathGeometry(this Geometry original)
|
||||
{
|
||||
if (original == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!(original is PathGeometry geometry))
|
||||
{
|
||||
return PathGeometry.CreateFromGeometry(original);
|
||||
}
|
||||
return geometry;
|
||||
}
|
||||
|
||||
internal static Geometry FixPathGeometryBoundary(Geometry geometry) => geometry;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using System.Windows;
|
||||
|
||||
namespace HandyControl.Expression.Drawing
|
||||
{
|
||||
internal sealed class PathSegmentData
|
||||
{
|
||||
public PathSegmentData(Point startPoint, System.Windows.Media.PathSegment pathSegment)
|
||||
{
|
||||
PathSegment = pathSegment;
|
||||
StartPoint = startPoint;
|
||||
}
|
||||
|
||||
public System.Windows.Media.PathSegment PathSegment { get; }
|
||||
|
||||
public Point StartPoint { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,755 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace HandyControl.Expression.Drawing
|
||||
{
|
||||
internal static class PathSegmentHelper
|
||||
{
|
||||
public static PathSegment ApplyTransform(PathSegment segment, Point start, GeneralTransform transform)
|
||||
{
|
||||
return PathSegmentImplementation.Create(segment, start).ApplyTransform(transform);
|
||||
}
|
||||
|
||||
public static PathSegment ArcToBezierSegments(ArcSegment arcSegment, Point startPoint)
|
||||
{
|
||||
var isStroked = arcSegment.IsStroked();
|
||||
ArcToBezierHelper.ArcToBezier(startPoint.X, startPoint.Y, arcSegment.Size.Width,
|
||||
arcSegment.Size.Height, arcSegment.RotationAngle, arcSegment.IsLargeArc,
|
||||
arcSegment.SweepDirection == SweepDirection.Clockwise, arcSegment.Point.X, arcSegment.Point.Y,
|
||||
out var pointArray, out var num);
|
||||
switch (num)
|
||||
{
|
||||
case -1:
|
||||
return null;
|
||||
|
||||
case 0:
|
||||
return CreateLineSegment(arcSegment.Point, isStroked);
|
||||
|
||||
case 1:
|
||||
return CreateBezierSegment(pointArray[0], pointArray[1], pointArray[2], isStroked);
|
||||
}
|
||||
|
||||
return CreatePolyBezierSegment(pointArray, 0, num * 3, isStroked);
|
||||
}
|
||||
|
||||
public static ArcSegment CreateArcSegment(Point point, Size size, bool isLargeArc, bool clockwise,
|
||||
double rotationAngle = 0.0, bool isStroked = true)
|
||||
{
|
||||
var dependencyObject = new ArcSegment();
|
||||
dependencyObject.SetIfDifferent(ArcSegment.PointProperty, point);
|
||||
dependencyObject.SetIfDifferent(ArcSegment.SizeProperty, size);
|
||||
dependencyObject.SetIfDifferent(ArcSegment.IsLargeArcProperty, isLargeArc);
|
||||
dependencyObject.SetIfDifferent(ArcSegment.SweepDirectionProperty,
|
||||
clockwise ? SweepDirection.Clockwise : SweepDirection.Counterclockwise);
|
||||
dependencyObject.SetIfDifferent(ArcSegment.RotationAngleProperty, rotationAngle);
|
||||
dependencyObject.SetIsStroked(isStroked);
|
||||
return dependencyObject;
|
||||
}
|
||||
|
||||
public static BezierSegment CreateBezierSegment(Point point1, Point point2, Point point3,
|
||||
bool isStroked = true)
|
||||
{
|
||||
var segment = new BezierSegment
|
||||
{
|
||||
Point1 = point1,
|
||||
Point2 = point2,
|
||||
Point3 = point3
|
||||
};
|
||||
segment.SetIsStroked(isStroked);
|
||||
return segment;
|
||||
}
|
||||
|
||||
public static LineSegment CreateLineSegment(Point point, bool isStroked = true)
|
||||
{
|
||||
var segment = new LineSegment
|
||||
{
|
||||
Point = point
|
||||
};
|
||||
segment.SetIsStroked(isStroked);
|
||||
return segment;
|
||||
}
|
||||
|
||||
public static PolyBezierSegment CreatePolyBezierSegment(IList<Point> points, int start, int count,
|
||||
bool isStroked = true)
|
||||
{
|
||||
if (points == null) throw new ArgumentNullException(nameof(points));
|
||||
count = count / 3 * 3;
|
||||
if (count < 0 || points.Count < start + count) throw new ArgumentOutOfRangeException(nameof(count));
|
||||
var segment = new PolyBezierSegment
|
||||
{
|
||||
Points = new PointCollection()
|
||||
};
|
||||
for (var i = 0; i < count; i++) segment.Points.Add(points[start + i]);
|
||||
segment.SetIsStroked(isStroked);
|
||||
return segment;
|
||||
}
|
||||
|
||||
public static PolyLineSegment CreatePolylineSegment(IList<Point> points, int start, int count,
|
||||
bool isStroked = true)
|
||||
{
|
||||
if (count < 0 || points.Count < start + count) throw new ArgumentOutOfRangeException(nameof(count));
|
||||
var segment = new PolyLineSegment
|
||||
{
|
||||
Points = new PointCollection()
|
||||
};
|
||||
for (var i = 0; i < count; i++) segment.Points.Add(points[start + i]);
|
||||
segment.SetIsStroked(isStroked);
|
||||
return segment;
|
||||
}
|
||||
|
||||
public static PolyQuadraticBezierSegment CreatePolyQuadraticBezierSegment(IList<Point> points,
|
||||
int start, int count, bool isStroked = true)
|
||||
{
|
||||
if (points == null) throw new ArgumentNullException(nameof(points));
|
||||
count = count / 2 * 2;
|
||||
if (count < 0 || points.Count < start + count) throw new ArgumentOutOfRangeException(nameof(count));
|
||||
var segment = new PolyQuadraticBezierSegment
|
||||
{
|
||||
Points = new PointCollection()
|
||||
};
|
||||
for (var i = 0; i < count; i++) segment.Points.Add(points[start + i]);
|
||||
segment.SetIsStroked(isStroked);
|
||||
return segment;
|
||||
}
|
||||
|
||||
public static QuadraticBezierSegment CreateQuadraticBezierSegment(Point point1, Point point2,
|
||||
bool isStroked = true)
|
||||
{
|
||||
var segment = new QuadraticBezierSegment
|
||||
{
|
||||
Point1 = point1,
|
||||
Point2 = point2
|
||||
};
|
||||
segment.SetIsStroked(isStroked);
|
||||
return segment;
|
||||
}
|
||||
|
||||
public static void FlattenSegment(this PathSegment segment, IList<Point> points, Point start,
|
||||
double tolerance)
|
||||
{
|
||||
PathSegmentImplementation.Create(segment, start).Flatten(points, tolerance);
|
||||
}
|
||||
|
||||
public static Point GetLastPoint(this PathSegment segment)
|
||||
{
|
||||
return segment.GetPoint(-1);
|
||||
}
|
||||
|
||||
public static Point GetPoint(this PathSegment segment, int index)
|
||||
{
|
||||
return PathSegmentImplementation.Create(segment).GetPoint(index);
|
||||
}
|
||||
|
||||
public static int GetPointCount(this PathSegment segment)
|
||||
{
|
||||
if (segment is ArcSegment) return 1;
|
||||
if (segment is LineSegment) return 1;
|
||||
if (segment is QuadraticBezierSegment) return 2;
|
||||
if (segment is BezierSegment) return 3;
|
||||
if (segment is PolyLineSegment segment2) return segment2.Points.Count;
|
||||
if (segment is PolyQuadraticBezierSegment segment3) return segment3.Points.Count / 2 * 2;
|
||||
if (segment is PolyBezierSegment segment4) return segment4.Points.Count / 3 * 3;
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static IEnumerable<SimpleSegment> GetSimpleSegments(this PathSegment segment, Point start)
|
||||
{
|
||||
return PathSegmentImplementation.Create(segment, start).GetSimpleSegments();
|
||||
}
|
||||
|
||||
public static bool IsEmpty(this PathSegment segment)
|
||||
{
|
||||
return segment.GetPointCount() == 0;
|
||||
}
|
||||
|
||||
private static void SetIsStroked(this PathSegment segment, bool isStroked)
|
||||
{
|
||||
if (segment.IsStroked != isStroked) segment.IsStroked = isStroked;
|
||||
}
|
||||
|
||||
public static bool SyncPolyBezierSegment(PathSegmentCollection collection, int index,
|
||||
IList<Point> points, int start, int count)
|
||||
{
|
||||
if (collection == null) throw new ArgumentNullException(nameof(collection));
|
||||
if (index < 0 || index >= collection.Count) throw new ArgumentOutOfRangeException(nameof(index));
|
||||
if (points == null) throw new ArgumentNullException(nameof(points));
|
||||
if (start < 0) throw new ArgumentOutOfRangeException(nameof(start));
|
||||
if (count < 0) throw new ArgumentOutOfRangeException(nameof(count));
|
||||
if (points.Count < start + count) throw new ArgumentOutOfRangeException(nameof(count));
|
||||
var flag = false;
|
||||
count = count / 3 * 3;
|
||||
var segment = collection[index] as PolyBezierSegment;
|
||||
if (segment == null)
|
||||
{
|
||||
collection[index] = segment = new PolyBezierSegment();
|
||||
flag = true;
|
||||
}
|
||||
|
||||
segment.Points.EnsureListCount(count);
|
||||
for (var i = 0; i < count; i++)
|
||||
if (segment.Points[i] != points[i + start])
|
||||
{
|
||||
segment.Points[i] = points[i + start];
|
||||
flag = true;
|
||||
}
|
||||
|
||||
return flag;
|
||||
}
|
||||
|
||||
public static bool SyncPolylineSegment(PathSegmentCollection collection, int index,
|
||||
IList<Point> points, int start, int count)
|
||||
{
|
||||
if (collection == null) throw new ArgumentNullException(nameof(collection));
|
||||
if (index < 0 || index >= collection.Count) throw new ArgumentOutOfRangeException(nameof(index));
|
||||
if (points == null) throw new ArgumentNullException(nameof(points));
|
||||
if (start < 0) throw new ArgumentOutOfRangeException(nameof(start));
|
||||
if (count < 0) throw new ArgumentOutOfRangeException(nameof(count));
|
||||
if (points.Count < start + count) throw new ArgumentOutOfRangeException(nameof(count));
|
||||
var flag = false;
|
||||
var segment = collection[index] as PolyLineSegment;
|
||||
if (segment == null)
|
||||
{
|
||||
collection[index] = segment = new PolyLineSegment();
|
||||
flag = true;
|
||||
}
|
||||
|
||||
flag |= segment.Points.EnsureListCount(count);
|
||||
for (var i = 0; i < count; i++)
|
||||
if (segment.Points[i] != points[i + start])
|
||||
{
|
||||
segment.Points[i] = points[i + start];
|
||||
flag = true;
|
||||
}
|
||||
|
||||
return flag;
|
||||
}
|
||||
|
||||
|
||||
private class ArcSegmentImplementation : PathSegmentImplementation
|
||||
{
|
||||
private ArcSegment _segment;
|
||||
|
||||
public override PathSegment ApplyTransform(GeneralTransform transform)
|
||||
{
|
||||
var segment = ArcToBezierSegments(_segment, Start);
|
||||
if (segment != null) return PathSegmentHelper.ApplyTransform(segment, Start, transform);
|
||||
_segment.Point = transform.Transform(_segment.Point);
|
||||
return _segment;
|
||||
}
|
||||
|
||||
public static PathSegmentImplementation Create(ArcSegment source)
|
||||
{
|
||||
if (source != null) return new ArcSegmentImplementation {_segment = source};
|
||||
return null;
|
||||
}
|
||||
|
||||
public override void Flatten(IList<Point> points, double tolerance)
|
||||
{
|
||||
var segment = ArcToBezierSegments(_segment, Start);
|
||||
segment?.FlattenSegment(points, Start, tolerance);
|
||||
}
|
||||
|
||||
public override Point GetPoint(int index)
|
||||
{
|
||||
if (index < -1 || index > 0) throw new ArgumentOutOfRangeException(nameof(index));
|
||||
return _segment.Point;
|
||||
}
|
||||
|
||||
public override IEnumerable<SimpleSegment> GetSimpleSegments()
|
||||
{
|
||||
var segment = ArcToBezierSegments(_segment, Start);
|
||||
if (segment != null) return segment.GetSimpleSegments(Start);
|
||||
return Enumerable.Empty<SimpleSegment>();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class ArcToBezierHelper
|
||||
{
|
||||
private static bool AcceptRadius(double rHalfChord2, double rFuzz2, ref double rRadius)
|
||||
{
|
||||
var flag = rRadius * rRadius > rHalfChord2 * rFuzz2;
|
||||
if (flag && rRadius < 0.0) rRadius = -rRadius;
|
||||
return flag;
|
||||
}
|
||||
|
||||
public static void ArcToBezier(double xStart, double yStart, double xRadius, double yRadius,
|
||||
double rRotation, bool fLargeArc, bool fSweepUp, double xEnd, double yEnd, out Point[] pPt,
|
||||
out int cPieces)
|
||||
{
|
||||
var num = 1E-06;
|
||||
pPt = new Point[12];
|
||||
var num12 = num * num;
|
||||
var flag = false;
|
||||
cPieces = -1;
|
||||
var num2 = 0.5 * (xEnd - xStart);
|
||||
var num3 = 0.5 * (yEnd - yStart);
|
||||
var num4 = num2 * num2 + num3 * num3;
|
||||
if (num4 >= num12)
|
||||
{
|
||||
if (!AcceptRadius(num4, num12, ref xRadius) || !AcceptRadius(num4, num12, ref yRadius))
|
||||
{
|
||||
cPieces = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
double num5;
|
||||
double num6;
|
||||
// ReSharper disable once InlineOutVariableDeclaration
|
||||
double num7;
|
||||
// ReSharper disable once InlineOutVariableDeclaration
|
||||
double num8;
|
||||
double num9;
|
||||
double num10;
|
||||
Point point4;
|
||||
if (Math.Abs(rRotation) < num)
|
||||
{
|
||||
num5 = 1.0;
|
||||
num6 = 0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
rRotation = -rRotation * 3.1415926535897931 / 180.0;
|
||||
num5 = Math.Cos(rRotation);
|
||||
num6 = Math.Sin(rRotation);
|
||||
var num15 = num2 * num5 - num3 * num6;
|
||||
num3 = num2 * num6 + num3 * num5;
|
||||
num2 = num15;
|
||||
}
|
||||
|
||||
num2 /= xRadius;
|
||||
num3 /= yRadius;
|
||||
num4 = num2 * num2 + num3 * num3;
|
||||
if (num4 > 1.0)
|
||||
{
|
||||
var num16 = Math.Sqrt(num4);
|
||||
xRadius *= num16;
|
||||
yRadius *= num16;
|
||||
num9 = num10 = 0.0;
|
||||
flag = true;
|
||||
num2 /= num16;
|
||||
num3 /= num16;
|
||||
}
|
||||
else
|
||||
{
|
||||
var num17 = Math.Sqrt((1.0 - num4) / num4);
|
||||
if (fLargeArc != fSweepUp)
|
||||
{
|
||||
num9 = -num17 * num3;
|
||||
num10 = num17 * num2;
|
||||
}
|
||||
else
|
||||
{
|
||||
num9 = num17 * num3;
|
||||
num10 = -num17 * num2;
|
||||
}
|
||||
}
|
||||
|
||||
var ptStart = new Point(-num2 - num9, -num3 - num10);
|
||||
var ptEnd = new Point(num2 - num9, num3 - num10);
|
||||
var matrix = new Matrix(num5 * xRadius, -num6 * xRadius, num6 * yRadius,
|
||||
num5 * yRadius, 0.5 * (xEnd + xStart), 0.5 * (yEnd + yStart));
|
||||
if (!flag)
|
||||
{
|
||||
matrix.OffsetX += matrix.M11 * num9 + matrix.M21 * num10;
|
||||
matrix.OffsetY += matrix.M12 * num9 + matrix.M22 * num10;
|
||||
}
|
||||
|
||||
GetArcAngle(ptStart, ptEnd, fLargeArc, fSweepUp, out num7, out num8, out cPieces);
|
||||
var bezierDistance = GetBezierDistance(num7);
|
||||
if (!fSweepUp) bezierDistance = -bezierDistance;
|
||||
var rhs = new Point(-bezierDistance * ptStart.Y, bezierDistance * ptStart.X);
|
||||
var index = 0;
|
||||
pPt = new Point[cPieces * 3];
|
||||
for (var i = 1; i < cPieces; i++)
|
||||
{
|
||||
var lhs = new Point(ptStart.X * num7 - ptStart.Y * num8,
|
||||
ptStart.X * num8 + ptStart.Y * num7);
|
||||
point4 = new Point(-bezierDistance * lhs.Y, bezierDistance * lhs.X);
|
||||
pPt[index++] = matrix.Transform(ptStart.Plus(rhs));
|
||||
pPt[index++] = matrix.Transform(lhs.Minus(point4));
|
||||
pPt[index++] = matrix.Transform(lhs);
|
||||
ptStart = lhs;
|
||||
rhs = point4;
|
||||
}
|
||||
|
||||
point4 = new Point(-bezierDistance * ptEnd.Y, bezierDistance * ptEnd.X);
|
||||
pPt[index++] = matrix.Transform(ptStart.Plus(rhs));
|
||||
pPt[index++] = matrix.Transform(ptEnd.Minus(point4));
|
||||
pPt[index] = new Point(xEnd, yEnd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void GetArcAngle(Point ptStart, Point ptEnd, bool fLargeArc, bool fSweepUp,
|
||||
out double rCosArcAngle, out double rSinArcAngle, out int cPieces)
|
||||
{
|
||||
rCosArcAngle = GeometryHelper.Dot(ptStart, ptEnd);
|
||||
rSinArcAngle = GeometryHelper.Determinant(ptStart, ptEnd);
|
||||
if (rCosArcAngle >= 0.0)
|
||||
{
|
||||
if (!fLargeArc)
|
||||
{
|
||||
cPieces = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
cPieces = 4;
|
||||
}
|
||||
else if (fLargeArc)
|
||||
{
|
||||
cPieces = 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
cPieces = 2;
|
||||
}
|
||||
|
||||
var d = Math.Atan2(rSinArcAngle, rCosArcAngle);
|
||||
if (fSweepUp)
|
||||
{
|
||||
if (d < 0.0) d += 6.2831853071795862;
|
||||
}
|
||||
else if (d > 0.0)
|
||||
{
|
||||
d -= 6.2831853071795862;
|
||||
}
|
||||
|
||||
d /= cPieces;
|
||||
rCosArcAngle = Math.Cos(d);
|
||||
rSinArcAngle = Math.Sin(d);
|
||||
}
|
||||
|
||||
private static double GetBezierDistance(double rDot, double rRadius = 1.0)
|
||||
{
|
||||
var num = rRadius * rRadius;
|
||||
var num5 = 0.0;
|
||||
var d = 0.5 * (num + rDot);
|
||||
if (d < 0.0) return num5;
|
||||
var num3 = num - d;
|
||||
if (num3 <= 0.0) return num5;
|
||||
var num4 = Math.Sqrt(num3);
|
||||
var num2 = 4.0 * (rRadius - Math.Sqrt(d)) / 3.0;
|
||||
if (num2 <= num4 * 1E-06) return 0.0;
|
||||
return num2 / num4;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class BezierSegmentImplementation : PathSegmentImplementation
|
||||
{
|
||||
private BezierSegment _segment;
|
||||
|
||||
public override PathSegment ApplyTransform(GeneralTransform transform)
|
||||
{
|
||||
_segment.Point1 = transform.Transform(_segment.Point1);
|
||||
_segment.Point2 = transform.Transform(_segment.Point2);
|
||||
_segment.Point3 = transform.Transform(_segment.Point3);
|
||||
return _segment;
|
||||
}
|
||||
|
||||
public static PathSegmentImplementation Create(BezierSegment source)
|
||||
{
|
||||
if (source != null) return new BezierSegmentImplementation {_segment = source};
|
||||
return null;
|
||||
}
|
||||
|
||||
public override void Flatten(IList<Point> points, double tolerance)
|
||||
{
|
||||
Point[] controlPoints = {Start, _segment.Point1, _segment.Point2, _segment.Point3};
|
||||
var resultPolyline = new List<Point>();
|
||||
BezierCurveFlattener.FlattenCubic(controlPoints, tolerance, resultPolyline, true);
|
||||
points.AddRange(resultPolyline);
|
||||
}
|
||||
|
||||
public override Point GetPoint(int index)
|
||||
{
|
||||
if (index < -1 || index > 2) throw new ArgumentOutOfRangeException(nameof(index));
|
||||
if (index == 0) return _segment.Point1;
|
||||
if (index == 1) return _segment.Point2;
|
||||
return _segment.Point3;
|
||||
}
|
||||
|
||||
public override IEnumerable<SimpleSegment> GetSimpleSegments()
|
||||
{
|
||||
yield return SimpleSegment.Create(Start, _segment.Point1, _segment.Point2, _segment.Point3);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class LineSegmentImplementation : PathSegmentImplementation
|
||||
{
|
||||
private LineSegment _segment;
|
||||
|
||||
public override PathSegment ApplyTransform(GeneralTransform transform)
|
||||
{
|
||||
_segment.Point = transform.Transform(_segment.Point);
|
||||
return _segment;
|
||||
}
|
||||
|
||||
public static PathSegmentImplementation Create(LineSegment source)
|
||||
{
|
||||
if (source != null) return new LineSegmentImplementation {_segment = source};
|
||||
return null;
|
||||
}
|
||||
|
||||
public override void Flatten(IList<Point> points, double tolerance)
|
||||
{
|
||||
points.Add(_segment.Point);
|
||||
}
|
||||
|
||||
public override Point GetPoint(int index)
|
||||
{
|
||||
if (index < -1 || index > 0) throw new ArgumentOutOfRangeException(nameof(index));
|
||||
return _segment.Point;
|
||||
}
|
||||
|
||||
public override IEnumerable<SimpleSegment> GetSimpleSegments()
|
||||
{
|
||||
yield return SimpleSegment.Create(Start, _segment.Point);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private abstract class PathSegmentImplementation
|
||||
{
|
||||
protected Point Start { get; private set; }
|
||||
|
||||
public abstract PathSegment ApplyTransform(GeneralTransform transform);
|
||||
|
||||
public static PathSegmentImplementation Create(PathSegment segment)
|
||||
{
|
||||
PathSegmentImplementation implementation;
|
||||
if ((implementation = BezierSegmentImplementation.Create(segment as BezierSegment)) == null &&
|
||||
(implementation = LineSegmentImplementation.Create(segment as LineSegment)) == null &&
|
||||
(implementation = ArcSegmentImplementation.Create(segment as ArcSegment)) == null &&
|
||||
(implementation = PolyLineSegmentImplementation.Create(segment as PolyLineSegment)) ==
|
||||
null &&
|
||||
(implementation = PolyBezierSegmentImplementation.Create(segment as PolyBezierSegment)) ==
|
||||
null &&
|
||||
(implementation =
|
||||
QuadraticBezierSegmentImplementation.Create(segment as QuadraticBezierSegment)) ==
|
||||
null &&
|
||||
(implementation =
|
||||
PolyQuadraticBezierSegmentImplementation.Create(segment as PolyQuadraticBezierSegment)
|
||||
) == null)
|
||||
throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture,
|
||||
ExceptionStringTable.TypeNotSupported, new object[] {segment.GetType().FullName}));
|
||||
return implementation;
|
||||
}
|
||||
|
||||
public static PathSegmentImplementation Create(PathSegment segment, Point start)
|
||||
{
|
||||
var implementation = Create(segment);
|
||||
implementation.Start = start;
|
||||
return implementation;
|
||||
}
|
||||
|
||||
public abstract void Flatten(IList<Point> points, double tolerance);
|
||||
|
||||
public abstract Point GetPoint(int index);
|
||||
|
||||
public abstract IEnumerable<SimpleSegment> GetSimpleSegments();
|
||||
}
|
||||
|
||||
|
||||
private class PolyBezierSegmentImplementation : PathSegmentImplementation
|
||||
{
|
||||
private PolyBezierSegment _segment;
|
||||
|
||||
public override PathSegment ApplyTransform(GeneralTransform transform)
|
||||
{
|
||||
_segment.Points.ApplyTransform(transform);
|
||||
return _segment;
|
||||
}
|
||||
|
||||
public static PathSegmentImplementation Create(PolyBezierSegment source)
|
||||
{
|
||||
if (source != null) return new PolyBezierSegmentImplementation {_segment = source};
|
||||
return null;
|
||||
}
|
||||
|
||||
public override void Flatten(IList<Point> points, double tolerance)
|
||||
{
|
||||
var start = Start;
|
||||
var num = _segment.Points.Count / 3 * 3;
|
||||
for (var i = 0; i < num; i += 3)
|
||||
{
|
||||
Point[] controlPoints = {start, _segment.Points[i], _segment.Points[i + 1], _segment.Points[i + 2]};
|
||||
var resultPolyline = new List<Point>();
|
||||
BezierCurveFlattener.FlattenCubic(controlPoints, tolerance, resultPolyline, true);
|
||||
points.AddRange(resultPolyline);
|
||||
start = _segment.Points[i + 2];
|
||||
}
|
||||
}
|
||||
|
||||
public override Point GetPoint(int index)
|
||||
{
|
||||
var num = _segment.Points.Count / 3 * 3;
|
||||
if (index < -1 || index > num - 1) throw new ArgumentOutOfRangeException(nameof(index));
|
||||
if (index != -1) return _segment.Points[index];
|
||||
return _segment.Points[num - 1];
|
||||
}
|
||||
|
||||
public override IEnumerable<SimpleSegment> GetSimpleSegments()
|
||||
{
|
||||
var start = Start;
|
||||
IList<Point> points = _segment.Points;
|
||||
var iteratorVariable2 = _segment.Points.Count / 3;
|
||||
var iteratorVariable3 = 0;
|
||||
while (true)
|
||||
{
|
||||
if (iteratorVariable3 >= iteratorVariable2) yield break;
|
||||
var iteratorVariable4 = iteratorVariable3 * 3;
|
||||
yield return SimpleSegment.Create(start, points[iteratorVariable4],
|
||||
points[iteratorVariable4 + 1], points[iteratorVariable4 + 2]);
|
||||
start = points[iteratorVariable4 + 2];
|
||||
iteratorVariable3++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class PolyLineSegmentImplementation : PathSegmentImplementation
|
||||
{
|
||||
private PolyLineSegment _segment;
|
||||
|
||||
public override PathSegment ApplyTransform(GeneralTransform transform)
|
||||
{
|
||||
_segment.Points.ApplyTransform(transform);
|
||||
return _segment;
|
||||
}
|
||||
|
||||
public static PathSegmentImplementation Create(PolyLineSegment source)
|
||||
{
|
||||
if (source != null) return new PolyLineSegmentImplementation {_segment = source};
|
||||
return null;
|
||||
}
|
||||
|
||||
public override void Flatten(IList<Point> points, double tolerance)
|
||||
{
|
||||
points.AddRange(_segment.Points);
|
||||
}
|
||||
|
||||
public override Point GetPoint(int index)
|
||||
{
|
||||
if (index < -1 || index > _segment.Points.Count - 1)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
if (index != -1) return _segment.Points[index];
|
||||
return _segment.Points.Last();
|
||||
}
|
||||
|
||||
public override IEnumerable<SimpleSegment> GetSimpleSegments()
|
||||
{
|
||||
var start = Start;
|
||||
// ReSharper disable once GenericEnumeratorNotDisposed
|
||||
var enumerator = _segment.Points.GetEnumerator();
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
var current = enumerator.Current;
|
||||
yield return SimpleSegment.Create(start, current);
|
||||
start = current;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class PolyQuadraticBezierSegmentImplementation : PathSegmentImplementation
|
||||
{
|
||||
private PolyQuadraticBezierSegment _segment;
|
||||
|
||||
public override PathSegment ApplyTransform(GeneralTransform transform)
|
||||
{
|
||||
_segment.Points.ApplyTransform(transform);
|
||||
return _segment;
|
||||
}
|
||||
|
||||
public static PathSegmentImplementation Create(PolyQuadraticBezierSegment source)
|
||||
{
|
||||
if (source != null) return new PolyQuadraticBezierSegmentImplementation {_segment = source};
|
||||
return null;
|
||||
}
|
||||
|
||||
public override void Flatten(IList<Point> points, double tolerance)
|
||||
{
|
||||
var start = Start;
|
||||
var num = _segment.Points.Count / 2 * 2;
|
||||
for (var i = 0; i < num; i += 2)
|
||||
{
|
||||
Point[] controlPoints = {start, _segment.Points[i], _segment.Points[i + 1]};
|
||||
var resultPolyline = new List<Point>();
|
||||
BezierCurveFlattener.FlattenQuadratic(controlPoints, tolerance, resultPolyline, true);
|
||||
points.AddRange(resultPolyline);
|
||||
start = _segment.Points[i + 1];
|
||||
}
|
||||
}
|
||||
|
||||
public override Point GetPoint(int index)
|
||||
{
|
||||
var num = _segment.Points.Count / 2 * 2;
|
||||
if (index < -1 || index > num - 1) throw new ArgumentOutOfRangeException(nameof(index));
|
||||
if (index != -1) return _segment.Points[index];
|
||||
return _segment.Points[num - 1];
|
||||
}
|
||||
|
||||
public override IEnumerable<SimpleSegment> GetSimpleSegments()
|
||||
{
|
||||
var start = Start;
|
||||
IList<Point> points = _segment.Points;
|
||||
var iteratorVariable2 = _segment.Points.Count / 2;
|
||||
var iteratorVariable3 = 0;
|
||||
while (true)
|
||||
{
|
||||
if (iteratorVariable3 >= iteratorVariable2) yield break;
|
||||
var iteratorVariable4 = iteratorVariable3 * 2;
|
||||
yield return SimpleSegment.Create(start, points[iteratorVariable4],
|
||||
points[iteratorVariable4 + 1]);
|
||||
start = points[iteratorVariable4 + 1];
|
||||
iteratorVariable3++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class QuadraticBezierSegmentImplementation : PathSegmentImplementation
|
||||
{
|
||||
private QuadraticBezierSegment _segment;
|
||||
|
||||
public override PathSegment ApplyTransform(GeneralTransform transform)
|
||||
{
|
||||
_segment.Point1 = transform.Transform(_segment.Point1);
|
||||
_segment.Point2 = transform.Transform(_segment.Point2);
|
||||
return _segment;
|
||||
}
|
||||
|
||||
public static PathSegmentImplementation Create(QuadraticBezierSegment source)
|
||||
{
|
||||
if (source != null) return new QuadraticBezierSegmentImplementation {_segment = source};
|
||||
return null;
|
||||
}
|
||||
|
||||
public override void Flatten(IList<Point> points, double tolerance)
|
||||
{
|
||||
Point[] controlPoints = {Start, _segment.Point1, _segment.Point2};
|
||||
var resultPolyline = new List<Point>();
|
||||
BezierCurveFlattener.FlattenQuadratic(controlPoints, tolerance, resultPolyline, true);
|
||||
points.AddRange(resultPolyline);
|
||||
}
|
||||
|
||||
public override Point GetPoint(int index)
|
||||
{
|
||||
if (index < -1 || index > 1) throw new ArgumentOutOfRangeException(nameof(index));
|
||||
return index == 0 ? _segment.Point1 : _segment.Point2;
|
||||
}
|
||||
|
||||
public override IEnumerable<SimpleSegment> GetSimpleSegments()
|
||||
{
|
||||
yield return SimpleSegment.Create(Start, _segment.Point1, _segment.Point2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
|
||||
namespace HandyControl.Expression.Drawing
|
||||
{
|
||||
internal class PolylineData
|
||||
{
|
||||
private IList<double> _accumulates;
|
||||
|
||||
private IList<double> _angles;
|
||||
|
||||
private IList<double> _lengths;
|
||||
|
||||
private IList<Vector> _normals;
|
||||
|
||||
private double? _totalLength;
|
||||
|
||||
public PolylineData(IList<Point> points)
|
||||
{
|
||||
if (points == null) throw new ArgumentNullException(nameof(points));
|
||||
if (points.Count <= 1) throw new ArgumentOutOfRangeException(nameof(points));
|
||||
Points = points;
|
||||
}
|
||||
|
||||
public IList<double> AccumulatedLength =>
|
||||
_accumulates ?? ComputeAccumulatedLength();
|
||||
|
||||
public IList<double> Angles =>
|
||||
_angles ?? ComputeAngles();
|
||||
|
||||
public int Count =>
|
||||
Points.Count;
|
||||
|
||||
public bool IsClosed =>
|
||||
Points[0] == Points.Last();
|
||||
|
||||
public IList<double> Lengths =>
|
||||
_lengths ?? ComputeLengths();
|
||||
|
||||
public IList<Vector> Normals =>
|
||||
_normals ?? ComputeNormals();
|
||||
|
||||
public IList<Point> Points { get; }
|
||||
|
||||
public double TotalLength
|
||||
{
|
||||
get
|
||||
{
|
||||
var length = _totalLength;
|
||||
return length ?? ComputeTotalLength();
|
||||
}
|
||||
}
|
||||
|
||||
private IList<double> ComputeAccumulatedLength()
|
||||
{
|
||||
_accumulates = new double[Count];
|
||||
_accumulates[0] = 0.0;
|
||||
for (var i = 1; i < Count; i++) _accumulates[i] = _accumulates[i - 1] + Lengths[i - 1];
|
||||
_totalLength = _accumulates.Last();
|
||||
return _accumulates;
|
||||
}
|
||||
|
||||
private IList<double> ComputeAngles()
|
||||
{
|
||||
_angles = new double[Count];
|
||||
for (var i = 1; i < Count - 1; i++) _angles[i] = -GeometryHelper.Dot(Normals[i - 1], Normals[i]);
|
||||
if (IsClosed)
|
||||
{
|
||||
var num2 = -GeometryHelper.Dot(Normals[0], Normals[Count - 2]);
|
||||
_angles[0] = _angles[Count - 1] = num2;
|
||||
}
|
||||
else
|
||||
{
|
||||
_angles[0] = _angles[Count - 1] = 1.0;
|
||||
}
|
||||
|
||||
return _angles;
|
||||
}
|
||||
|
||||
private IList<double> ComputeLengths()
|
||||
{
|
||||
_lengths = new double[Count];
|
||||
for (var i = 0; i < Count; i++) _lengths[i] = Difference(i).Length;
|
||||
return _lengths;
|
||||
}
|
||||
|
||||
private IList<Vector> ComputeNormals()
|
||||
{
|
||||
_normals = new Vector[Points.Count];
|
||||
for (var i = 0; i < Count - 1; i++) _normals[i] = GeometryHelper.Normal(Points[i], Points[i + 1]);
|
||||
_normals[Count - 1] = _normals[Count - 2];
|
||||
return _normals;
|
||||
}
|
||||
|
||||
private double ComputeTotalLength()
|
||||
{
|
||||
ComputeAccumulatedLength();
|
||||
// ReSharper disable once PossibleInvalidOperationException
|
||||
return _totalLength.Value;
|
||||
}
|
||||
|
||||
public Vector Difference(int index)
|
||||
{
|
||||
var num = (index + 1) % Count;
|
||||
return Points[num].Subtract(Points[index]);
|
||||
}
|
||||
|
||||
public Vector SmoothNormal(int index, double fraction, double cornerRadius)
|
||||
{
|
||||
if (cornerRadius > 0.0)
|
||||
{
|
||||
var num = Lengths[index];
|
||||
if (MathHelper.IsVerySmall(num))
|
||||
{
|
||||
var num2 = index - 1;
|
||||
if (num2 < 0 && IsClosed) num2 = Count - 1;
|
||||
var num3 = index + 1;
|
||||
if (IsClosed && num3 >= Count - 1) num3 = 0;
|
||||
if (num2 >= 0 && num3 < Count)
|
||||
return GeometryHelper.Lerp(Normals[num3], Normals[num2], 0.5).Normalized();
|
||||
return Normals[index];
|
||||
}
|
||||
|
||||
var num4 = Math.Min(cornerRadius / num, 0.5);
|
||||
if (fraction <= num4)
|
||||
{
|
||||
var num5 = index - 1;
|
||||
if (IsClosed && num5 == -1) num5 = Count - 1;
|
||||
if (num5 >= 0)
|
||||
{
|
||||
var alpha = (num4 - fraction) / (2.0 * num4);
|
||||
return GeometryHelper.Lerp(Normals[index], Normals[num5], alpha).Normalized();
|
||||
}
|
||||
}
|
||||
else if (fraction >= 1.0 - num4)
|
||||
{
|
||||
var num7 = index + 1;
|
||||
if (IsClosed && num7 >= Count - 1) num7 = 0;
|
||||
if (num7 < Count)
|
||||
{
|
||||
var num8 = (fraction + num4 - 1.0) / (2.0 * num4);
|
||||
return GeometryHelper.Lerp(Normals[index], Normals[num7], num8).Normalized();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Normals[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace HandyControl.Expression.Drawing
|
||||
{
|
||||
internal static class PolylineHelper
|
||||
{
|
||||
public static IEnumerable<PolylineData> GetWrappedPolylines(IList<PolylineData> lines,
|
||||
ref double startArcLength)
|
||||
{
|
||||
var count = 0;
|
||||
for (var i = 0; i < lines.Count; i++)
|
||||
{
|
||||
count = i;
|
||||
startArcLength -= lines[i].TotalLength;
|
||||
if (MathHelper.LessThanOrClose(startArcLength, 0.0)) break;
|
||||
}
|
||||
|
||||
if (!MathHelper.LessThanOrClose(startArcLength, 0.0))
|
||||
throw new ArgumentOutOfRangeException(nameof(startArcLength));
|
||||
startArcLength += lines[count].TotalLength;
|
||||
return lines.Skip(count).Concat(lines.Take(count + 1));
|
||||
}
|
||||
|
||||
public static void PathMarch(PolylineData polyline, double startArcLength, double cornerThreshold,
|
||||
Func<MarchLocation, double> stopCallback)
|
||||
{
|
||||
if (polyline == null) throw new ArgumentNullException(nameof(polyline));
|
||||
var count = polyline.Count;
|
||||
if (count <= 1) throw new ArgumentOutOfRangeException(nameof(polyline));
|
||||
var flag = false;
|
||||
var x = startArcLength;
|
||||
var before = 0.0;
|
||||
var index = 0;
|
||||
var num5 = Math.Cos(cornerThreshold * 3.1415926535897931 / 180.0);
|
||||
while (true)
|
||||
{
|
||||
var num6 = polyline.Lengths[index];
|
||||
if (!MathHelper.IsFiniteDouble(x)) return;
|
||||
if (MathHelper.IsVerySmall(x))
|
||||
{
|
||||
x = stopCallback(MarchLocation.Create(MarchStopReason.CompleteStep, index, before,
|
||||
num6 - before, x));
|
||||
flag = true;
|
||||
}
|
||||
else if (MathHelper.GreaterThan(x, 0.0))
|
||||
{
|
||||
if (MathHelper.LessThanOrClose(x + before, num6))
|
||||
{
|
||||
before += x;
|
||||
x = stopCallback(MarchLocation.Create(MarchStopReason.CompleteStep, index, before,
|
||||
num6 - before, 0.0));
|
||||
flag = true;
|
||||
}
|
||||
else if (index < count - 2)
|
||||
{
|
||||
index++;
|
||||
var num7 = num6 - before;
|
||||
x -= num7;
|
||||
before = 0.0;
|
||||
// ReSharper disable once CompareOfFloatsByEqualityOperator
|
||||
if (flag && num5 != 1.0 && polyline.Angles[index] > num5)
|
||||
{
|
||||
num6 = polyline.Lengths[index];
|
||||
x = stopCallback(MarchLocation.Create(MarchStopReason.CornerPoint, index, before,
|
||||
num6 - before, x));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var num8 = num6 - before;
|
||||
x -= num8;
|
||||
num6 = polyline.Lengths[index];
|
||||
before = polyline.Lengths[index];
|
||||
x = stopCallback(MarchLocation.Create(MarchStopReason.CompletePolyline, index, before,
|
||||
num6 - before, x));
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
else if (MathHelper.LessThan(x, 0.0))
|
||||
{
|
||||
if (MathHelper.GreaterThanOrClose(x + before, 0.0))
|
||||
{
|
||||
before += x;
|
||||
x = stopCallback(MarchLocation.Create(MarchStopReason.CompleteStep, index, before,
|
||||
num6 - before, 0.0));
|
||||
flag = true;
|
||||
}
|
||||
else if (index > 0)
|
||||
{
|
||||
index--;
|
||||
x += before;
|
||||
before = polyline.Lengths[index];
|
||||
// ReSharper disable once CompareOfFloatsByEqualityOperator
|
||||
if (flag && num5 != 1.0 && polyline.Angles[index + 1] > num5)
|
||||
{
|
||||
num6 = polyline.Lengths[index];
|
||||
x = stopCallback(MarchLocation.Create(MarchStopReason.CornerPoint, index, before,
|
||||
num6 - before, x));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
x += before;
|
||||
num6 = polyline.Lengths[index];
|
||||
before = 0.0;
|
||||
x = stopCallback(MarchLocation.Create(MarchStopReason.CompletePolyline, index, before,
|
||||
num6 - before, x));
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
using System;
|
||||
|
||||
namespace HandyControl.Expression.Drawing
|
||||
{
|
||||
internal class RandomEngine
|
||||
{
|
||||
private double? _anotherSample;
|
||||
|
||||
private Random _random;
|
||||
|
||||
public RandomEngine(long seed) => Initialize(seed);
|
||||
|
||||
private double Gaussian()
|
||||
{
|
||||
double num;
|
||||
double num2;
|
||||
double num3;
|
||||
if (_anotherSample.HasValue)
|
||||
{
|
||||
var num4 = _anotherSample.Value;
|
||||
_anotherSample = null;
|
||||
return num4;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
num2 = 2.0 * Uniform() - 1.0;
|
||||
num3 = 2.0 * Uniform() - 1.0;
|
||||
num = num2 * num2 + num3 * num3;
|
||||
} while (num >= 1.0);
|
||||
|
||||
var num5 = Math.Sqrt(-2.0 * Math.Log(num) / num);
|
||||
_anotherSample = num2 * num5;
|
||||
return num3 * num5;
|
||||
}
|
||||
|
||||
private void Initialize(long seed) => _random = new Random((int) seed);
|
||||
|
||||
public double NextGaussian(double mean, double variance) => Gaussian() * variance + mean;
|
||||
|
||||
public double NextUniform(double min, double max) => Uniform() * (max - min) + min;
|
||||
|
||||
private double Uniform() => _random.NextDouble();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Windows;
|
||||
|
||||
namespace HandyControl.Expression.Drawing
|
||||
{
|
||||
internal class SimpleSegment
|
||||
{
|
||||
public enum SegmentType
|
||||
{
|
||||
Line,
|
||||
|
||||
CubicBeizer
|
||||
}
|
||||
|
||||
|
||||
private SimpleSegment()
|
||||
{
|
||||
}
|
||||
|
||||
public Point[] Points { get; private set; }
|
||||
|
||||
public SegmentType Type { get; private set; }
|
||||
|
||||
public static SimpleSegment Create(Point point0, Point point1)
|
||||
{
|
||||
var segment = new SimpleSegment
|
||||
{
|
||||
Type = SegmentType.Line,
|
||||
Points = new[] {point0, point1}
|
||||
};
|
||||
return segment;
|
||||
}
|
||||
|
||||
public static SimpleSegment Create(Point point0, Point point1, Point point2)
|
||||
{
|
||||
var point = GeometryHelper.Lerp(point0, point1, 0.66666666666666663);
|
||||
var point3 = GeometryHelper.Lerp(point1, point2, 0.33333333333333331);
|
||||
var segment = new SimpleSegment
|
||||
{
|
||||
Type = SegmentType.CubicBeizer,
|
||||
Points = new[] {point0, point, point3, point2}
|
||||
};
|
||||
return segment;
|
||||
}
|
||||
|
||||
public static SimpleSegment Create(Point point0, Point point1, Point point2, Point point3)
|
||||
{
|
||||
var segment = new SimpleSegment
|
||||
{
|
||||
Type = SegmentType.CubicBeizer,
|
||||
Points = new[] {point0, point1, point2, point3}
|
||||
};
|
||||
return segment;
|
||||
}
|
||||
|
||||
public void Flatten(IList<Point> resultPolyline, double tolerance, IList<double> resultParameters)
|
||||
{
|
||||
switch (Type)
|
||||
{
|
||||
case SegmentType.Line:
|
||||
resultPolyline.Add(Points[1]);
|
||||
if (resultParameters == null) break;
|
||||
resultParameters.Add(1.0);
|
||||
return;
|
||||
|
||||
case SegmentType.CubicBeizer:
|
||||
BezierCurveFlattener.FlattenCubic(Points, tolerance, resultPolyline, true,
|
||||
resultParameters);
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user