1
+ using System ;
2
+ using System . Linq ;
3
+ using Whathecode . System . Arithmetic . Interpolation . KeyPoint ;
4
+ using Whathecode . System . Arithmetic . Interpolation . TypeProvider ;
5
+ using Whathecode . System . Operators ;
6
+
7
+
8
+ namespace Whathecode . System . Arithmetic . Interpolation
9
+ {
10
+ /// <summary>
11
+ /// Interpolation between known values using piecewise cubic curves and a tension parameter for the tangents.
12
+ /// // TODO: Are double calculations inside this class always desired?
13
+ /// </summary>
14
+ /// <typeparam name = "TValue">The type of the values to interpolate between.</typeparam>
15
+ /// <typeparam name = "TMath">The type to use for the calculations.</typeparam>
16
+ public class CardinalSplineInterpolation < TValue , TMath > : AbstractInterpolation < TValue , TMath >
17
+ where TMath : IComparable < TMath >
18
+ {
19
+ /// <summary>
20
+ /// Determines the 'length' of the tangents. 1 will yield all zero tangents, 0 yields a Catmull-Rom spline (default).
21
+ /// </summary>
22
+ public double Tension { get ; private set ; }
23
+
24
+
25
+ /// <summary>
26
+ /// Create a new object to do cardinal spline interpolation.
27
+ /// </summary>
28
+ /// <param name = "keyPoints">The list of key points to interpolate between.</param>
29
+ public CardinalSplineInterpolation ( AbstractKeyPointCollection < TValue , TMath > keyPoints )
30
+ : this ( keyPoints , 0.5 ) { }
31
+
32
+ /// <summary>
33
+ /// Create a new object to do cardinal spline interpolation.
34
+ /// </summary>
35
+ /// <param name = "keyPoints">The list of key points to interpolate between.</param>
36
+ /// <param name = "tension">Specify a custom tension for the tangents. 1 will yield all zero tangents, 0.5 yields a Catmull-Rom spline (default).</param>
37
+ public CardinalSplineInterpolation ( AbstractKeyPointCollection < TValue , TMath > keyPoints , double tension )
38
+ : base ( keyPoints )
39
+ {
40
+ if ( tension < 0 || tension > 1 )
41
+ {
42
+ throw new ArgumentException ( "The tension should be a value between 0 and 1." , nameof ( tension ) ) ;
43
+ }
44
+
45
+ Tension = tension ;
46
+ }
47
+
48
+
49
+ protected override TValue Interpolate ( int smallerIndex , int biggerIndex , TMath position , double percentage )
50
+ {
51
+ AbstractTypeInterpolationProvider < TValue , TMath > typeProvider = KeyPoints . TypeProvider ;
52
+
53
+ // Retrieve values for all dimensions for all 4 control points.
54
+ double [ ] [ ] values = RetrieveValues ( smallerIndex , biggerIndex , typeProvider ) ;
55
+
56
+ // Set up hermite base functions.
57
+ double t = percentage ;
58
+ double t2 = Math . Pow ( t , 2 ) ;
59
+ double t3 = Math . Pow ( t , 3 ) ;
60
+ double baseFunction00 = ( 2 * t3 ) - ( 3 * t2 ) + 1 ;
61
+ double baseFunction10 = t3 - ( 2 * t2 ) + t ;
62
+ double baseFunction01 = ( - 2 * t3 ) + ( 3 * t2 ) ;
63
+ double baseFunction11 = t3 - t2 ;
64
+
65
+ // Interpolate.
66
+ double tension = 1 - Tension ;
67
+ var interpolated = new TMath [ typeProvider . AmountOfDimensions ] ;
68
+ for ( int i = 0 ; i < typeProvider . AmountOfDimensions ; ++ i )
69
+ {
70
+ // Calculate tangents.
71
+ double tangentSmaller = tension * ( values [ 2 ] [ i ] - values [ 0 ] [ i ] ) ;
72
+ double tangentBigger = tension * ( values [ 3 ] [ i ] - values [ 1 ] [ i ] ) ;
73
+
74
+ // Multiply hermite base functions with the points (and tangents) and sum up.
75
+ double result =
76
+ ( baseFunction00 * values [ 1 ] [ i ] )
77
+ + ( baseFunction01 * values [ 2 ] [ i ] )
78
+ + ( baseFunction10 * tangentSmaller )
79
+ + ( baseFunction11 * tangentBigger ) ;
80
+
81
+ // Sum up all functions.
82
+ interpolated [ i ] = CastOperator < double , TMath > . Cast ( result ) ;
83
+ }
84
+
85
+ return typeProvider . CreateInstance ( position , interpolated ) ;
86
+ }
87
+
88
+ protected override TValue TangentAt ( int smallerIndex , int biggerIndex , TMath position , double percentage )
89
+ {
90
+ AbstractTypeInterpolationProvider < TValue , TMath > typeProvider = KeyPoints . TypeProvider ;
91
+
92
+ // Retrieve values for all dimensions for all 4 control points.
93
+ double [ ] [ ] values = RetrieveValues ( smallerIndex , biggerIndex , typeProvider ) ;
94
+
95
+ // Set up hermite base function derivatives.
96
+ double t = percentage ;
97
+ double t2 = Math . Pow ( t , 2 ) ;
98
+ double baseFunction00 = ( 6 * t2 ) - ( 6 * t ) ;
99
+ double baseFunction10 = ( 3 * t2 ) - ( 4 * t ) + 1 ;
100
+ double baseFunction01 = ( 6 * t ) - ( 6 * t2 ) ;
101
+ double baseFunction11 = ( 3 * t2 ) - ( 2 * t ) ;
102
+
103
+ // Get tangent by calculating derivative.
104
+ double tension = 1 - Tension ;
105
+ var tangent = new TMath [ typeProvider . AmountOfDimensions ] ;
106
+ for ( int i = 0 ; i < typeProvider . AmountOfDimensions ; ++ i )
107
+ {
108
+ // Original: (((2*t^3)-(3*t^2)+1)*b) + (((-2*t^3)+(3*t^2))*c) + ((t^3-(2*t^2)+t)*(n*(c-a))) + ((t^3-t^2)*(n*(d-b)))
109
+ // First derivative: ((3*d+3*c-3*b-3*a)*n-6*c+6*b)*t^2 + ((-2*d-4*c+2*b+4*a)*n+6*c-6*b)*t + (c-a)*n
110
+
111
+ // Calculate tangents.
112
+ double tangentSmaller = tension * ( values [ 2 ] [ i ] - values [ 0 ] [ i ] ) ;
113
+ double tangentBigger = tension * ( values [ 3 ] [ i ] - values [ 1 ] [ i ] ) ;
114
+
115
+ // Multiply derived hermite base functions with the points (and tangents) and sum up.
116
+ double result =
117
+ ( baseFunction00 * values [ 1 ] [ i ] )
118
+ + ( baseFunction01 * values [ 2 ] [ i ] )
119
+ + ( baseFunction10 * tangentSmaller )
120
+ + ( baseFunction11 * tangentBigger ) ;
121
+
122
+ // Sum up all functions.
123
+ tangent [ i ] = CastOperator < double , TMath > . Cast ( result ) ;
124
+ }
125
+
126
+ return typeProvider . CreateInstance ( position , tangent ) ;
127
+ }
128
+
129
+ /// <summary>
130
+ /// Retrieve values for all dimensions for all 4 control points.
131
+ /// </summary>
132
+ /// <returns>
133
+ /// A 2 dimensional array with all the dimension values for all 4 control points.
134
+ /// The first dimension of the array indicates the control point, the second the dimension.
135
+ /// </returns>
136
+ double [ ] [ ] RetrieveValues ( int smallerIndex , int biggerIndex , AbstractTypeInterpolationProvider < TValue , TMath > typeProvider )
137
+ {
138
+ // Retrieve required values.
139
+ TValue p0 = KeyPoints [ smallerIndex != 0 ? smallerIndex - 1 : smallerIndex ] ;
140
+ TValue p1 = KeyPoints [ smallerIndex ] ;
141
+ TValue p2 = KeyPoints [ biggerIndex ] ;
142
+ TValue p3 = KeyPoints [ biggerIndex != KeyPoints . Count - 1 ? biggerIndex + 1 : biggerIndex ] ;
143
+
144
+ // Retrieve required dimension values.
145
+ double [ ] p0Values = typeProvider . GetDimensionValues ( p0 ) . Cast < double > ( ) . ToArray ( ) ;
146
+ double [ ] p1Values = typeProvider . GetDimensionValues ( p1 ) . Cast < double > ( ) . ToArray ( ) ;
147
+ double [ ] p2Values = typeProvider . GetDimensionValues ( p2 ) . Cast < double > ( ) . ToArray ( ) ;
148
+ double [ ] p3Values = typeProvider . GetDimensionValues ( p3 ) . Cast < double > ( ) . ToArray ( ) ;
149
+
150
+ return new [ ] { p0Values , p1Values , p2Values , p3Values } ;
151
+ }
152
+ }
153
+ }
0 commit comments