UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
InterpolationPolicies.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4#include "CoreMinimal.h"
5#include "SplineMath.h"
7
8namespace UE
9{
10namespace Math
11{
12 template<typename T>
13 struct TQuat;
14}
15}
16
17namespace UE
18{
19namespace Geometry
20{
21namespace Spline
22{
23
24// Trait to detect if type T supports math operations needed for interpolation.
25template<typename T, typename = void>
26struct TIsMathInterpolable : std::false_type {};
27
28template<typename T>
30 decltype(FMath::Lerp(std::declval<T>(), std::declval<T>(), 0.5f)),
31 decltype(std::declval<T>() + std::declval<T>()),
32 decltype(std::declval<T>() * std::declval<float>())
33 >> : std::true_type {};
38template<typename T>
40{
41public:
42 // Keep existing parameter-based interpolation (used by attribute channels)
44 {
45 if (Window.Num() < 2)
46 {
47 return Window.Num() == 1 ? *Window[0] : T();
48 }
50 {
51 return FMath::Lerp(*Window[0], *Window[1], Parameter);
52 }
53 else
54 {
55 // Fallback for non-math types:
56 return Parameter < 0.5f ? *Window[0] : *Window[1];
57 }
58 }
59
60 // Add window-based interpolation for spline implementations
62 {
64 {
65 T Result = T(); // Assumes T() gives a neutral value (e.g. zero vector)
66 int32 Count = FMath::Min(Window.Num(), Basis.Num());
67 for (int32 i = 0; i < Count; ++i)
68 {
69 // Assumes T supports addition and multiplication by float.
70 Result = Result + (*Window[i] * Basis[i]);
71 }
72 return Result;
73 }
74 else
75 {
76 // Fallback: simply return the first element if available.
77 return Window.Num() > 0 ? *Window[0] : T();
78 }
79 }
80
81 // Template-based n-th derivative calculation
82 template<int32 Order>
84 {
85 if (Order == 0)
86 {
87 return Interpolate(Window, Parameter);
88 }
90 {
91 // For math types, use the generic derivative helper.
93 }
94 else
95 {
96 // For non-math types, derivatives are not meaningful.
97 // Return a default constructed value.
98 return T();
99 }
100 }
101};
102
103// Specialization only for int32
104template<>
106{
107public:
109 {
110 if (Window.Num() < 2)
111 {
112 return Window.Num() == 1 ? *Window[0] : 0;
113 }
114
115 // Handle boundary cases
116 if (Parameter <= 0.0f) return *Window[0];
117 if (Parameter >= 1.0f) return *Window[1];
118
119 // For intermediate values, explicitly convert to avoid implicit conversion warnings
120 const float Start = static_cast<float>(*Window[0]);
121 const float End = static_cast<float>(*Window[1]);
122 const float Result = Start + Parameter * (End - Start);
123
124 // Round to nearest integer
125 return FMath::RoundToInt(Result);
126 }
127
129 {
130 float Result = 0.0f;
131 for (int32 i = 0; i < FMath::Min(Window.Num(), Basis.Num()); ++i)
132 {
133 Result += static_cast<float>(*Window[i]) * Basis[i];
134 }
135 return FMath::RoundToInt(Result);
136 }
137
138 // Template-based n-th derivative calculation
139 template<int32 Order>
141 {
142 if (Order == 0)
143 {
144 return Interpolate(Window, Parameter);
145 }
147 }
148};
149
150// FLinearColor interpolation policy
151template<>
153{
154public:
156 {
157 if (Window.Num() < 2)
158 {
159 return Window.Num() == 1 ? *Window[0] : FLinearColor();
160 }
161
162 // Convert to linear color for better interpolation
163 return FLinearColor::LerpUsingHSV(*Window[0],*Window[1], Parameter).ToFColor(true);
164 }
165
167 {
168 // Convert all colors to linear space first
169 TArray<FLinearColor> LinearColors;
170 LinearColors.Reserve(Window.Num());
171 for (const FLinearColor* Color : Window)
172 {
173 LinearColors.Add(*Color);
174 }
175
176 // Perform weighted sum in linear space
178 for (int32 i = 0; i < FMath::Min(Window.Num(), Basis.Num()); ++i)
179 {
180 Result += LinearColors[i] * Basis[i];
181 }
182
183 // Convert back to FColor
184 return Result;
185 }
186
187 // Template-based n-th derivative calculation
188 template<int32 Order>
190 {
191 if (Order == 0)
192 {
193 return Interpolate(Window, Parameter);
194 }
196 }
197};
198
199// Partial specialization for any quaternion type (TQuat)
200template <typename RealType>
202{
204public:
205 // Parameter-based interpolation using Slerp
207 {
208 if (Window.Num() < 2)
209 {
210 return Window.Num() == 1 ? *Window[0] : TQuat::Identity;
211 }
212 return TQuat::Slerp(*Window[0], *Window[1], static_cast<RealType>(Parameter));
213 }
214
215 // Weighted quaternion interpolation
216 // based on eigenvector approach from Markley et al.,
217 // which handles antipodal quaternions and degenerate cases effectively.
219 {
220 const int32 Count = FMath::Min(Window.Num(), Basis.Num());
221 if (Count == 0)
222 {
223 return TQuat::Identity;
224 }
225
226 // Use first quaternion as reference for hemisphere checks
227 const TQuat& ReferenceQuat = *Window[0];
228
229 // Build 4x4 correlation matrix
230 RealType M[4][4] = {};
231 RealType TotalWeight = 0;
232
233 for (int32 Index = 0; Index < Count; ++Index)
234 {
235 const RealType Weight = static_cast<RealType>(Basis[Index]);
236 if (Weight > static_cast<RealType>(UE_KINDA_SMALL_NUMBER))
237 {
239
240 // Ensure consistent hemisphere to avoid cancellation
241 if ((Quat | ReferenceQuat) < static_cast<RealType>(0))
242 {
243 Quat *= static_cast<RealType>(-1);
244 }
245
246 // Accumulate outer product contribution
247 const RealType Q[4] = {Quat.X, Quat.Y, Quat.Z, Quat.W};
248 for (int32 Row = 0; Row < 4; ++Row)
249 {
250 for (int32 Col = 0; Col < 4; ++Col)
251 {
252 M[Row][Col] += Weight * Q[Row] * Q[Col];
253 }
254 }
255
256 TotalWeight += Weight;
257 }
258 }
259
260 if (FMath::IsNearlyZero(TotalWeight))
261 {
262 return TQuat::Identity;
263 }
264
265 // Precompute inverse for efficiency
266 double InvTotalWeight = 1.0 / TotalWeight;
267
268 // Normalize matrix for numerical stability
269 for (int32 Row = 0; Row < 4; ++Row)
270 {
271 for (int32 Col = 0; Col < 4; ++Col)
272 {
273 M[Row][Col] *= InvTotalWeight;
274 }
275 }
276
277 // Find dominant eigenvector using power iteration
278 RealType EigenVec[4] = {0, 0, 0, 1};
279 RealType PrevEigenValue = 0;
280
281 constexpr int32 MaxIterations = 32;
282 for (int32 Iter = 0; Iter < MaxIterations; ++Iter)
283 {
284 // Matrix-vector multiplication: v' = M*v
285 RealType NewVec[4] = {0};
286 for (int32 Row = 0; Row < 4; ++Row)
287 {
288 for (int32 Col = 0; Col < 4; ++Col)
289 {
290 NewVec[Row] += M[Row][Col] * EigenVec[Col];
291 }
292 }
293
294 // Normalize vector
295 RealType LengthSq =
296 NewVec[0] * NewVec[0] +
297 NewVec[1] * NewVec[1] +
298 NewVec[2] * NewVec[2] +
299 NewVec[3] * NewVec[3];
300
301 // Possible with certain opposing quaternion configurations
303 {
304 return TQuat::Identity;
305 }
306
307 RealType InvLength = FMath::InvSqrt(LengthSq);
308 for (int32 i = 0; i < 4; ++i)
309 {
310 EigenVec[i] = NewVec[i] * InvLength;
311 }
312
313 // Calculate Rayleigh quotient for convergence check
314 RealType EigenValue = 0;
315 for (int32 Row = 0; Row < 4; ++Row)
316 {
317 for (int32 Col = 0; Col < 4; ++Col)
318 {
319 EigenValue += EigenVec[Row] * M[Row][Col] * EigenVec[Col];
320 }
321 }
322
323 // Check for sufficient convergence
325 {
326 break;
327 }
328
330 }
331
332 // Convert eigenvector to TQuat
334 EigenVec[0],
335 EigenVec[1],
336 EigenVec[2],
337 EigenVec[3]
338 );
339
340 ResultQuat.Normalize();
341 return ResultQuat;
342 }
343
344
345 // Template-based n-th derivative calculation
346 template<int32 Order>
348 {
349 if (Order == 0)
350 {
351 return Interpolate(Window, Parameter);
352 }
353
354 // First derivative - angular velocity
355 if (Order == 1)
356 {
357 // Adaptive step size based on quaternion difference
358 RealType base_h = static_cast<RealType>(0.01);
359 TQuat Q0 = Interpolate(Window, Parameter);
360 TQuat Q1base = Interpolate(Window, Parameter + static_cast<float>(base_h));
361 RealType diff = Q0.AngularDistance(Q1base);
362 RealType h = base_h * FMath::Clamp(
363 diff,
364 static_cast<RealType>(0.001),
365 static_cast<RealType>(0.1)
366 );
367
368 TQuat Q1 = Interpolate(Window, Parameter + static_cast<float>(h));
369 return (Q1 * Q0.Inverse()).Log() * (static_cast<RealType>(1) / h);
370 }
371
372 // Second derivative - angular acceleration
373 if (Order == 2)
374 {
375 RealType base_h = static_cast<RealType>(0.01);
376 TQuat Q_m1 = Interpolate(Window, Parameter - static_cast<float>(base_h));
377 TQuat Q_p1 = Interpolate(Window, Parameter + static_cast<float>(base_h));
378 RealType diff = Q_m1.AngularDistance(Q_p1);
379 RealType h = base_h * FMath::Clamp(
380 diff / static_cast<RealType>(2),
381 static_cast<RealType>(0.001),
382 static_cast<RealType>(0.1)
383 );
384
385 TQuat V0 = EvaluateDerivative<1>(Window, Parameter - static_cast<float>(h));
386 TQuat V1 = EvaluateDerivative<1>(Window, Parameter + static_cast<float>(h));
387 return (V1 * V0.Inverse()).Log() * (static_cast<RealType>(1) / (static_cast<RealType>(2) * h));
388 }
389
390 // Higher orders generally not meaningful for rotations
391 return TQuat::Identity;
392 }
393};
394
395// FTransform interpolation policy
396template<>
398{
399public:
401 {
402 if (Window.Num() < 2)
403 {
404 return Window.Num() == 1 ? *Window[0] : FTransform::Identity;
405 }
406
407 const FTransform& A = *Window[0];
408 const FTransform& B = *Window[1];
409
410 FTransform Result;
411 Result.SetLocation(FMath::Lerp(A.GetLocation(), B.GetLocation(), Parameter));
412 Result.SetRotation(FQuat::Slerp(A.GetRotation(), B.GetRotation(), Parameter));
413 Result.SetScale3D(FMath::Lerp(A.GetScale3D(), B.GetScale3D(), Parameter));
414
415 return Result;
416 }
417
419 {
420 // Handle empty case
421 if (Window.Num() == 0 || Basis.Num() == 0)
422 {
424 }
425
426 // Interpolate components separately
427 FVector Location = FVector::ZeroVector;
429 FVector Scale = FVector(1.0f);
430
431 float TotalRotWeight = 0.0f;
432
433 // Weighted sum of locations
434 for (int32 i = 0; i < FMath::Min(Window.Num(), Basis.Num()); ++i)
435 {
436 const float Weight = Basis[i];
438 {
439 Location += Window[i]->GetLocation() * Weight;
440 Scale += Window[i]->GetScale3D() * Weight;
441
442 // Handle rotation separately with proper quaternion blending
443 const FQuat& Q = Window[i]->GetRotation();
444 const double Dot = Rotation.W * Q.W + Rotation.X * Q.X + Rotation.Y * Q.Y + Rotation.Z * Q.Z;
445 Rotation += (Dot >= 0.0f ? Q : -Q) * Weight;
447 }
448 }
449
450 // Normalize the quaternion if we accumulated any weights
452 {
453 Rotation.Normalize();
454 }
455
456 return FTransform(Rotation, Location, Scale);
457 }
458
459 template<int32 Order>
461 {
462 if (Order == 0)
463 {
464 return Interpolate(Window, Parameter);
465 }
466
467 // Split into components
468 TArray<FVector> Translations;
469 TArray<FQuat> Rotations;
470 TArray<FVector> Scales;
471
472 Translations.SetNum(Window.Num());
473 Rotations.SetNum(Window.Num());
474 Scales.SetNum(Window.Num());
475
476 for (int32 i = 0; i < Window.Num(); ++i)
477 {
478 Translations[i] = Window[i]->GetTranslation();
479 Rotations[i] = Window[i]->GetRotation();
480 Scales[i] = Window[i]->GetScale3D();
481 }
482
483 // Calculate derivatives for each component
484 TArrayView<const FVector> TransView(Translations.GetData(), Translations.Num());
485 TArrayView<const FQuat> RotView(Rotations.GetData(), Rotations.Num());
487
491
493 }
494};
495
496
497// Base derivative calculator
498template <typename T, int32 Order>
506} // end namespace UE::Geometry::Spline
507} // end namespace UE::Geometry
508} // end namespace UE
FPlatformTypes::int32 int32
A 32-bit signed integer.
Definition Platform.h:1125
UE_FORCEINLINE_HINT TSharedRef< CastToType, Mode > StaticCastSharedRef(TSharedRef< CastFromType, Mode > const &InSharedRef)
Definition SharedPointer.h:127
#define FVector
Definition IOSSystemIncludes.h:8
UE::Math::TTransform< double > FTransform
Definition MathFwd.h:53
#define UE_SMALL_NUMBER
Definition UnrealMathUtility.h:130
#define UE_KINDA_SMALL_NUMBER
Definition UnrealMathUtility.h:131
Definition ArrayView.h:139
UE_FORCEINLINE_HINT constexpr SizeType Num() const
Definition ArrayView.h:380
Definition Array.h:670
UE_REWRITE SizeType Num() const
Definition Array.h:1144
UE_NODEBUG UE_FORCEINLINE_HINT ElementType * GetData() UE_LIFETIMEBOUND
Definition Array.h:1027
void SetNum(SizeType NewNum, EAllowShrinking AllowShrinking=UE::Core::Private::AllowShrinkingByDefault< AllocatorType >())
Definition Array.h:2308
UE_NODEBUG UE_FORCEINLINE_HINT SizeType Add(ElementType &&Item)
Definition Array.h:2696
UE_FORCEINLINE_HINT void Reserve(SizeType Number)
Definition Array.h:3016
static FLinearColor Interpolate(TArrayView< const FLinearColor *const > Window, float Parameter)
Definition InterpolationPolicies.h:155
static FLinearColor InterpolateWithBasis(TArrayView< const FLinearColor *const > Window, TArrayView< const float > Basis)
Definition InterpolationPolicies.h:166
static FLinearColor EvaluateDerivative(TArrayView< const FLinearColor *const > Window, float Parameter)
Definition InterpolationPolicies.h:189
static FTransform Interpolate(TArrayView< const FTransform *const > Window, float Parameter)
Definition InterpolationPolicies.h:400
static FTransform InterpolateWithBasis(TArrayView< const FTransform *const > Window, TArrayView< const float > Basis)
Definition InterpolationPolicies.h:418
static FTransform EvaluateDerivative(TArrayView< const FTransform *const > Window, float Parameter)
Definition InterpolationPolicies.h:460
static TQuat EvaluateDerivative(TArrayView< const TQuat *const > Window, float Parameter)
Definition InterpolationPolicies.h:347
static TQuat Interpolate(TArrayView< const TQuat *const > Window, float Parameter)
Definition InterpolationPolicies.h:206
static TQuat InterpolateWithBasis(TArrayView< const TQuat *const > Window, TArrayView< const float > Basis)
Definition InterpolationPolicies.h:218
static int32 EvaluateDerivative(TArrayView< const int32 *const > Window, float Parameter)
Definition InterpolationPolicies.h:140
static int32 Interpolate(TArrayView< const int32 *const > Window, float Parameter)
Definition InterpolationPolicies.h:108
static int32 InterpolateWithBasis(TArrayView< const int32 *const > Window, TArrayView< const float > Basis)
Definition InterpolationPolicies.h:128
Definition InterpolationPolicies.h:40
static T Interpolate(TArrayView< const T *const > Window, float Parameter)
Definition InterpolationPolicies.h:43
static T EvaluateDerivative(TArrayView< const T *const > Window, float Parameter)
Definition InterpolationPolicies.h:83
static T InterpolateWithBasis(TArrayView< const T *const > Window, TArrayView< const float > Basis)
Definition InterpolationPolicies.h:61
Definition AdvancedWidgetsModule.cpp:13
U16 Index
Definition radfft.cpp:71
Definition Color.h:48
static CORE_API const FLinearColor Black
Definition Color.h:458
static CORE_API FLinearColor LerpUsingHSV(const FLinearColor &From, const FLinearColor &To, const float Progress)
Definition Color.cpp:445
FColor ToFColor(const bool bSRGB) const
Definition Color.h:810
static UE_FORCEINLINE_HINT bool IsNearlyEqual(float A, float B, float ErrorTolerance=UE_SMALL_NUMBER)
Definition UnrealMathUtility.h:388
static constexpr UE_FORCEINLINE_HINT T Lerp(const T &A, const T &B, const U &Alpha)
Definition UnrealMathUtility.h:1116
static constexpr UE_FORCEINLINE_HINT T Clamp(const T X, const T MinValue, const T MaxValue)
Definition UnrealMathUtility.h:592
static UE_FORCEINLINE_HINT bool IsNearlyZero(float Value, float ErrorTolerance=UE_SMALL_NUMBER)
Definition UnrealMathUtility.h:407
static T Compute(TArrayView< const T *const > Window, float Parameter)
Definition SplineMath.h:460
Definition InterpolationPolicies.h:500
static T Compute(TArrayView< const T *const > Window, float Parameter)
Definition InterpolationPolicies.h:501
Definition InterpolationPolicies.h:26
Definition Quat.h:39
T W
Definition Quat.h:58
static UE_FORCEINLINE_HINT TQuat< T > Slerp(const TQuat< T > &Quat1, const TQuat< T > &Quat2, T Slerp)
Definition Quat.h:660
T Y
Definition Quat.h:52
T Z
Definition Quat.h:55
static CORE_API const TQuat< T > Identity
Definition Quat.h:63
T X
Definition Quat.h:49
static CORE_API const TTransform< double > Identity
Definition TransformNonVectorized.h:58
static CORE_API const TVector< double > ZeroVector
Definition Vector.h:79