Skip to content

Commit

Permalink
feat: 添加贝塞尔曲线 Ease
Browse files Browse the repository at this point in the history
  • Loading branch information
SlimeNull committed May 22, 2024
1 parent d4b7ddb commit 097e27f
Show file tree
Hide file tree
Showing 3 changed files with 215 additions and 0 deletions.
12 changes: 12 additions & 0 deletions EleCho.WpfSuite/Animations/BezierEaseBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.Windows.Media.Animation;

namespace EleCho.WpfSuite
{
public abstract class BezierEaseBase : IEasingFunction
{
protected abstract double GetSampleRate(double time);
protected abstract double GetSampleValue(double rate);

public double Ease(double normalizedTime) => GetSampleValue(GetSampleRate(normalizedTime));
}
}
139 changes: 139 additions & 0 deletions EleCho.WpfSuite/Animations/CubicBezierEase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
using System;
using System.Windows.Media.Animation;

namespace EleCho.WpfSuite
{

public class CubicBezierEase : BezierEaseBase
{
private double x1;
private double y1;
private double x2;
private double y2;

/// <summary>
/// X value of control point 1
/// </summary>
public double X1
{
get => x1; set
{
if (value < 0 || value > 1)
throw new ArgumentOutOfRangeException();

x1 = value;
}
}

/// <summary>
/// Y value of control point 1
/// </summary>
public double Y1 { get => y1; set => y1 = value; }

/// <summary>
/// X value of control point 2
/// </summary>
public double X2
{
get => x2; set
{
if (value < 0 || value > 1)
throw new ArgumentOutOfRangeException();

x2 = value;
}
}

/// <summary>
/// Y value of control point 2
/// </summary>
public double Y2 { get => y2; set => y2 = value; }

private double MathCbrt(double num)
{
return num < 0 ? -Math.Pow(-num, 1d / 3) : Math.Pow(num, 1d / 3);
}
private double PickAppropriateRate(double cp1, double cp2, double p, params double[] rates)
{
double result = double.NaN;
double diffNow = double.MaxValue;
foreach (var rate in rates)
if (!double.IsNaN(rate) && Math.Abs(GetSamplePoint(cp1, cp2, rate) - p) < diffNow)
result = rate;
return result;
}
private double GetSampleRate(double cp1, double cp2, double p)
{
// 警告, 这里是魔法, 是求根公式

double
a = 3 * cp1 - 3 * cp2 + 1,
b = -6 * cp1 + 3 * cp2,
c = 3 * cp1,
d = -p;
double
A = b * b - 3 * a * c,
B = b * c - 9 * a * d,
C = c * c - 3 * b * d;
double
delta = B * B - 4 * A * C;
if (A == B)
{
double x = -c / b; // -b/3a -c/b -3d/c
double rst = x;
return rst;
}
else if (delta > 0)
{
//double I = double.NaN;
double
y1 = A * b + 3 * a * ((-B + Math.Sqrt(delta)) / 2),
y2 = A * b + 3 * a * ((-B - Math.Sqrt(delta)) / 2);
double
xtmp1 = MathCbrt(y1) + MathCbrt(y2); //,
//xtmp2 = Cubic(y1) - Cubic(y2);
double // what the fuck is I? virtual number wtf... impossible for now (((
x1 = (-b - xtmp1) / (3 * a); //,
//x2 = (-2 * b + xtmp1 + Math.Sqrt(3) * xtmp2 * I) / (6 * a),
//x3 = (-2 * b + xtmp1 - Math.Sqrt(3) * xtmp2 * I) / (6 * a);
double rst = x1;
return rst;
}
else if (delta == 0)
{
double k = B / A;
double
x1 = -b / a + k,
x2 = -k / 2;
double rst = PickAppropriateRate(cp1, cp2, p, x1, x2);
return rst;
}
else // delta < 0
{
double
t = (2 * A * b - 3 * a * B) / (2 * A * Math.Sqrt(A)),
sita = Math.Acos(t);
double
x1 = (-b - 2 * Math.Sqrt(A) * Math.Cos(sita / 3)) / (3 * a),
x2 = (-b + Math.Sqrt(A) * (Math.Cos(sita / 3) + Math.Sqrt(3) * Math.Sin(sita / 3))) / (3 * a),
x3 = (-b + Math.Sqrt(A) * (Math.Cos(sita / 3) - Math.Sqrt(3) * Math.Sin(sita / 3))) / (3 * a);
double rst = PickAppropriateRate(cp1, cp2, p, x1, x2, x3);
return rst;
}
}
private double GetSamplePoint(double cp1, double cp2, double rate)
{
return 3d * cp1 * rate * (1d - rate) * (1d - rate) + 3d * cp2 * rate * rate * (1d - rate) + rate * rate * rate;
// return 3 * cp1 * rate - 3 * cp1 * 2 * rate * rate + 3 * cp1 * rate * rate * rate + 3 * cp2 * rate * rate - 3 * cp2 * rate * rate * rate + rate * rate * rate;
}

protected override double GetSampleRate(double time)
{
return GetSampleRate(X1, X2, time);
}
protected override double GetSampleValue(double rate)
{
return GetSamplePoint(Y1, Y2, rate);
}
}
}
64 changes: 64 additions & 0 deletions EleCho.WpfSuite/Animations/QuadraticBezierEase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
using System;

namespace EleCho.WpfSuite
{
public class QuadraticBezierEase : BezierEaseBase
{
private double x;
private double y;

/// <summary>
/// X value of control point
/// </summary>
public double X { get => x; set => x = value; }

/// <summary>
/// Y value of control point
/// </summary>
public double Y { get => y; set => y = value; }

private double GetSamplePoint(double cp, double rate)
{
return 2 * rate * (1 - rate) * cp + rate * rate * 1;
}
private double PickAppropriateRate(double cp, double p, params double[] rates)
{
double result = double.NaN;
double diffNow = double.MaxValue;
foreach (var rate in rates)
if (rate >= 0 && rate <= 1 && Math.Abs(GetSamplePoint(cp, rate) - p) < diffNow)
result = rate;
return result;
}
private double GetSampleRate(double cp, double p)
{
double
a = 1 - 2 * cp,
b = 2 * cp,
c = -p;
double
delta = b * b - 4 * a * c;
if (delta > 0)
{
double
x1 = (-b + Math.Sqrt(delta)) / (2 * a),
x2 = (-b - Math.Sqrt(delta)) / (2 * a);
double rst = PickAppropriateRate(cp, p, x1, x2);
return rst;
}
else if (delta == 0)
{
double
x = -b / (2 * a);
return x;
}
else
{
throw new Exception("Fuck");
}
}

protected override double GetSampleRate(double time) => GetSampleRate(this.X, time);
protected override double GetSampleValue(double rate) => GetSamplePoint(this.X, rate);
}
}

0 comments on commit 097e27f

Please sign in to comment.