Skip to content

Commit c91af75

Browse files
committed
added shared interface to polynomials & cleanup
I think I also fixed the compose function
1 parent b3955ab commit c91af75

File tree

8 files changed

+410
-189
lines changed

8 files changed

+410
-189
lines changed

Runtime/Curves/IPolynomialCubic.cs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
namespace Freya {
2+
3+
public interface IPolynomialCubic<P, V> {
4+
5+
/// <summary>The constant coefficient</summary>
6+
public V C0 { get; set; }
7+
/// <summary>The linear coefficient</summary>
8+
public V C1 { get; set; }
9+
/// <summary>The quadratic coefficient</summary>
10+
public V C2 { get; set; }
11+
/// <summary>The cubic coefficient</summary>
12+
public V C3 { get; set; }
13+
14+
/// <summary>The degree of the polynomial</summary>
15+
public int Degree { get; }
16+
17+
/// <summary>Returns the component polynomial of the given axis/dimension</summary>
18+
/// <param name="i">Index of axis/dimension. 0 = x. 1 = y, etc</param>
19+
public Polynomial this[ int i ] { get; set; }
20+
21+
/// <summary>Gets the coefficient of the given degree</summary>
22+
/// <param name="degree">The degree of the coefficient you want to get. For example, 0 will return the constant coefficient, 3 will return the cubic coefficient</param>
23+
V GetCoefficient( int degree );
24+
25+
/// <summary>Sets the coefficient of the given degree</summary>
26+
/// <param name="degree">The degree of the coefficient you want to set. For example, 0 will return the constant coefficient, 3 will return the cubic coefficient</param>
27+
/// <param name="value">The value to set it to</param>
28+
public void SetCoefficient( int degree, V value );
29+
30+
/// <summary>Evaluates the polynomial at the given value <c>t</c></summary>
31+
/// <param name="t">The value to sample at</param>
32+
public V Eval( float t );
33+
34+
/// <summary>Evaluates the <c>n</c>:th derivative of the polynomial at the given value <c>t</c></summary>
35+
/// <param name="t">The value to sample at</param>
36+
/// <param name="n">The derivative to evaluate</param>
37+
public V Eval( float t, int n );
38+
39+
/// <summary>Differentiates this function, returning the n-th derivative of this polynomial</summary>
40+
/// <param name="n">The number of times to differentiate this function. 0 returns the function itself, 1 returns the first derivative</param>
41+
public P Differentiate( int n = 1 );
42+
43+
/// <summary>Scales the parameter space by a factor. For example, the output of the polynomial in the input interval [0 to 1] will now be in the range [0 to factor]</summary>
44+
/// <param name="factor">The factor to scale the input parameters by</param>
45+
public P ScaleParameterSpace( float factor );
46+
47+
/// <summary>Given an inner function g(x), returns f(g(x))</summary>
48+
/// <param name="g0">The constant coefficient of the inner function g(x)</param>
49+
/// <param name="g1">The linear coefficient of the inner function g(x)</param>
50+
public P Compose( float g0, float g1 );
51+
52+
}
53+
54+
}

Runtime/Curves/IPolynomialCubic.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Runtime/Curves/IPolynomialMath.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
using UnityEngine;
2+
3+
namespace Freya {
4+
5+
public interface IPolynomialMath<P, V> {
6+
public P NaN { get; }
7+
public P FitCubicFrom0( float x1, float x2, float x3, V y0, V y1, V y2, V y3 );
8+
}
9+
10+
public struct PolynomialMath1D : IPolynomialMath<Polynomial, float> {
11+
public Polynomial NaN => Polynomial.NaN;
12+
13+
/// <inheritdoc cref="Polynomial.FitCubicFrom0(float,float,float,float,float,float,float)"/>
14+
public Polynomial FitCubicFrom0( float x1, float x2, float x3, float y0, float y1, float y2, float y3 ) => Polynomial.FitCubicFrom0( x1, x2, x3, y0, y1, y2, y3 );
15+
}
16+
17+
public struct PolynomialMath2D : IPolynomialMath<Polynomial2D, Vector2> {
18+
public Polynomial2D NaN => Polynomial2D.NaN;
19+
20+
/// <inheritdoc cref="Polynomial2D.FitCubicFrom0(float,float,float,Vector2,Vector2,Vector2,Vector2)"/>
21+
public Polynomial2D FitCubicFrom0( float x1, float x2, float x3, Vector2 y0, Vector2 y1, Vector2 y2, Vector2 y3 ) => Polynomial2D.FitCubicFrom0( x1, x2, x3, y0, y1, y2, y3 );
22+
}
23+
24+
public struct PolynomialMath3D : IPolynomialMath<Polynomial3D, Vector3> {
25+
public Polynomial3D NaN => Polynomial3D.NaN;
26+
27+
/// <inheritdoc cref="Polynomial3D.FitCubicFrom0(float,float,float,Vector3,Vector3,Vector3,Vector3)"/>
28+
public Polynomial3D FitCubicFrom0( float x1, float x2, float x3, Vector3 y0, Vector3 y1, Vector3 y2, Vector3 y3 ) => Polynomial3D.FitCubicFrom0( x1, x2, x3, y0, y1, y2, y3 );
29+
}
30+
31+
public struct PolynomialMath4D : IPolynomialMath<Polynomial4D, Vector4> {
32+
public Polynomial4D NaN => Polynomial4D.NaN;
33+
34+
/// <inheritdoc cref="Polynomial4D.FitCubicFrom0(float,float,float,Vector4,Vector4,Vector4,Vector4)"/>
35+
public Polynomial4D FitCubicFrom0( float x1, float x2, float x3, Vector4 y0, Vector4 y1, Vector4 y2, Vector4 y3 ) => Polynomial4D.FitCubicFrom0( x1, x2, x3, y0, y1, y2, y3 );
36+
37+
}
38+
39+
}

Runtime/Curves/IPolynomialMath.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Runtime/Curves/Polynomial.cs

Lines changed: 86 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -9,52 +9,27 @@
99
namespace Freya {
1010

1111
/// <summary>A polynomial in the form <c>ax³+bx²+cx+d</c>, up to a cubic, with functions like derivatives, root finding, and more</summary>
12-
[Serializable] public struct Polynomial {
12+
[Serializable] public struct Polynomial : IPolynomialCubic<Polynomial, float> {
1313

1414
const MethodImplOptions INLINE = MethodImplOptions.AggressiveInlining;
1515

1616
/// <summary>A polynomial with all 0 coefficients. f(x) = 0</summary>
1717
public static readonly Polynomial zero = new Polynomial( 0, 0, 0, 0 );
18-
18+
1919
/// <summary>A polynomial with all NaN coefficients</summary>
2020
public static readonly Polynomial NaN = new Polynomial( float.NaN, float.NaN, float.NaN, float.NaN );
2121

22-
/// <summary>The cubic coefficient</summary>
23-
[FormerlySerializedAs( "fCubic" )] public float c3;
24-
25-
/// <summary>The quadratic coefficient</summary>
26-
[FormerlySerializedAs( "fQuadratic" )] public float c2;
22+
/// <summary>The constant coefficient</summary>
23+
[FormerlySerializedAs( "fConstant" )] public float c0;
2724

2825
/// <summary>The linear coefficient</summary>
2926
[FormerlySerializedAs( "fLinear" )] public float c1;
3027

31-
/// <summary>The constant coefficient</summary>
32-
[FormerlySerializedAs( "fConstant" )] public float c0;
33-
34-
/// <summary>Get or set the coefficient of the given degree</summary>
35-
/// <param name="degree">The degree of the coefficient you want to get/set. For example, 0 will return the constant coefficient, 3 will return the cubic coefficient</param>
36-
public float this[ int degree ] {
37-
get =>
38-
degree switch {
39-
0 => c0,
40-
1 => c1,
41-
2 => c2,
42-
3 => c3,
43-
_ => throw new IndexOutOfRangeException( "Polynomial coefficient degree/index has to be between 0 and 3" )
44-
};
45-
set {
46-
_ = degree switch {
47-
0 => c0 = value,
48-
1 => c1 = value,
49-
2 => c2 = value,
50-
3 => c3 = value,
51-
_ => throw new IndexOutOfRangeException( "Polynomial coefficient degree/index has to be between 0 and 3" )
52-
};
53-
}
54-
}
28+
/// <summary>The quadratic coefficient</summary>
29+
[FormerlySerializedAs( "fQuadratic" )] public float c2;
5530

56-
/// <summary>The degree of the polynomial</summary>
57-
public int Degree => GetPolynomialDegree( c0, c1, c2, c3 );
31+
/// <summary>The cubic coefficient</summary>
32+
[FormerlySerializedAs( "fCubic" )] public float c3;
5833

5934
/// <summary>Creates a polynomial up to a cubic</summary>
6035
/// <param name="c0">The constant coefficient</param>
@@ -90,18 +65,59 @@ public float this[ int degree ] {
9065
/// <inheritdoc cref="Polynomial(Vector4)"/>
9166
public Polynomial( (float c0, float c1, float c2) coefficients ) => ( c0, c1, c2, c3 ) = ( coefficients.c0, coefficients.c1, coefficients.c2, 0 );
9267

93-
/// <summary>Evaluates the polynomial at the given value <c>t</c></summary>
94-
/// <param name="t">The value to sample at</param>
95-
public float Eval( float t ) => c3 * ( t * t * t ) + c2 * ( t * t ) + c1 * t + c0;
68+
#region IPolynomialCubic
69+
70+
public float C0 {
71+
[MethodImpl( INLINE )] get => c0;
72+
[MethodImpl( INLINE )] set => c0 = value;
73+
}
74+
public float C1 {
75+
[MethodImpl( INLINE )] get => c1;
76+
[MethodImpl( INLINE )] set => c1 = value;
77+
}
78+
public float C2 {
79+
[MethodImpl( INLINE )] get => c2;
80+
[MethodImpl( INLINE )] set => c2 = value;
81+
}
82+
public float C3 {
83+
[MethodImpl( INLINE )] get => c3;
84+
[MethodImpl( INLINE )] set => c3 = value;
85+
}
86+
public Polynomial this[ int i ] {
87+
[MethodImpl( INLINE )] get => i == 0 ? this : throw new IndexOutOfRangeException( "float polynomials don't have vector components" );
88+
[MethodImpl( INLINE )] set => this = value;
89+
}
90+
91+
public int Degree => GetPolynomialDegree( c0, c1, c2, c3 );
92+
93+
[MethodImpl( INLINE )] public float GetCoefficient( int degree ) =>
94+
degree switch {
95+
0 => c0,
96+
1 => c1,
97+
2 => c2,
98+
3 => c3,
99+
_ => throw new IndexOutOfRangeException( "Polynomial coefficient degree/index has to be between 0 and 3" )
100+
};
101+
102+
[MethodImpl( INLINE )] public void SetCoefficient( int degree, float value ) {
103+
_ = degree switch {
104+
0 => c0 = value,
105+
1 => c1 = value,
106+
2 => c2 = value,
107+
3 => c3 = value,
108+
_ => throw new IndexOutOfRangeException( "Polynomial coefficient degree/index has to be between 0 and 3" )
109+
};
110+
}
96111

97-
/// <summary>Evaluates the <c>n</c>:th derivative of the polynomial at the given value <c>t</c></summary>
98-
/// <param name="t">The value to sample at</param>
99-
/// <param name="n">The derivative to evaluate</param>
100-
public float Eval( float t, int n ) => Differentiate( n ).Eval( t );
112+
public float Eval( float t ) {
113+
float t2 = t * t;
114+
float t3 = t * t2;
115+
return c3 * t3 + c2 * t2 + c1 * t + c0;
116+
}
101117

102-
/// <summary>Differentiates this function, returning the n-th derivative of this polynomial</summary>
103-
/// <param name="n">The number of times to differentiate this function. 0 returns the function itself, 1 returns the first derivative</param>
104-
public Polynomial Differentiate( int n = 1 ) {
118+
[MethodImpl( INLINE )] public float Eval( float t, int n ) => Differentiate( n ).Eval( t );
119+
120+
[MethodImpl( INLINE )] public Polynomial Differentiate( int n = 1 ) {
105121
return n switch {
106122
0 => this,
107123
1 => new Polynomial( c1, 2 * c2, 3 * c3, 0 ),
@@ -111,25 +127,38 @@ public Polynomial Differentiate( int n = 1 ) {
111127
};
112128
}
113129

130+
public Polynomial ScaleParameterSpace( float factor ) {
131+
// ReSharper disable once CompareOfFloatsByEqualityOperator
132+
if( factor == 1f )
133+
return this;
134+
float factor2 = factor * factor;
135+
float factor3 = factor2 * factor;
136+
return new Polynomial(
137+
c0,
138+
c1 / factor,
139+
c2 / factor2,
140+
c3 / factor3
141+
);
142+
}
143+
114144
/// <summary>Given an inner function g(x), returns f(g(x))</summary>
115145
/// <param name="g0">The constant coefficient of the inner function g(x)</param>
116146
/// <param name="g1">The linear coefficient of the inner function g(x)</param>
117147
public Polynomial Compose( float g0, float g1 ) {
118-
float ss = g1 * g1;
119-
float sss = ss * g1;
120-
float oo = g0 * g0;
121-
float ooo = oo * g0;
122-
float _3c3 = 3 * c3;
123-
float c2g0 = c2 * g0;
124-
148+
float g0_2 = g0 * g0;
149+
float g0_3 = g0 * g0_2;
150+
float g1_2 = g1 * g1;
151+
float g1_3 = g1 * g1_2;
125152
return new Polynomial(
126-
c3 * ooo + c2 * oo + c2g0 + c0,
127-
g1 * ( _3c3 * oo + 2 * c2g0 + c1 ),
128-
ss * ( _3c3 * g0 + c2 ),
129-
sss * c3
153+
c0 + c1 * g0 + c2 * g0_2 + c3 * g0_3,
154+
c1 * g1 + c2 * 2 * g0 * g1 + c3 * 3 * g0_2 * g1,
155+
c2 * g1_2 + c3 * 3 * g0 * g1_2,
156+
c3 * g1_3
130157
);
131158
}
132159

160+
#endregion
161+
133162
/// <summary>Fits a cubic polynomial to pass through the given coordinates</summary>
134163
public static Polynomial FitCubic( float x0, float x1, float x2, float x3, float y0, float y1, float y2, float y3 ) {
135164
// precalcs
@@ -202,21 +231,6 @@ public static Polynomial FitCubicFrom0( float x1, float x2, float x3, float y0,
202231
return new Polynomial( c0, c1, c2, c3 );
203232
}
204233

205-
/// <summary>Scales the parameter space by a factor. For example, the output in the interval [0 to 1] will now be in the range [0 to factor]</summary>
206-
/// <param name="factor">The factor to scale the input parameters by</param>
207-
public Polynomial ScaleParameterSpace( float factor ) {
208-
// ReSharper disable once CompareOfFloatsByEqualityOperator
209-
if( factor == 1f )
210-
return this;
211-
float factor2 = factor * factor;
212-
float factor3 = factor2 * factor;
213-
return new Polynomial(
214-
c0,
215-
c1 / factor,
216-
c2 / factor2,
217-
c3 / factor3
218-
);
219-
}
220234

221235
/// <summary>Splits the 0-1 range into two distinct polynomials at the given parameter value u, where both new curves cover the same total range with their individual 0-1 ranges</summary>
222236
/// <param name="u">The parameter value to split at</param>
@@ -482,15 +496,15 @@ public override string ToString() {
482496

483497
bool hasAddedFirstTerm = false;
484498
for( int c = 0; c < 4; c++ ) {
485-
float value = this[c];
499+
float value = GetCoefficient( c );
486500
if( value != 0 ) {
487501
if( hasAddedFirstTerm == false ) {
488502
hasAddedFirstTerm = true;
489-
strBuilder.Append( this[c] );
503+
strBuilder.Append( GetCoefficient( c ) );
490504
} else {
491505
if( value > 0 )
492506
strBuilder.Append( "+" );
493-
strBuilder.Append( this[c] );
507+
strBuilder.Append( GetCoefficient( c ) );
494508
if( c > 0 )
495509
strBuilder.Append( tPowerSuffixStr[c] );
496510
}

0 commit comments

Comments
 (0)