Skip to content

Commit 036015e

Browse files
committed
Added interpolation classes from old FCL library.
1 parent b694d52 commit 036015e

13 files changed

+1213
-0
lines changed
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
using System;
2+
using Whathecode.System.Arithmetic.Interpolation.KeyPoint;
3+
using Whathecode.System.Arithmetic.Range;
4+
using Whathecode.System.Collections.Algorithm;
5+
using Whathecode.System.Operators;
6+
7+
8+
namespace Whathecode.System.Arithmetic.Interpolation
9+
{
10+
/// <summary>
11+
/// An abstract class for the computation of values between ones that are known using the surrounding values.
12+
/// </summary>
13+
/// <typeparam name = "TValue">The type of the values to interpolate between.</typeparam>
14+
/// <typeparam name = "TMath">The value type to use for the calculations.</typeparam>
15+
public abstract class AbstractInterpolation<TValue, TMath>
16+
where TMath : IComparable<TMath>
17+
{
18+
/// <summary>
19+
/// The list of data between which is interpolated.
20+
/// </summary>
21+
public AbstractKeyPointCollection<TValue, TMath> KeyPoints { get; set; }
22+
23+
24+
/// <summary>
25+
/// Create a new object to do interpolation, initialized with a given list of data.
26+
/// </summary>
27+
/// <param name = "keyPoints">The list of key points to interpolate between.</param>
28+
protected AbstractInterpolation( AbstractKeyPointCollection<TValue, TMath> keyPoints )
29+
{
30+
KeyPoints = keyPoints;
31+
}
32+
33+
34+
#region Abstract definitions
35+
36+
/// <summary>
37+
/// Interpolate between two values, with the given neighboring indices.
38+
/// </summary>
39+
/// <param name = "smallerIndex">The index of the smaller value.</param>
40+
/// <param name = "biggerIndex">The index of the larger value.</param>
41+
/// <param name = "position">The position within the data range of the key points.</param>
42+
/// <param name = "percentage">The percentage in between the two values.</param>
43+
/// <returns>The interpolated value between the two values.</returns>
44+
protected abstract TValue Interpolate( int smallerIndex, int biggerIndex, TMath position, double percentage );
45+
46+
/// <summary>
47+
/// Get the tangent in between two values, with the given neighbouring indices.
48+
/// </summary>
49+
/// <param name = "smallerIndex">The index of the smaller value.</param>
50+
/// <param name = "biggerIndex">The index of the larger value.</param>
51+
/// <param name= "position">The position within the data range of the key points.</param>
52+
/// <param name = "percentage">The percentage in between the two values.</param>
53+
/// <returns>A value representing the tangent between the two values.</returns>
54+
protected abstract TValue TangentAt( int smallerIndex, int biggerIndex, TMath position, double percentage );
55+
56+
#endregion
57+
58+
59+
/// <summary>
60+
/// Get interpolated data for a given percentage within the total range of the key points.
61+
/// TODO: Would it be cleaner not to use a double for percentage, but a generic Percentage type?
62+
/// </summary>
63+
/// <param name = "percentage">The percentage in between the first and the last value to get interpolated data for.</param>
64+
/// <returns>The interpolated data.</returns>
65+
public TValue Interpolate( double percentage )
66+
{
67+
if ( KeyPoints.Count <= 0 )
68+
{
69+
throw new InvalidOperationException( "No key points between which to interpolate available." );
70+
}
71+
72+
// TODO: Allow extrapolation?
73+
if ( percentage < 0 )
74+
{
75+
percentage = 0;
76+
}
77+
else if ( percentage > 1 )
78+
{
79+
percentage = 1;
80+
}
81+
82+
// Find in between which two keypoints the desired position lies.
83+
TMath position = KeyPoints.DataRange.GetValueAt( percentage );
84+
BinarySearchResult<TMath> searchResult = KeyPoints.BinarySearch( position );
85+
86+
// Return exact value when found, or interpolated when not found.
87+
TValue result;
88+
if ( searchResult.IsObjectFound )
89+
{
90+
result = KeyPoints[ searchResult.Found.Object ];
91+
}
92+
else
93+
{
94+
// Use double math to calculate the desired value inside the interval. (percentage)
95+
double smallerValue = CastOperator<TMath, double>.Cast( searchResult.NotFound.Smaller );
96+
double biggerValue = CastOperator<TMath, double>.Cast( searchResult.NotFound.Bigger );
97+
98+
result = Interpolate(
99+
KeyPoints.IndexAtPosition( searchResult.NotFound.Smaller ),
100+
KeyPoints.IndexAtPosition( searchResult.NotFound.Bigger ),
101+
position,
102+
new Interval<double>( smallerValue, biggerValue ).GetPercentageFor( CastOperator<TMath, double>.Cast( position ) ) );
103+
}
104+
105+
return result;
106+
}
107+
108+
/// <summary>
109+
/// Get interpolated data at a certain position in the range.
110+
/// TODO: This position is based on the distance measure as provided by the type interpolation provider.
111+
/// This might only make sense for AbsoluteKeyPointCollection's.
112+
/// </summary>
113+
/// <param name = "at">The position of which to get the interpolated data.</param>
114+
/// <returns>The interpolated data.</returns>
115+
public TValue ValueAt( TMath at )
116+
{
117+
double percentage = KeyPoints.DataRange.GetPercentageFor( at );
118+
119+
return Interpolate( percentage );
120+
}
121+
122+
/// <summary>
123+
/// Get the tangent at a given percentage within the range of the key points.
124+
/// </summary>
125+
/// <param name = "percentage">The percentage within the range for which to receive the tangent at.</param>
126+
/// <returns>The tangent at a given percentage within the range of the key points.</returns>
127+
public TValue TangentAt( double percentage )
128+
{
129+
// TODO: Allow extrapolation?
130+
if ( percentage < 0 || percentage > 1 )
131+
{
132+
throw new ArgumentException( "", nameof( percentage ) );
133+
}
134+
if ( KeyPoints.Count <= 0 )
135+
{
136+
throw new InvalidOperationException( "No key points between which to interpolate available." );
137+
}
138+
139+
// Find in between which two keypoints the desired position lies.
140+
TMath position = KeyPoints.DataRange.GetValueAt( percentage );
141+
BinarySearchResult<TMath> searchResult = KeyPoints.BinarySearch( position );
142+
143+
// Use double math to calculate percentage of desired value inside
144+
double smallerValue = CastOperator<TMath, double>.Cast( searchResult.NotFound.Smaller );
145+
double biggerValue = CastOperator<TMath, double>.Cast( searchResult.NotFound.Bigger );
146+
147+
TValue result = TangentAt(
148+
KeyPoints.IndexAtPosition( searchResult.NotFound.Smaller ),
149+
KeyPoints.IndexAtPosition( searchResult.NotFound.Bigger ),
150+
position,
151+
new Interval<double>( smallerValue, biggerValue ).GetPercentageFor( CastOperator<TMath, double>.Cast( position ) ) );
152+
153+
return result;
154+
}
155+
}
156+
}
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
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+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
namespace Whathecode.System.Arithmetic.Interpolation.KeyPoint
2+
{
3+
/// <summary>
4+
/// A helper struct for <see cref="AbsoluteKeyPointCollection{TValue, TMath}" /> which allows linking key points to values which
5+
/// are used by <see cref="AbsoluteKeyPointCollection{TValue, TMath}" />.
6+
/// </summary>
7+
/// <typeparam name = "TKey">The type used for the values to which the added key points are linked.</typeparam>
8+
/// <typeparam name = "TValue">The type of the key points.</typeparam>
9+
public struct AbsoluteKeyPoint<TKey, TValue>
10+
where TKey : new()
11+
{
12+
/// <summary>
13+
/// A reference key point which can be used to initialize the
14+
/// <see cref="System.Arithmetic.Interpolation.KeyPoint.AbsoluteKeyPointCollection{TValue,TMath}">AbsoluteKeyPointCollection</see>.
15+
/// </summary>
16+
public static AbsoluteKeyPoint<TKey, TValue> ReferenceKeyPoint
17+
{
18+
get { return new AbsoluteKeyPoint<TKey, TValue>( new TKey(), default(TValue) ); }
19+
}
20+
21+
/// <summary>
22+
/// The value to which the key point is linked.
23+
/// </summary>
24+
public TKey Key { get; private set; }
25+
26+
/// <summary>
27+
/// The actual key point.
28+
/// </summary>
29+
public TValue KeyPoint { get; private set; }
30+
31+
32+
/// <summary>
33+
/// Create a new key point linked to a value.
34+
/// </summary>
35+
/// <param name = "key">The value to which the key point is linked.</param>
36+
/// <param name = "keyPoint">The actual key point.</param>
37+
public AbsoluteKeyPoint( TKey key, TValue keyPoint )
38+
: this()
39+
{
40+
Key = key;
41+
KeyPoint = keyPoint;
42+
}
43+
}
44+
}

0 commit comments

Comments
 (0)