add CircleProgressBar

thanks to @ghost1372 suggestion
This commit is contained in:
NaBian committed 2018-11-01 17:45:45 +08:00
1 parent fb71862464
commit 4aa4afd292
49 files changed
+3697 -40

No files matched your search

@@ -0,0 +1,81 @@
using System.Windows;
using System.Windows.Controls.Primitives;
using HandyControl.Data;
using HandyControl.Expression.Shapes;
namespace HandyControl.Controls
{
[TemplatePart(Name = IndicatorTemplateName, Type = typeof(Arc))]
public class CircleProgressBar : RangeBase
{
private const string IndicatorTemplateName = "PART_Indicator";
public static readonly DependencyProperty ArcThicknessProperty = DependencyProperty.Register(
"ArcThickness", typeof(double), typeof(CircleProgressBar), new PropertyMetadata(default(double)));
public static readonly DependencyProperty ShowTextProperty = DependencyProperty.Register(
"ShowText", typeof(bool), typeof(CircleProgressBar), new PropertyMetadata(true));
private Arc _indicator;
static CircleProgressBar()
{
FocusableProperty.OverrideMetadata(typeof(CircleProgressBar),
new FrameworkPropertyMetadata(BooleanBoxes.FalseBox));
MaximumProperty.OverrideMetadata(typeof(CircleProgressBar), new FrameworkPropertyMetadata(100.0));
}
public bool ShowText
{
get => (bool) GetValue(ShowTextProperty);
set => SetValue(ShowTextProperty, value);
}
public double ArcThickness
{
get => (double) GetValue(ArcThicknessProperty);
set => SetValue(ArcThicknessProperty, value);
}
private void SetProgressBarIndicatorAngle()
{
if (_indicator == null) return;
var minimum = Minimum;
var maximum = Maximum;
var num = Value;
_indicator.EndAngle = (maximum <= minimum ? 0 : (num - minimum) / (maximum - minimum)) * 360;
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_indicator = GetTemplateChild(IndicatorTemplateName) as Arc;
if (_indicator != null)
{
_indicator.StartAngle = 0;
_indicator.EndAngle = 0;
}
SetProgressBarIndicatorAngle();
}
protected override void OnMinimumChanged(double oldMinimum, double newMinimum)
{
base.OnMinimumChanged(oldMinimum, newMinimum);
SetProgressBarIndicatorAngle();
}
protected override void OnMaximumChanged(double oldMaximum, double newMaximum)
{
base.OnMaximumChanged(oldMaximum, newMaximum);
SetProgressBarIndicatorAngle();
}
protected override void OnValueChanged(double oldValue, double newValue)
{
base.OnValueChanged(oldValue, newValue);
SetProgressBarIndicatorAngle();
}
}
}
+6 -6
View File
@@ -284,7 +284,7 @@ namespace HandyControl.Controls
/// <param name="growlInfo"></param>
public static void Success(GrowlInfo growlInfo)
{
if (growlInfo == null) throw new NullReferenceException("growlInfo is null");
if (growlInfo == null) throw new ArgumentNullException(nameof(growlInfo));
growlInfo.IconKey = ResourceToken.SuccessGeometry;
growlInfo.IconBrushKey = ResourceToken.SuccessBrush;
growlInfo.Type = InfoType.Success;
@@ -306,7 +306,7 @@ namespace HandyControl.Controls
/// <param name="growlInfo"></param>
public static void Info(GrowlInfo growlInfo)
{
if (growlInfo == null) throw new NullReferenceException("growlInfo is null");
if (growlInfo == null) throw new ArgumentNullException(nameof(growlInfo));
growlInfo.IconKey = ResourceToken.InfoGeometry;
growlInfo.IconBrushKey = ResourceToken.InfoBrush;
growlInfo.Type = InfoType.Info;
@@ -328,7 +328,7 @@ namespace HandyControl.Controls
/// <param name="growlInfo"></param>
public static void Warning(GrowlInfo growlInfo)
{
if (growlInfo == null) throw new NullReferenceException("growlInfo is null");
if (growlInfo == null) throw new ArgumentNullException(nameof(growlInfo));
growlInfo.IconKey = ResourceToken.WarningGeometry;
growlInfo.IconBrushKey = ResourceToken.WarningBrush;
growlInfo.Type = InfoType.Warning;
@@ -350,7 +350,7 @@ namespace HandyControl.Controls
/// <param name="growlInfo"></param>
public static void Error(GrowlInfo growlInfo)
{
if (growlInfo == null) throw new NullReferenceException("growlInfo is null");
if (growlInfo == null) throw new ArgumentNullException(nameof(growlInfo));
growlInfo.IconKey = ResourceToken.ErrorGeometry;
growlInfo.IconBrushKey = ResourceToken.DangerBrush;
growlInfo.StaysOpen = true;
@@ -373,7 +373,7 @@ namespace HandyControl.Controls
/// <param name="growlInfo"></param>
public static void Fatal(GrowlInfo growlInfo)
{
if (growlInfo == null) throw new NullReferenceException("growlInfo is null");
if (growlInfo == null) throw new ArgumentNullException(nameof(growlInfo));
growlInfo.IconKey = ResourceToken.FatalGeometry;
growlInfo.IconBrushKey = ResourceToken.PrimaryTextBrush;
growlInfo.StaysOpen = true;
@@ -401,7 +401,7 @@ namespace HandyControl.Controls
/// <param name="growlInfo"></param>
public static void Ask(GrowlInfo growlInfo)
{
if (growlInfo == null) throw new NullReferenceException("growlInfo is null");
if (growlInfo == null) throw new ArgumentNullException(nameof(growlInfo));
growlInfo.IconKey = ResourceToken.AskGeometry;
growlInfo.IconBrushKey = ResourceToken.AccentBrush;
growlInfo.StaysOpen = true;
@@ -1,14 +1,12 @@
using System;
using System.Windows;
using System.Windows;
using System.Windows.Controls;
// ReSharper disable once CheckNamespace
namespace HandyControl.Controls
{
internal class SliderCanvas : Canvas
internal class InternalCanvas : Canvas
{
internal static readonly DependencyProperty OrientationProperty = DependencyProperty.Register(
"Orientation", typeof(Orientation), typeof(SliderCanvas), new PropertyMetadata(default(Orientation)));
"Orientation", typeof(Orientation), typeof(InternalCanvas), new PropertyMetadata(default(Orientation)));
internal Orientation Orientation
{
@@ -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;
}
}
}
}
@@ -0,0 +1,470 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Media;
using HandyControl.Expression.Drawing;
namespace HandyControl.Expression.Media
{
internal class ArcGeometrySource : GeometrySource<IArcGeometrySourceParameters>
{
private double _absoluteThickness;
private double _relativeThickness;
private static bool AreCloseEnough(double angleA, double angleB)
{
return Math.Abs(angleA - angleB) < 0.001;
}
internal static double[] ComputeAngleRanges(double radiusX, double radiusY, double intersect,
double start, double end)
{
var values = new List<double>
{
start,
end,
intersect,
180.0 - intersect,
180.0 + intersect,
360.0 - intersect,
360.0 + intersect,
540.0 - intersect,
540.0 + intersect,
720.0 - intersect
};
values.Sort();
var index = values.IndexOf(start);
var num2 = values.IndexOf(end);
if (num2 == index)
{
num2++;
}
else if (start < end)
{
IncreaseDuplicatedIndex(values, ref index);
DecreaseDuplicatedIndex(values, ref num2);
}
else if (start > end)
{
DecreaseDuplicatedIndex(values, ref index);
IncreaseDuplicatedIndex(values, ref num2);
}
var list = new List<double>();
if (index < num2)
for (var i = index; i <= num2; i++)
list.Add(values[i]);
else
for (var j = index; j >= num2; j--)
list.Add(values[j]);
var num5 = EnsureFirstQuadrant((list[0] + list[1]) / 2.0);
if (radiusX < radiusY && num5 < intersect || radiusX > radiusY && num5 > intersect)
list.RemoveAt(0);
if (list.Count % 2 == 1) list.RemoveLast();
if (list.Count == 0)
{
var num6 = Math.Min(index, num2) - 1;
if (num6 < 0) num6 = Math.Max(index, num2) + 1;
list.Add(values[num6]);
list.Add(values[num6]);
}
return list.ToArray();
}
protected override Rect ComputeLogicalBounds(Rect layoutBounds, IGeometrySourceParameters parameters)
{
return GeometryHelper.GetStretchBound(base.ComputeLogicalBounds(layoutBounds, parameters),
parameters.Stretch, new Size(1.0, 1.0));
}
private static IList<Point> ComputeOneInnerCurve(double start, double end, Rect bounds, double offset)
{
var num = bounds.Width / 2.0;
var num2 = bounds.Height / 2.0;
var point = bounds.Center();
start = start * 3.1415926535897931 / 180.0;
end = end * 3.1415926535897931 / 180.0;
var num3 = 0.17453292519943295;
var num4 = (int) Math.Ceiling(Math.Abs(end - start) / num3);
num4 = Math.Max(2, num4);
var list = new List<Point>(num4);
var list2 = new List<Vector>(num4);
var lhs = new Point();
var item = new Point();
var vector = new Vector();
var vector2 = new Vector();
var vector3 = new Vector();
var vector4 = new Vector();
for (var i = 0; i < num4; i++)
{
var a = MathHelper.Lerp(start, end, i / (double) (num4 - 1));
var num10 = Math.Sin(a);
var num11 = Math.Cos(a);
lhs.X = point.X + num * num10;
lhs.Y = point.Y - num2 * num11;
vector.X = num * num11;
vector.Y = num2 * num10;
vector2.X = -num2 * num10;
vector2.Y = num * num11;
var d = num2 * num2 * num10 * num10 + num * num * num11 * num11;
var num6 = Math.Sqrt(d);
var num7 = 2.0 * num10 * num11 * (num2 * num2 - num * num);
vector3.X = -num2 * num11;
vector3.Y = -num * num10;
item.X = lhs.X + offset * vector2.X / num6;
item.Y = lhs.Y + offset * vector2.Y / num6;
vector4.X = vector.X + offset / num6 * (vector3.X - 0.5 * vector2.X / d * num7);
vector4.Y = vector.Y + offset / num6 * (vector3.Y - 0.5 * vector2.Y / d * num7);
list.Add(item);
list2.Add(-vector4.Normalized());
}
var list3 = new List<Point>(num4 * 3 + 1)
{
list[0]
};
for (var j = 1; j < num4; j++)
{
lhs = list[j - 1];
item = list[j];
var num13 = GeometryHelper.Distance(lhs, item) / 3.0;
list3.Add(lhs + list2[j - 1] * num13);
list3.Add(item - list2[j] * num13);
list3.Add(item);
}
return list3;
}
private static void DecreaseDuplicatedIndex(IList<double> values, ref int index)
{
// ReSharper disable once CompareOfFloatsByEqualityOperator
while (index > 0 && values[index] == values[index - 1]) index--;
}
internal static double EnsureFirstQuadrant(double angle)
{
angle = Math.Abs(angle % 180.0);
if (angle <= 90.0) return angle;
return 180.0 - angle;
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static Size GetArcSize(Rect bound)
{
return new Size(bound.Width / 2.0, bound.Height / 2.0);
}
private static void IncreaseDuplicatedIndex(IList<double> values, ref int index)
{
// ReSharper disable once CompareOfFloatsByEqualityOperator
while (index < values.Count - 1 && values[index] == values[index + 1]) index++;
}
internal static double InnerCurveSelfIntersect(double radiusX, double radiusY, double thickness)
{
var angleA = 0.0;
var angleB = 1.5707963267948966;
var flag = radiusX <= radiusY;
var vector = new Vector();
while (!AreCloseEnough(angleA, angleB))
{
var d = (angleA + angleB) / 2.0;
var num4 = Math.Cos(d);
var num5 = Math.Sin(d);
vector.X = radiusY * num5;
vector.Y = radiusX * num4;
vector.Normalize();
if (flag)
{
var num6 = radiusX * num5 - vector.X * thickness;
if (num6 > 0.0)
angleB = d;
else if (num6 < 0.0) angleA = d;
}
else
{
var num7 = radiusY * num4 - vector.Y * thickness;
if (num7 < 0.0)
angleB = d;
else if (num7 > 0.0) angleA = d;
}
}
var num8 = (angleA + angleB) / 2.0;
if (AreCloseEnough(num8, 0.0)) return 0.0;
if (!AreCloseEnough(num8, 1.5707963267948966)) return num8 * 180.0 / 3.1415926535897931;
return 90.0;
}
private static double NormalizeAngle(double degree)
{
if (degree < 0.0 || degree > 360.0)
{
degree = degree % 360.0;
if (degree < 0.0) degree += 360.0;
}
return degree;
}
private void NormalizeThickness(IArcGeometrySourceParameters parameters)
{
var num = LogicalBounds.Width / 2.0;
var num2 = LogicalBounds.Height / 2.0;
var rhs = Math.Min(num, num2);
var arcThickness = parameters.ArcThickness;
if (parameters.ArcThicknessUnit == UnitType.Pixel)
arcThickness = MathHelper.SafeDivide(arcThickness, rhs, 0.0);
_relativeThickness = MathHelper.EnsureRange(arcThickness, 0.0, 1.0);
_absoluteThickness = rhs * _relativeThickness;
}
private bool SyncPieceWiseInnerCurves(PathFigure figure, int index, ref Point firstPoint,
params double[] angles)
{
var flag = false;
var length = angles.Length;
var logicalBounds = LogicalBounds;
var absoluteThickness = _absoluteThickness;
flag |= figure.Segments.EnsureListCount(index + length / 2, () => new PolyBezierSegment());
for (var i = 0; i < length / 2; i++)
{
var points = ComputeOneInnerCurve(angles[i * 2], angles[i * 2 + 1], logicalBounds,
absoluteThickness);
if (i == 0) firstPoint = points[0];
flag |= PathSegmentHelper.SyncPolyBezierSegment(figure.Segments, index + i, points, 1,
points.Count - 1);
}
return flag;
}
[SuppressMessage("ReSharper", "ConditionIsAlwaysTrueOrFalse")]
[SuppressMessage("ReSharper", "CompareOfFloatsByEqualityOperator")]
protected override bool UpdateCachedGeometry(IArcGeometrySourceParameters parameters)
{
var flag = false;
NormalizeThickness(parameters);
var relativeMode = parameters.ArcThicknessUnit == UnitType.Percent;
var flag3 = MathHelper.AreClose(parameters.StartAngle, parameters.EndAngle);
var angle = NormalizeAngle(parameters.StartAngle);
var end = NormalizeAngle(parameters.EndAngle);
if (end < angle) end += 360.0;
var isFilled = _relativeThickness == 1.0;
var flag5 = _relativeThickness == 0.0;
if (flag3) return flag | UpdateZeroAngleGeometry(relativeMode, angle);
if (MathHelper.IsVerySmall((end - angle) % 360.0))
{
if (flag5 || isFilled) return flag | UpdateEllipseGeometry(isFilled);
return flag | UpdateFullRingGeometry(relativeMode);
}
if (isFilled) return flag | UpdatePieGeometry(angle, end);
if (flag5) return flag | UpdateOpenArcGeometry(angle, end);
return flag | UpdateRingArcGeometry(relativeMode, angle, end);
}
private bool UpdateEllipseGeometry(bool isFilled)
{
var flag = false;
var y = MathHelper.Lerp(LogicalBounds.Top, LogicalBounds.Bottom, 0.5);
var point = new Point(LogicalBounds.Left, y);
var point2 = new Point(LogicalBounds.Right, y);
flag |= GeometryHelper.EnsureGeometryType(out var geometry, ref CachedGeometry,
() => new PathGeometry());
flag |= geometry.Figures.EnsureListCount(1, () => new PathFigure());
var dependencyObject = geometry.Figures[0];
flag |= dependencyObject.SetIfDifferent(PathFigure.IsClosedProperty, true);
flag |= dependencyObject.SetIfDifferent(PathFigure.IsFilledProperty, isFilled);
flag |= dependencyObject.Segments.EnsureListCount(2, () => new ArcSegment());
flag |= dependencyObject.SetIfDifferent(PathFigure.StartPointProperty, point);
flag |= GeometryHelper.EnsureSegmentType(out var segment, dependencyObject.Segments, 0,
() => new ArcSegment());
flag |= GeometryHelper.EnsureSegmentType(out var segment2, dependencyObject.Segments, 1,
() => new ArcSegment());
var size = new Size(LogicalBounds.Width / 2.0, LogicalBounds.Height / 2.0);
flag |= segment.SetIfDifferent(ArcSegment.IsLargeArcProperty, false);
flag |= segment.SetIfDifferent(ArcSegment.SizeProperty, size);
flag |= segment.SetIfDifferent(ArcSegment.SweepDirectionProperty, SweepDirection.Clockwise);
flag |= segment.SetIfDifferent(ArcSegment.PointProperty, point2);
flag |= segment2.SetIfDifferent(ArcSegment.IsLargeArcProperty, false);
flag |= segment2.SetIfDifferent(ArcSegment.SizeProperty, size);
flag |= segment2.SetIfDifferent(ArcSegment.SweepDirectionProperty, SweepDirection.Clockwise);
return flag | segment2.SetIfDifferent(ArcSegment.PointProperty, point);
}
private bool UpdateFullRingGeometry(bool relativeMode)
{
var flag = false;
flag |= GeometryHelper.EnsureGeometryType(out var geometry, ref CachedGeometry,
() => new PathGeometry());
flag |= geometry.SetIfDifferent(PathGeometry.FillRuleProperty, FillRule.EvenOdd);
flag |= geometry.Figures.EnsureListCount(2, () => new PathFigure());
flag |= PathFigureHelper.SyncEllipseFigure(geometry.Figures[0], LogicalBounds,
SweepDirection.Clockwise);
var logicalBounds = LogicalBounds;
var num = logicalBounds.Width / 2.0;
var num2 = logicalBounds.Height / 2.0;
if (relativeMode || MathHelper.AreClose(num, num2))
{
var bounds = LogicalBounds.Resize(1.0 - _relativeThickness);
return flag | PathFigureHelper.SyncEllipseFigure(geometry.Figures[1], bounds,
SweepDirection.Counterclockwise);
}
flag |= geometry.Figures[1].SetIfDifferent(PathFigure.IsClosedProperty, true);
flag |= geometry.Figures[1].SetIfDifferent(PathFigure.IsFilledProperty, true);
var firstPoint = new Point();
var intersect = InnerCurveSelfIntersect(num, num2, _absoluteThickness);
var angles = ComputeAngleRanges(num, num2, intersect, 360.0, 0.0);
flag |= SyncPieceWiseInnerCurves(geometry.Figures[1], 0, ref firstPoint, angles);
return flag | geometry.Figures[1].SetIfDifferent(PathFigure.StartPointProperty, firstPoint);
}
private bool UpdateOpenArcGeometry(double start, double end)
{
PathFigure figure;
ArcSegment segment;
var flag = false;
var cachedGeometry = CachedGeometry as PathGeometry;
if (cachedGeometry == null || cachedGeometry.Figures.Count != 1 ||
(figure = cachedGeometry.Figures[0]).Segments.Count != 1 ||
(segment = figure.Segments[0] as ArcSegment) == null)
{
figure = new PathFigure();
CachedGeometry = new PathGeometry
{
Figures = {figure}
};
figure.Segments.Add(segment = new ArcSegment());
figure.IsClosed = false;
segment.SweepDirection = SweepDirection.Clockwise;
flag = true;
}
flag |= figure.SetIfDifferent(PathFigure.StartPointProperty,
GeometryHelper.GetArcPoint(start, LogicalBounds));
flag |= figure.SetIfDifferent(PathFigure.IsFilledProperty, false);
flag |= segment.SetIfDifferent(ArcSegment.PointProperty,
GeometryHelper.GetArcPoint(end, LogicalBounds));
flag |= segment.SetIfDifferent(ArcSegment.SizeProperty, GetArcSize(LogicalBounds));
return flag | segment.SetIfDifferent(ArcSegment.IsLargeArcProperty, end - start > 180.0);
}
private bool UpdatePieGeometry(double start, double end)
{
PathFigure figure;
ArcSegment segment;
LineSegment segment2;
var flag = false;
var cachedGeometry = CachedGeometry as PathGeometry;
if (cachedGeometry == null || cachedGeometry.Figures.Count != 1 ||
(figure = cachedGeometry.Figures[0]).Segments.Count != 2 ||
(segment = figure.Segments[0] as ArcSegment) == null ||
(segment2 = figure.Segments[1] as LineSegment) == null)
{
figure = new PathFigure();
CachedGeometry = new PathGeometry
{
Figures = {figure}
};
figure.Segments.Add(segment = new ArcSegment());
figure.Segments.Add(segment2 = new LineSegment());
figure.IsClosed = true;
segment.SweepDirection = SweepDirection.Clockwise;
flag = true;
}
flag |= figure.SetIfDifferent(PathFigure.StartPointProperty,
GeometryHelper.GetArcPoint(start, LogicalBounds));
flag |= segment.SetIfDifferent(ArcSegment.PointProperty,
GeometryHelper.GetArcPoint(end, LogicalBounds));
flag |= segment.SetIfDifferent(ArcSegment.SizeProperty, GetArcSize(LogicalBounds));
flag |= segment.SetIfDifferent(ArcSegment.IsLargeArcProperty, end - start > 180.0);
return flag | segment2.SetIfDifferent(LineSegment.PointProperty, LogicalBounds.Center());
}
private bool UpdateRingArcGeometry(bool relativeMode, double start, double end)
{
var flag = false;
flag |= GeometryHelper.EnsureGeometryType(out var geometry, ref CachedGeometry,
() => new PathGeometry());
flag |= geometry.SetIfDifferent(PathGeometry.FillRuleProperty, FillRule.Nonzero);
flag |= geometry.Figures.EnsureListCount(1, () => new PathFigure());
var dependencyObject = geometry.Figures[0];
flag |= dependencyObject.SetIfDifferent(PathFigure.IsClosedProperty, true);
flag |= dependencyObject.SetIfDifferent(PathFigure.IsFilledProperty, true);
flag |= dependencyObject.SetIfDifferent(PathFigure.StartPointProperty,
GeometryHelper.GetArcPoint(start, LogicalBounds));
flag |= dependencyObject.Segments.EnsureListCountAtLeast(3, () => new ArcSegment());
flag |= GeometryHelper.EnsureSegmentType(out var segment, dependencyObject.Segments, 0,
() => new ArcSegment());
flag |= segment.SetIfDifferent(ArcSegment.PointProperty,
GeometryHelper.GetArcPoint(end, LogicalBounds));
flag |= segment.SetIfDifferent(ArcSegment.SizeProperty,
new Size(LogicalBounds.Width / 2.0, LogicalBounds.Height / 2.0));
flag |= segment.SetIfDifferent(ArcSegment.IsLargeArcProperty, end - start > 180.0);
flag |= segment.SetIfDifferent(ArcSegment.SweepDirectionProperty, SweepDirection.Clockwise);
flag |= GeometryHelper.EnsureSegmentType(out var segment2, dependencyObject.Segments, 1,
() => new LineSegment());
var logicalBounds = LogicalBounds;
var num = logicalBounds.Width / 2.0;
var num2 = logicalBounds.Height / 2.0;
if (relativeMode || MathHelper.AreClose(num, num2))
{
var bound = LogicalBounds.Resize(1.0 - _relativeThickness);
flag |= segment2.SetIfDifferent(LineSegment.PointProperty,
GeometryHelper.GetArcPoint(end, bound));
flag |= dependencyObject.Segments.EnsureListCount(3, () => new ArcSegment());
flag |= GeometryHelper.EnsureSegmentType(out var segment3, dependencyObject.Segments, 2,
() => new ArcSegment());
flag |= segment3.SetIfDifferent(ArcSegment.PointProperty,
GeometryHelper.GetArcPoint(start, bound));
flag |= segment3.SetIfDifferent(ArcSegment.SizeProperty, GetArcSize(bound));
flag |= segment3.SetIfDifferent(ArcSegment.IsLargeArcProperty, end - start > 180.0);
return flag | segment3.SetIfDifferent(ArcSegment.SweepDirectionProperty,
SweepDirection.Counterclockwise);
}
var firstPoint = new Point();
var intersect = InnerCurveSelfIntersect(num, num2, _absoluteThickness);
var angles = ComputeAngleRanges(num, num2, intersect, end, start);
flag |= SyncPieceWiseInnerCurves(dependencyObject, 2, ref firstPoint, angles);
return flag | segment2.SetIfDifferent(LineSegment.PointProperty, firstPoint);
}
private bool UpdateZeroAngleGeometry(bool relativeMode, double angle)
{
Point point2;
var flag = false;
var arcPoint = GeometryHelper.GetArcPoint(angle, LogicalBounds);
var logicalBounds = LogicalBounds;
var num = logicalBounds.Width / 2.0;
var num2 = logicalBounds.Height / 2.0;
if (relativeMode || MathHelper.AreClose(num, num2))
{
var bound = LogicalBounds.Resize(1.0 - _relativeThickness);
point2 = GeometryHelper.GetArcPoint(angle, bound);
}
else
{
var intersect = InnerCurveSelfIntersect(num, num2, _absoluteThickness);
var numArray = ComputeAngleRanges(num, num2, intersect, angle, angle);
var a = numArray[0] * 3.1415926535897931 / 180.0;
var vector = new Vector(num2 * Math.Sin(a), -num * Math.Cos(a));
point2 = GeometryHelper.GetArcPoint(numArray[0], LogicalBounds) -
vector.Normalized() * _absoluteThickness;
}
flag |= GeometryHelper.EnsureGeometryType(out var geometry, ref CachedGeometry,
() => new LineGeometry());
flag |= geometry.SetIfDifferent(LineGeometry.StartPointProperty, arcPoint);
return flag | geometry.SetIfDifferent(LineGeometry.EndPointProperty, point2);
}
}
}
@@ -0,0 +1,11 @@
using System;
namespace HandyControl.Expression.Media
{
internal class DrawingPropertyChangedEventArgs : EventArgs
{
public bool IsAnimated { get; set; }
public DrawingPropertyMetadata Metadata { get; set; }
}
}
@@ -0,0 +1,77 @@
using System;
using System.Windows;
namespace HandyControl.Expression.Media
{
internal class DrawingPropertyMetadata : FrameworkPropertyMetadata
{
private DrawingPropertyMetadataOptions options;
private PropertyChangedCallback propertyChangedCallback;
public static event EventHandler<DrawingPropertyChangedEventArgs> DrawingPropertyChanged;
static DrawingPropertyMetadata()
{
DrawingPropertyChanged += delegate (object sender, DrawingPropertyChangedEventArgs args) {
IShape shape = sender as IShape;
if ((shape != null) && args.Metadata.AffectsRender)
{
InvalidateGeometryReasons reasons = InvalidateGeometryReasons.PropertyChanged;
if (args.IsAnimated)
{
reasons |= InvalidateGeometryReasons.IsAnimated;
}
shape.InvalidateGeometry(reasons);
}
};
}
public DrawingPropertyMetadata(object defaultValue) : this(defaultValue, DrawingPropertyMetadataOptions.None, null)
{
}
public DrawingPropertyMetadata(PropertyChangedCallback propertyChangedCallback) : this(DependencyProperty.UnsetValue, DrawingPropertyMetadataOptions.None, propertyChangedCallback)
{
}
private DrawingPropertyMetadata(DrawingPropertyMetadataOptions options, object defaultValue) : base(defaultValue, (FrameworkPropertyMetadataOptions)options)
{
}
public DrawingPropertyMetadata(object defaultValue, DrawingPropertyMetadataOptions options) : this(defaultValue, options, null)
{
}
public DrawingPropertyMetadata(object defaultValue, DrawingPropertyMetadataOptions options, PropertyChangedCallback propertyChangedCallback) : base(defaultValue, (FrameworkPropertyMetadataOptions)options, AttachCallback(defaultValue, options, propertyChangedCallback))
{
}
private static PropertyChangedCallback AttachCallback(object defaultValue, DrawingPropertyMetadataOptions options, PropertyChangedCallback propertyChangedCallback)
{
DrawingPropertyMetadata metadata = new DrawingPropertyMetadata(options, defaultValue)
{
options = options,
propertyChangedCallback = propertyChangedCallback
};
return new PropertyChangedCallback(metadata.InternalCallback);
}
private void InternalCallback(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
if (DrawingPropertyChanged != null)
{
DrawingPropertyChangedEventArgs args = new DrawingPropertyChangedEventArgs
{
Metadata = this,
IsAnimated = DependencyPropertyHelper.GetValueSource(sender, e.Property).IsAnimated
};
DrawingPropertyChanged(sender, args);
}
if (this.propertyChangedCallback != null)
{
this.propertyChangedCallback(sender, e);
}
}
}
}
@@ -0,0 +1,12 @@
using System;
namespace HandyControl.Expression.Media
{
[Flags]
internal enum DrawingPropertyMetadataOptions
{
AffectsMeasure = 1,
AffectsRender = 0x10,
None = 0
}
}
@@ -0,0 +1,172 @@
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Media;
using System.Windows.Threading;
namespace HandyControl.Expression.Media
{
[TypeConverter(typeof(GeometryEffectConverter))]
public abstract class GeometryEffect : Freezable
{
// ReSharper disable once InconsistentNaming
private static GeometryEffect defaultGeometryEffect;
public static readonly DependencyProperty GeometryEffectProperty =
DependencyProperty.RegisterAttached("GeometryEffect", typeof(GeometryEffect),
typeof(GeometryEffect),
new DrawingPropertyMetadata(DefaultGeometryEffect,
DrawingPropertyMetadataOptions.AffectsRender, OnGeometryEffectChanged));
protected Geometry CachedGeometry;
private bool _effectInvalidated;
static GeometryEffect()
{
DrawingPropertyMetadata.DrawingPropertyChanged +=
delegate(object sender, DrawingPropertyChangedEventArgs args)
{
if (sender is GeometryEffect effect && args.Metadata.AffectsRender)
effect.InvalidateGeometry(InvalidateGeometryReasons.PropertyChanged);
};
}
public static GeometryEffect DefaultGeometryEffect =>
defaultGeometryEffect ?? (defaultGeometryEffect = new NoGeometryEffect());
public Geometry OutputGeometry =>
CachedGeometry;
protected internal DependencyObject Parent { get; private set; }
protected internal virtual void Attach(DependencyObject obj)
{
if (Parent != null) Detach();
_effectInvalidated = true;
CachedGeometry = null;
if (InvalidateParent(obj)) Parent = obj;
}
public new GeometryEffect CloneCurrentValue()
{
return (GeometryEffect) base.CloneCurrentValue();
}
protected override Freezable CreateInstanceCore()
{
return (Freezable) Activator.CreateInstance(GetType());
}
protected abstract GeometryEffect DeepCopy();
protected internal virtual void Detach()
{
_effectInvalidated = true;
CachedGeometry = null;
if (Parent != null)
{
InvalidateParent(Parent);
Parent = null;
}
}
public abstract bool Equals(GeometryEffect geometryEffect);
public static GeometryEffect GetGeometryEffect(DependencyObject obj)
{
return (GeometryEffect) obj.GetValue(GeometryEffectProperty);
}
public bool InvalidateGeometry(InvalidateGeometryReasons reasons)
{
if (_effectInvalidated) return false;
_effectInvalidated = true;
if (reasons != InvalidateGeometryReasons.ParentInvalidated) InvalidateParent(Parent);
return true;
}
private static bool InvalidateParent(DependencyObject parent)
{
if (parent is IShape shape)
{
shape.InvalidateGeometry(InvalidateGeometryReasons.ChildInvalidated);
return true;
}
if (parent is GeometryEffect effect)
{
effect.InvalidateGeometry(InvalidateGeometryReasons.ChildInvalidated);
return true;
}
return false;
}
private static void OnGeometryEffectChanged(DependencyObject obj,
DependencyPropertyChangedEventArgs e)
{
var oldValue = e.OldValue as GeometryEffect;
var newValue = e.NewValue as GeometryEffect;
if (!Equals(oldValue, newValue))
{
if (oldValue != null && obj.Equals(oldValue.Parent)) oldValue.Detach();
if (newValue != null)
{
if (newValue.Parent != null)
{
obj.Dispatcher.BeginInvoke(new Action(() =>
{
var effect = newValue.CloneCurrentValue();
obj.SetValue(GeometryEffectProperty, effect);
}), DispatcherPriority.Send, null);
}
else
{
newValue.Attach(obj);
}
}
}
}
public bool ProcessGeometry(Geometry input)
{
var flag = false;
if (_effectInvalidated)
{
flag |= UpdateCachedGeometry(input);
_effectInvalidated = false;
}
return flag;
}
public static void SetGeometryEffect(DependencyObject obj, GeometryEffect value)
{
obj.SetValue(GeometryEffectProperty, value);
}
protected abstract bool UpdateCachedGeometry(Geometry input);
private class NoGeometryEffect : GeometryEffect
{
protected override GeometryEffect DeepCopy()
{
return new NoGeometryEffect();
}
public override bool Equals(GeometryEffect geometryEffect)
{
if (geometryEffect != null) return geometryEffect is NoGeometryEffect;
return true;
}
protected override bool UpdateCachedGeometry(Geometry input)
{
CachedGeometry = input;
return false;
}
}
}
}
@@ -0,0 +1,59 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
namespace HandyControl.Expression.Media
{
public sealed class GeometryEffectConverter : TypeConverter
{
private static readonly Dictionary<string, GeometryEffect> RegisteredEffects;
static GeometryEffectConverter()
{
var dictionary = new Dictionary<string, GeometryEffect>
{
{
"None",
GeometryEffect.DefaultGeometryEffect
},
{
"Sketch",
new SketchGeometryEffect()
}
};
RegisteredEffects = dictionary;
}
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return typeof(string).IsAssignableFrom(sourceType);
}
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
return typeof(string).IsAssignableFrom(destinationType);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string key && RegisteredEffects.TryGetValue(key, out var effect))
return effect.CloneCurrentValue();
return null;
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value,
Type destinationType)
{
if (typeof(string).IsAssignableFrom(destinationType))
{
if (value is string) return value;
foreach (var pair in RegisteredEffects)
if (pair.Value?.Equals(value as GeometryEffect) ?? value == null)
return pair.Key;
}
return null;
}
}
}
@@ -0,0 +1,88 @@
using System.Windows;
using System.Windows.Media;
using HandyControl.Expression.Drawing;
namespace HandyControl.Expression.Media
{
public abstract class GeometrySource<TParameters> : IGeometrySource
where TParameters : IGeometrySourceParameters
{
private bool _geometryInvalidated;
protected Geometry CachedGeometry;
public bool InvalidateGeometry(InvalidateGeometryReasons reasons)
{
if ((reasons & InvalidateGeometryReasons.TemplateChanged) != 0) CachedGeometry = null;
if (!_geometryInvalidated)
{
_geometryInvalidated = true;
return true;
}
return false;
}
public bool UpdateGeometry(IGeometrySourceParameters parameters, Rect layoutBounds)
{
var force = false;
if (parameters is TParameters parameters1)
{
var rect = ComputeLogicalBounds(layoutBounds, parameters1);
force |= LayoutBounds != layoutBounds || LogicalBounds != rect;
if (_geometryInvalidated || force)
{
LayoutBounds = layoutBounds;
LogicalBounds = rect;
force |= UpdateCachedGeometry(parameters1);
force |= ApplyGeometryEffect(parameters1, force);
}
}
_geometryInvalidated = false;
return force;
}
public Geometry Geometry { get; private set; }
public Rect LayoutBounds { get; private set; }
public Rect LogicalBounds { get; private set; }
private bool ApplyGeometryEffect(IGeometrySourceParameters parameters, bool force)
{
var flag = false;
var cachedGeometry = CachedGeometry;
var geometryEffect = parameters.GetGeometryEffect();
if (geometryEffect != null)
{
if (force)
{
flag = true;
geometryEffect.InvalidateGeometry(InvalidateGeometryReasons.ParentInvalidated);
}
if (geometryEffect.ProcessGeometry(CachedGeometry))
{
flag = true;
cachedGeometry = geometryEffect.OutputGeometry;
}
}
if (!Equals(Geometry, cachedGeometry))
{
flag = true;
Geometry = cachedGeometry;
}
return flag;
}
protected virtual Rect ComputeLogicalBounds(Rect layoutBounds, IGeometrySourceParameters parameters)
{
return GeometryHelper.Inflate(layoutBounds, -parameters.GetHalfStrokeThickness());
}
protected abstract bool UpdateCachedGeometry(TParameters parameters);
}
}
@@ -0,0 +1,13 @@
namespace HandyControl.Expression.Media
{
public interface IArcGeometrySourceParameters : IGeometrySourceParameters
{
double ArcThickness { get; }
UnitType ArcThicknessUnit { get; }
double EndAngle { get; }
double StartAngle { get; }
}
}
@@ -0,0 +1,17 @@
using System.Windows;
namespace HandyControl.Expression.Media
{
public interface IGeometrySource
{
bool InvalidateGeometry(InvalidateGeometryReasons reasons);
bool UpdateGeometry(IGeometrySourceParameters parameters, Rect layoutBounds);
System.Windows.Media.Geometry Geometry { get; }
Rect LayoutBounds { get; }
Rect LogicalBounds { get; }
}
}
@@ -0,0 +1,31 @@
using System;
using System.Windows;
namespace HandyControl.Expression.Media
{
internal static class GeometrySourceExtensions
{
public static GeometryEffect GetGeometryEffect(this IGeometrySourceParameters parameters)
{
if (parameters is DependencyObject obj2)
{
var geometryEffect = GeometryEffect.GetGeometryEffect(obj2);
if (geometryEffect != null && obj2.Equals(geometryEffect.Parent)) return geometryEffect;
}
return null;
}
public static double GetHalfStrokeThickness(this IGeometrySourceParameters parameter)
{
if (parameter.Stroke != null)
{
var strokeThickness = parameter.StrokeThickness;
if (!double.IsNaN(strokeThickness) && !double.IsInfinity(strokeThickness))
return Math.Abs(strokeThickness) / 2.0;
}
return 0.0;
}
}
}
@@ -0,0 +1,13 @@
using System.Windows.Media;
namespace HandyControl.Expression.Media
{
public interface IGeometrySourceParameters
{
Stretch Stretch { get; }
Brush Stroke { get; }
double StrokeThickness { get; }
}
}
+25
View File
@@ -0,0 +1,25 @@
using System;
using System.Windows;
using System.Windows.Media;
namespace HandyControl.Expression.Media
{
public interface IShape
{
event EventHandler RenderedGeometryChanged;
void InvalidateGeometry(InvalidateGeometryReasons reasons);
Brush Fill { get; set; }
Thickness GeometryMargin { get; }
Geometry RenderedGeometry { get; }
Stretch Stretch { get; set; }
Brush Stroke { get; set; }
double StrokeThickness { get; set; }
}
}
@@ -0,0 +1,14 @@
using System;
namespace HandyControl.Expression.Media
{
[Flags]
public enum InvalidateGeometryReasons
{
ChildInvalidated = 4,
IsAnimated = 2,
ParentInvalidated = 8,
PropertyChanged = 1,
TemplateChanged = 0x10
}
}
@@ -0,0 +1,135 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Windows;
using System.Windows.Media;
using HandyControl.Expression.Drawing;
namespace HandyControl.Expression.Media
{
public sealed class SketchGeometryEffect : GeometryEffect
{
private readonly long _randomSeed = DateTime.Now.Ticks;
protected override GeometryEffect DeepCopy()
{
return new SketchGeometryEffect();
}
private static void DisturbPoints(RandomEngine random, double scale, IList<Point> points,
IList<Vector> normals)
{
var count = points.Count;
for (var i = 1; i < count; i++)
{
var num3 = random.NextGaussian(0.0, 1.0 * scale);
var num4 = random.NextUniform(-0.5, 0.5) * scale;
var point = points[i];
var vector = normals[i];
var vector2 = normals[i];
var point2 = points[i];
var vector3 = normals[i];
var vector4 = normals[i];
points[i] = new Point(point.X + vector.X * num4 - vector2.Y * num3,
point2.Y + vector3.X * num3 + vector4.Y * num4);
}
}
public override bool Equals(GeometryEffect geometryEffect)
{
return geometryEffect is SketchGeometryEffect;
}
private IEnumerable<SimpleSegment> GetEffectiveSegments(PathFigure pathFigure)
{
var startPoint = pathFigure.StartPoint;
foreach (var iteratorVariable1 in pathFigure.AllSegments())
foreach (var iteratorVariable2 in iteratorVariable1.PathSegment.GetSimpleSegments(
iteratorVariable1.StartPoint))
{
yield return iteratorVariable2;
startPoint = iteratorVariable2.Points.Last();
}
if (pathFigure.IsClosed) yield return SimpleSegment.Create(startPoint, pathFigure.StartPoint);
}
[SuppressMessage("ReSharper", "ConditionIsAlwaysTrueOrFalse")]
protected override bool UpdateCachedGeometry(Geometry input)
{
var flag = false;
var inputPath = input.AsPathGeometry();
if (inputPath != null) return flag | UpdateSketchGeometry(inputPath);
CachedGeometry = input;
return flag;
}
private bool UpdateSketchGeometry(PathGeometry inputPath)
{
var flag = false;
flag |= GeometryHelper.EnsureGeometryType(out var geometry, ref CachedGeometry,
() => new PathGeometry());
flag |= geometry.Figures.EnsureListCount(inputPath.Figures.Count, () => new PathFigure());
var random = new RandomEngine(_randomSeed);
for (var i = 0; i < inputPath.Figures.Count; i++)
{
var pathFigure = inputPath.Figures[i];
var isClosed = pathFigure.IsClosed;
var isFilled = pathFigure.IsFilled;
if (pathFigure.Segments.Count == 0)
{
flag |= geometry.Figures[i]
.SetIfDifferent(PathFigure.StartPointProperty, pathFigure.StartPoint);
flag |= geometry.Figures[i].Segments.EnsureListCount(0);
}
else
{
var list = new List<Point>(pathFigure.Segments.Count * 3);
foreach (var segment in GetEffectiveSegments(pathFigure))
{
var resultPolyline = new List<Point>
{
segment.Points[0]
};
segment.Flatten(resultPolyline, 0.0, null);
var polyline = new PolylineData(resultPolyline);
if (resultPolyline.Count > 1 && polyline.TotalLength > 4.0)
{
var a = polyline.TotalLength / 8.0;
var sampleCount = (int) Math.Max(2.0, Math.Ceiling(a));
var interval = polyline.TotalLength / sampleCount;
var scale = interval / 8.0;
var samplePoints = new List<Point>(sampleCount);
var sampleNormals = new List<Vector>(sampleCount);
var sampleIndex = 0;
PolylineHelper.PathMarch(polyline, 0.0, 0.0, delegate(MarchLocation location)
{
if (location.Reason == MarchStopReason.CompletePolyline) return double.NaN;
if (location.Reason != MarchStopReason.CompleteStep) return location.Remain;
if (sampleIndex++ == sampleCount) return double.NaN;
samplePoints.Add(location.GetPoint(polyline.Points));
sampleNormals.Add(location.GetNormal(polyline));
return interval;
});
DisturbPoints(random, scale, samplePoints, sampleNormals);
list.AddRange(samplePoints);
}
else
{
list.AddRange(resultPolyline);
list.RemoveLast();
}
}
if (!isClosed) list.Add(pathFigure.Segments.Last().GetLastPoint());
flag |= PathFigureHelper.SyncPolylineFigure(geometry.Figures[i], list, isClosed,
isFilled);
}
}
if (flag) CachedGeometry = PathGeometryHelper.FixPathGeometryBoundary(CachedGeometry);
return flag;
}
}
}
@@ -0,0 +1,8 @@
namespace HandyControl.Expression.Media
{
public enum UnitType
{
Pixel,
Percent
}
}
+64
View File
@@ -0,0 +1,64 @@
using System.Windows;
using System.Windows.Media;
using HandyControl.Expression.Media;
namespace HandyControl.Expression.Shapes
{
public sealed class Arc : PrimitiveShape, IArcGeometrySourceParameters
{
public static readonly DependencyProperty ArcThicknessProperty =
DependencyProperty.Register("ArcThickness", typeof(double), typeof(Arc),
new DrawingPropertyMetadata(0.0, DrawingPropertyMetadataOptions.AffectsRender));
public static readonly DependencyProperty ArcThicknessUnitProperty =
DependencyProperty.Register("ArcThicknessUnit", typeof(UnitType), typeof(Arc),
new DrawingPropertyMetadata(UnitType.Pixel, DrawingPropertyMetadataOptions.AffectsRender));
public static readonly DependencyProperty EndAngleProperty = DependencyProperty.Register("EndAngle",
typeof(double), typeof(Arc),
new DrawingPropertyMetadata(90.0, DrawingPropertyMetadataOptions.AffectsRender));
public static readonly DependencyProperty StartAngleProperty =
DependencyProperty.Register("StartAngle", typeof(double), typeof(Arc),
new DrawingPropertyMetadata(0.0, DrawingPropertyMetadataOptions.AffectsRender));
public double ArcThickness
{
get =>
(double) GetValue(ArcThicknessProperty);
set => SetValue(ArcThicknessProperty, value);
}
public UnitType ArcThicknessUnit
{
get =>
(UnitType) GetValue(ArcThicknessUnitProperty);
set => SetValue(ArcThicknessUnitProperty, value);
}
public double EndAngle
{
get =>
(double) GetValue(EndAngleProperty);
set => SetValue(EndAngleProperty, value);
}
Stretch IGeometrySourceParameters.Stretch => Stretch;
Brush IGeometrySourceParameters.Stroke => Stroke;
double IGeometrySourceParameters.StrokeThickness => StrokeThickness;
public double StartAngle
{
get =>
(double) GetValue(StartAngleProperty);
set => SetValue(StartAngleProperty, value);
}
protected override IGeometrySource CreateGeometrySource()
{
return new ArcGeometrySource();
}
}
}
@@ -0,0 +1,79 @@
using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Shapes;
using HandyControl.Expression.Drawing;
using HandyControl.Expression.Media;
namespace HandyControl.Expression.Shapes
{
public abstract class PrimitiveShape : Shape, IGeometrySourceParameters, IShape
{
private IGeometrySource _geometrySource;
static PrimitiveShape()
{
StretchProperty.OverrideMetadata(typeof(PrimitiveShape),
new DrawingPropertyMetadata(Stretch.Fill, DrawingPropertyMetadataOptions.AffectsRender));
StrokeThicknessProperty.OverrideMetadata(typeof(PrimitiveShape),
new DrawingPropertyMetadata(1.0, DrawingPropertyMetadataOptions.AffectsRender));
}
protected sealed override Geometry DefiningGeometry =>
GeometrySource.Geometry ?? Geometry.Empty;
private IGeometrySource GeometrySource => _geometrySource ?? (_geometrySource = CreateGeometrySource());
Stretch IGeometrySourceParameters.Stretch => Stretch;
Brush IGeometrySourceParameters.Stroke => Stroke;
double IGeometrySourceParameters.StrokeThickness => StrokeThickness;
public event EventHandler RenderedGeometryChanged;
public void InvalidateGeometry(InvalidateGeometryReasons reasons)
{
if (GeometrySource.InvalidateGeometry(reasons)) InvalidateArrange();
}
public Thickness GeometryMargin => GeometrySource.LogicalBounds.Subtract(RenderedGeometry.Bounds);
Brush IShape.Fill
{
get => Fill;
set => Fill = value;
}
Stretch IShape.Stretch
{
get => Stretch;
set => Stretch = value;
}
Brush IShape.Stroke
{
get => Stroke;
set => Stroke = value;
}
double IShape.StrokeThickness
{
get => StrokeThickness;
set => StrokeThickness = value;
}
protected override Size ArrangeOverride(Size finalSize)
{
if (GeometrySource.UpdateGeometry(this, finalSize.Bounds())) RealizeGeometry();
base.ArrangeOverride(finalSize);
return finalSize;
}
protected abstract IGeometrySource CreateGeometrySource();
protected override Size MeasureOverride(Size availableSize) => new Size(base.StrokeThickness, base.StrokeThickness);
private void RealizeGeometry() => RenderedGeometryChanged?.Invoke(this, EventArgs.Empty);
}
}
+34 -1
View File
@@ -47,6 +47,7 @@
<Compile Include="Controls\Button\ContextMenuToggleButton.cs" />
<Compile Include="Controls\Carousel.cs" />
<Compile Include="Controls\CirclePanel.cs" />
<Compile Include="Controls\CircleProgressBar.cs" />
<Compile Include="Controls\Clock\Clock.cs" />
<Compile Include="Controls\Clock\ClockRadioButton.cs" />
<Compile Include="Controls\Growl.cs" />
@@ -61,7 +62,7 @@
<Compile Include="Controls\Loading\LoadingBase.cs" />
<Compile Include="Controls\Loading\LoadingCircle.cs" />
<Compile Include="Controls\Loading\LoadingLine.cs" />
<Compile Include="Controls\Slider\SliderCanvas.cs" />
<Compile Include="Controls\InternalCanvas.cs" />
<Compile Include="Data\Enum\InfoType.cs" />
<Compile Include="Data\Enum\SkinType.cs" />
<Compile Include="Data\Enum\TipPlacement.cs" />
@@ -71,6 +72,38 @@
<DesignTime>True</DesignTime>
<DependentUpon>ResourceToken.tt</DependentUpon>
</Compile>
<Compile Include="Expression\Drawing\BezierCurveFlattener.cs" />
<Compile Include="Expression\Drawing\CommonExtensions.cs" />
<Compile Include="Expression\Drawing\ExceptionStringTable.cs" />
<Compile Include="Expression\Drawing\GeometryHelper.cs" />
<Compile Include="Expression\Drawing\MarchLocation.cs" />
<Compile Include="Expression\Drawing\MarchStopReason.cs" />
<Compile Include="Expression\Drawing\MathHelper.cs" />
<Compile Include="Expression\Drawing\PathFigureHelper.cs" />
<Compile Include="Expression\Drawing\PathGeometryHelper.cs" />
<Compile Include="Expression\Drawing\PathSegmentData.cs" />
<Compile Include="Expression\Drawing\PathSegmentHelper.cs" />
<Compile Include="Expression\Drawing\PolylineData.cs" />
<Compile Include="Expression\Drawing\PolylineHelper.cs" />
<Compile Include="Expression\Drawing\RandomEngine.cs" />
<Compile Include="Expression\Drawing\SimpleSegment.cs" />
<Compile Include="Expression\Media\ArcGeometrySource.cs" />
<Compile Include="Expression\Media\DrawingPropertyChangedEventArgs.cs" />
<Compile Include="Expression\Media\DrawingPropertyMetadata.cs" />
<Compile Include="Expression\Media\DrawingPropertyMetadataOptions.cs" />
<Compile Include="Expression\Media\GeometryEffect.cs" />
<Compile Include="Expression\Media\GeometryEffectConverter.cs" />
<Compile Include="Expression\Media\GeometrySource!1.cs" />
<Compile Include="Expression\Media\IArcGeometrySourceParameters.cs" />
<Compile Include="Expression\Media\IGeometrySource.cs" />
<Compile Include="Expression\Media\IGeometrySourceExtensions.cs" />
<Compile Include="Expression\Media\IGeometrySourceParameters.cs" />
<Compile Include="Expression\Media\InvalidateGeometryReasons.cs" />
<Compile Include="Expression\Media\IShape.cs" />
<Compile Include="Expression\Media\SketchGeometryEffect.cs" />
<Compile Include="Expression\Media\UnitType.cs" />
<Compile Include="Expression\Shapes\Arc.cs" />
<Compile Include="Expression\Shapes\PrimitiveShape.cs" />
<Compile Include="Tools\SingleOpenHelper.cs" />
<Compile Include="Controls\Steps\StepBar.cs" />
<Compile Include="Controls\Steps\StepItem.cs" />
@@ -1,6 +1,11 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:HandyControl.Controls">
xmlns:controls="clr-namespace:HandyControl.Controls"
xmlns:shapes="clr-namespace:HandyControl.Expression.Shapes">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../../Basic/Converters.xaml"/>
</ResourceDictionary.MergedDictionaries>
<LinearGradientBrush x:Key="ProgressBarIndicatorAnimatedFill" StartPoint="0,0" EndPoint="1,0">
<GradientStop Color="#00FFFFFF" Offset="0"/>
@@ -18,18 +23,16 @@
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ProgressBar">
<Grid>
<Border x:Name="TemplateRoot" CornerRadius="{Binding Path=(controls:BorderElement.CornerRadius),RelativeSource={RelativeSource TemplatedParent}}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}"/>
<Grid Margin="{TemplateBinding BorderThickness}">
<Rectangle x:Name="PART_Track"/>
<Grid HorizontalAlignment="Left">
<Decorator x:Name="PART_Indicator" HorizontalAlignment="Left">
<Border CornerRadius="{Binding Path=(controls:BorderElement.CornerRadius),RelativeSource={RelativeSource TemplatedParent}}" x:Name="Animation" ClipToBounds="true" Background="{TemplateBinding Foreground}">
<Rectangle x:Name="PART_GlowRect" Fill="{StaticResource ProgressBarIndicatorAnimatedFill}" HorizontalAlignment="Left" Margin="-100,0,0,0" Width="100"/>
</Border>
</Decorator>
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="{DynamicResource TextIconBrush}" Text="{Binding Value,RelativeSource={RelativeSource TemplatedParent},StringFormat={}{0} %}"/>
</Grid>
<Grid x:Name="TemplateRoot">
<Border CornerRadius="{Binding Path=(controls:BorderElement.CornerRadius),RelativeSource={RelativeSource TemplatedParent}}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}"/>
<Rectangle x:Name="PART_Track"/>
<Grid HorizontalAlignment="Left">
<Decorator x:Name="PART_Indicator" HorizontalAlignment="Left">
<Border CornerRadius="{Binding Path=(controls:BorderElement.CornerRadius),RelativeSource={RelativeSource TemplatedParent}}" x:Name="Animation" ClipToBounds="true" Background="{TemplateBinding Foreground}">
<Rectangle x:Name="PART_GlowRect" Fill="{StaticResource ProgressBarIndicatorAnimatedFill}" HorizontalAlignment="Left" Margin="-100,0,0,0" Width="100"/>
</Border>
</Decorator>
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="{DynamicResource TextIconBrush}" Text="{Binding Value,RelativeSource={RelativeSource TemplatedParent},StringFormat={}{0} %}"/>
</Grid>
</Grid>
</ControlTemplate>
@@ -51,11 +54,9 @@
</ControlTemplate.Resources>
<Grid x:Name="TemplateRoot" SnapsToDevicePixels="true">
<Border CornerRadius="{Binding Path=(controls:BorderElement.CornerRadius),RelativeSource={RelativeSource TemplatedParent}}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}"/>
<Rectangle x:Name="PART_Track" Margin="1"/>
<Rectangle x:Name="PART_Track"/>
<Decorator x:Name="PART_Indicator" HorizontalAlignment="Left" Margin="1">
<Grid x:Name="Foreground">
<Border x:Name="Indicator" CornerRadius="{Binding Path=(controls:BorderElement.CornerRadius),RelativeSource={RelativeSource TemplatedParent}}" Background="{TemplateBinding Foreground}"/>
</Grid>
<Border x:Name="Indicator" CornerRadius="{Binding Path=(controls:BorderElement.CornerRadius),RelativeSource={RelativeSource TemplatedParent}}" Background="{TemplateBinding Foreground}"/>
</Decorator>
</Grid>
<ControlTemplate.Triggers>
@@ -68,4 +69,24 @@
</Setter>
</Style>
<Style x:Key="ProgressBarCircleBaseStyle" TargetType="controls:CircleProgressBar">
<Setter Property="controls:BorderElement.CornerRadius" Value="4"/>
<Setter Property="Background" Value="{DynamicResource SecondaryRegionBrush}"/>
<Setter Property="BorderBrush" Value="{DynamicResource BorderBrush}"/>
<Setter Property="Height" Value="100"/>
<Setter Property="Width" Value="100"/>
<Setter Property="ArcThickness" Value="4"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="controls:CircleProgressBar">
<Grid x:Name="TemplateRoot">
<shapes:Arc ArcThickness="{TemplateBinding ArcThickness}" Stretch="None" EndAngle="360" Width="{TemplateBinding ActualWidth}" Height="{TemplateBinding ActualHeight}" Stroke="{TemplateBinding BorderBrush}" Fill="{TemplateBinding Background}" StrokeThickness="0"/>
<shapes:Arc ArcThickness="{TemplateBinding ArcThickness}" Stretch="None" Width="{TemplateBinding ActualWidth}" StrokeThickness="0" Height="{TemplateBinding ActualHeight}" x:Name="PART_Indicator" Fill="{TemplateBinding Foreground}"/>
<TextBlock Visibility="{Binding ShowText,RelativeSource={RelativeSource TemplatedParent},Converter={StaticResource Boolean2VisibilityConverter}}" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="{DynamicResource PrimaruTextBrush}" Text="{Binding Value,RelativeSource={RelativeSource TemplatedParent},StringFormat={}{0} %}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
+22 -1
View File
@@ -1,5 +1,6 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:HandyControl.Controls">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Base/ProgressBarBaseStyle.xaml"/>
@@ -175,6 +176,26 @@
</Setter>
</Style>
<Style BasedOn="{StaticResource ProgressBarCircleBaseStyle}" TargetType="controls:CircleProgressBar">
<Setter Property="Foreground" Value="{DynamicResource PrimaryBrush}"/>
</Style>
<Style x:Key="ProgressBarSuccessCircle" BasedOn="{StaticResource ProgressBarCircleBaseStyle}" TargetType="controls:CircleProgressBar">
<Setter Property="Foreground" Value="{DynamicResource SuccessBrush}"/>
</Style>
<Style x:Key="ProgressBarInfoCircle" BasedOn="{StaticResource ProgressBarCircleBaseStyle}" TargetType="controls:CircleProgressBar">
<Setter Property="Foreground" Value="{DynamicResource InfoBrush}"/>
</Style>
<Style x:Key="ProgressBarWarningCircle" BasedOn="{StaticResource ProgressBarCircleBaseStyle}" TargetType="controls:CircleProgressBar">
<Setter Property="Foreground" Value="{DynamicResource WarningBrush}"/>
</Style>
<Style x:Key="ProgressBarDangerCircle" BasedOn="{StaticResource ProgressBarCircleBaseStyle}" TargetType="controls:CircleProgressBar">
<Setter Property="Foreground" Value="{DynamicResource DangerBrush}"/>
</Style>
<Style x:Key="ProgressBarFlat" TargetType="ProgressBar">
<Setter Property="Height" Value="4"/>
<Setter Property="Background" Value="{DynamicResource SecondaryRegionBrush}"/>
+8 -8
View File
@@ -503,10 +503,10 @@
</ControlTemplate>
<ControlTemplate x:Key="TipSliderThumbHorizontalTop" TargetType="Thumb">
<controls:SliderCanvas>
<controls:InternalCanvas>
<Label Name="LabelValue" Opacity="0" Canvas.Bottom="28" Style="{StaticResource LabelPrimary}" Content="{Binding Value,RelativeSource={RelativeSource FindAncestor,AncestorType=Slider}}"/>
<Border Canvas.Bottom="0" Effect="{StaticResource EffectShadow1}" Name="BorderDot" BorderThickness="0" BorderBrush="{DynamicResource PrimaryBrush}" Width="18" Height="18" Background="White" CornerRadius="9"/>
</controls:SliderCanvas>
</controls:InternalCanvas>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Effect" Value="{StaticResource EffectShadow2}" TargetName="BorderDot"/>
@@ -534,10 +534,10 @@
</ControlTemplate>
<ControlTemplate x:Key="TipSliderThumbHorizontalBottom" TargetType="Thumb">
<controls:SliderCanvas>
<controls:InternalCanvas>
<Label Name="LabelValue" Opacity="0" Canvas.Top="28" Style="{StaticResource LabelPrimary}" Content="{Binding Value,RelativeSource={RelativeSource FindAncestor,AncestorType=Slider}}"/>
<Border Canvas.Bottom="0" Effect="{StaticResource EffectShadow1}" Name="BorderDot" BorderThickness="0" BorderBrush="{DynamicResource PrimaryBrush}" Width="18" Height="18" Background="White" CornerRadius="9"/>
</controls:SliderCanvas>
</controls:InternalCanvas>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Effect" Value="{StaticResource EffectShadow2}" TargetName="BorderDot"/>
@@ -565,10 +565,10 @@
</ControlTemplate>
<ControlTemplate x:Key="TipSliderThumbVerticalLeft" TargetType="Thumb">
<controls:SliderCanvas Orientation="Vertical">
<controls:InternalCanvas Orientation="Vertical">
<Label Name="LabelValue" Opacity="0" Canvas.Right="28" Style="{StaticResource LabelPrimary}" Content="{Binding Value,RelativeSource={RelativeSource FindAncestor,AncestorType=Slider}}"/>
<Border Canvas.Right="0" Effect="{StaticResource EffectShadow1}" Name="BorderDot" BorderThickness="0" BorderBrush="{DynamicResource PrimaryBrush}" Width="18" Height="18" Background="White" CornerRadius="9"/>
</controls:SliderCanvas>
</controls:InternalCanvas>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Effect" Value="{StaticResource EffectShadow2}" TargetName="BorderDot"/>
@@ -596,10 +596,10 @@
</ControlTemplate>
<ControlTemplate x:Key="TipSliderThumbVerticalRight" TargetType="Thumb">
<controls:SliderCanvas Orientation="Vertical">
<controls:InternalCanvas Orientation="Vertical">
<Label Name="LabelValue" Opacity="0" Canvas.Left="28" Style="{StaticResource LabelPrimary}" Content="{Binding Value,RelativeSource={RelativeSource FindAncestor,AncestorType=Slider}}"/>
<Border Canvas.Right="0" Effect="{StaticResource EffectShadow1}" Name="BorderDot" BorderThickness="0" BorderBrush="{DynamicResource PrimaryBrush}" Width="18" Height="18" Background="White" CornerRadius="9"/>
</controls:SliderCanvas>
</controls:InternalCanvas>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Effect" Value="{StaticResource EffectShadow2}" TargetName="BorderDot"/>
+2
View File
@@ -36,6 +36,8 @@
public static readonly string PreviewSliderDemoCtl = nameof(PreviewSliderDemoCtl);
public static readonly string CircleProgressBarDemoCtl = nameof(CircleProgressBarDemoCtl);
public static readonly string ButtonDemoCtl = nameof(ButtonDemoCtl);
public static readonly string ToggleButtonDemoCtl = nameof(ToggleButtonDemoCtl);
+2 -1
View File
@@ -20,7 +20,8 @@ var controlList = new List<string>
"NumericUpDownDemoCtl",
"WindowDemoCtl",
"ScrollViewerDemoCtl",
"PreviewSliderDemoCtl"
"PreviewSliderDemoCtl",
"CircleProgressBarDemoCtl"
};
var styleList = new List<string>
{
+8
View File
@@ -104,6 +104,9 @@
<Compile Include="UserControl\Controls\CirclePanelDemoCtl.xaml.cs">
<DependentUpon>CirclePanelDemoCtl.xaml</DependentUpon>
</Compile>
<Compile Include="UserControl\Controls\CircleProgressBarDemoCtl.xaml.cs">
<DependentUpon>CircleProgressBarDemoCtl.xaml</DependentUpon>
</Compile>
<Compile Include="UserControl\Controls\ClockDemoCtl.xaml.cs">
<DependentUpon>ClockDemoCtl.xaml</DependentUpon>
</Compile>
@@ -309,6 +312,10 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="UserControl\Controls\CircleProgressBarDemoCtl.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="UserControl\Controls\ClockDemoCtl.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
@@ -505,6 +512,7 @@
<Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>MessageToken.cs</LastGenOutput>
</Content>
<Resource Include="Resources\Img\LeftMainContent\ColorWheel_16x.png" />
<Resource Include="Resources\Img\LeftMainContent\Slider_16x.png" />
<Resource Include="Resources\Img\LeftMainContent\HoverMenu_16x.png" />
<Resource Include="Resources\Img\icon.ico" />
+9
View File
@@ -195,6 +195,15 @@ namespace HandyControlDemo.Properties.Langs {
}
}
/// <summary>
/// 查找类似 圆形进度条 的本地化字符串。
/// </summary>
public static string CircleProgressBar {
get {
return ResourceManager.GetString("CircleProgressBar", resourceCulture);
}
}
/// <summary>
/// 查找类似 清空 的本地化字符串。
/// </summary>
@@ -387,4 +387,7 @@
<data name="Slider" xml:space="preserve">
<value>Slider</value>
</data>
<data name="CircleProgressBar" xml:space="preserve">
<value>CircleProgressBar</value>
</data>
</root>
@@ -367,4 +367,7 @@
<data name="Slider" xml:space="preserve">
<value>滑块</value>
</data>
<data name="CircleProgressBar" xml:space="preserve">
<value>圆形进度条</value>
</data>
</root>
Binary file not shown.

After

Width:  |  Height:  |  Size: 553 B

@@ -0,0 +1,16 @@
<UserControl x:Class="HandyControlDemo.UserControl.CircleProgressBarDemoCtl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:HandyControl.Controls;assembly=HandyControl"
Background="{DynamicResource RegionBrush}">
<controls:TransitioningContentControl>
<StackPanel Orientation="Horizontal" Margin="32">
<controls:CircleProgressBar Value="50"/>
<controls:CircleProgressBar Value="80" FontSize="30" Margin="16,0,0,0"/>
<controls:CircleProgressBar Value="10" Margin="16,0,0,0" ShowText="False" Width="20" Height="20" ArcThickness="2" Style="{StaticResource ProgressBarSuccessCircle}"/>
<controls:CircleProgressBar Value="20" Margin="16,0,0,0" ShowText="False" Width="30" Height="30" ArcThickness="6" Style="{StaticResource ProgressBarInfoCircle}"/>
<controls:CircleProgressBar Value="40" Margin="16,0,0,0" ShowText="False" Width="40" Height="40" ArcThickness="10" Style="{StaticResource ProgressBarWarningCircle}"/>
<controls:CircleProgressBar Value="80" Margin="16,0,0,0" Width="50" Height="50" Style="{StaticResource ProgressBarDangerCircle}"/>
</StackPanel>
</controls:TransitioningContentControl>
</UserControl>
@@ -0,0 +1,11 @@
// ReSharper disable once CheckNamespace
namespace HandyControlDemo.UserControl
{
public partial class CircleProgressBarDemoCtl
{
public CircleProgressBarDemoCtl()
{
InitializeComponent();
}
}
}
@@ -130,6 +130,11 @@
</controls:EdgeElement.LeftContent>
</ListBoxItem>
<ListBoxItem Tag="{x:Static data:MessageToken.CirclePanelDemoCtl}" Content="{x:Static langs:Lang.CirclePanel}">
<controls:EdgeElement.LeftContent>
<Image Source="../../Resources/Img/LeftMainContent/ColorWheel_16x.png"/>
</controls:EdgeElement.LeftContent>
</ListBoxItem>
<ListBoxItem Style="{StaticResource ListBoxItemNew}" Tag="{x:Static data:MessageToken.CircleProgressBarDemoCtl}" Content="{x:Static langs:Lang.CircleProgressBar}">
<controls:EdgeElement.LeftContent>
<Image Source="../../Resources/Img/LeftMainContent/ShapeArcRing_16x.png"/>
</controls:EdgeElement.LeftContent>
@@ -179,7 +184,7 @@
<Image Source="../../Resources/Img/LeftMainContent/SplitterControl_16x.png"/>
</controls:EdgeElement.LeftContent>
</ListBoxItem>
<ListBoxItem Style="{StaticResource ListBoxItemNew}" Tag="{x:Static data:MessageToken.PreviewSliderDemoCtl}" Content="{x:Static langs:Lang.PreviewSlider}">
<ListBoxItem Tag="{x:Static data:MessageToken.PreviewSliderDemoCtl}" Content="{x:Static langs:Lang.PreviewSlider}">
<controls:EdgeElement.LeftContent>
<Image Source="../../Resources/Img/LeftMainContent/HoverMenu_16x.png"/>
</controls:EdgeElement.LeftContent>