// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreTypes.h" #include "Misc/FrameNumber.h" #include "Misc/FrameTime.h" #include "Misc/FrameRate.h" #include "Misc/TVariant.h" /** * This file contains structures that represent specific interpolation algorithms for either a continuous or discrete range. * * They are used by Sequencer evaluation to bypass expensive piecewise data searching every frame by caching the resulting * interpolation over the relevant time range which allows it to perform only the minimum computation required to find a result. * * FCachedInterpolation is a variant type that can represent any one of the supported interpolation modes in this file. */ namespace UE::MovieScene::Interpolation { struct FInvalidValue; struct FConstantValue; struct FLinearInterpolation; struct FQuadraticInterpolation; struct FCubicInterpolation; struct FQuarticInterpolation; struct FCubicBezierInterpolation; struct FWeightedCubicInterpolation; /** * Template structure used for representing an array of solutions with a minimum size * * For example: * // An array with at least 3 elements, varifyable at compile-time * int32 Solve3(TInterpSolutions Solutions) * { * Solutions[0] = 0.0; * Solutions[1] = 1.0; * Solutions[2] = 2.0; * return 3; * } * int32 Solve4(TInterpSolutions Solutions); * * double Solutions[8]; * Solve3(Solutions); * Solve4(Solutions); */ template struct TInterpSolutions { /** * Construction from a c-style array with a size of at least MinN */ template TInterpSolutions(T (&In)[N]) requires (N >= MinN) : Array(In) {} /** * Conversion to another array of size <= this */ template operator TInterpSolutions() const requires (OtherN <= MinN) { return TInterpSolutions(Array); } /** * Access the nth element of this array */ T& operator[](int Index) const { check(Index friend struct TInterpSolutions; explicit TInterpSolutions(T* InArray) : Array(InArray) {} T* Array; }; /** * Structure that represents the extents of a curve in y */ struct FInterpolationExtents { double MinValue = std::numeric_limits::max(); double MaxValue = std::numeric_limits::lowest(); FFrameTime MinValueTime; FFrameTime MaxValueTime; MOVIESCENE_API bool IsValid() const; MOVIESCENE_API void AddPoint(double Value, FFrameTime Time); MOVIESCENE_API void Combine(const FInterpolationExtents& Other); }; /** * Sentinal type that represents an invalid interpolation value. * Only used when a curve has no data whatsoever, and therefore cannot be evaluated */ struct FInvalidValue { }; /** * Structure representing a constant value. * Temporarily includes a flag to determine whether it needs to be re-evaluated for legacy fallback. */ struct FConstantValue { /** The constant value */ double Value; FFrameNumber Origin; UE_DEPRECATED(5.5, "Please provide an origin. This is required for Integral() to work correctly.") FConstantValue(double InValue) : FConstantValue(0, InValue) {} FConstantValue(FFrameNumber InOrigin, double InValue) : Value(InValue) , Origin(InOrigin) {} MOVIESCENE_API FConstantValue Derivative() const; MOVIESCENE_API FLinearInterpolation Integral(double ConstantOffset = 0.0) const; }; /** * Structure representing a linear interpolation of the form f(t) = a(t-o) + b. */ struct FLinearInterpolation { /** The coeffient 'a' in f(t) = a(t-o) + b */ double Coefficient; /** The constant 'b' in f(t) = a(t-o) + b */ double Constant; /** The origin 'o' in f(t) = a(t-o) + b */ FFrameNumber Origin; FLinearInterpolation(FFrameNumber InOrigin, double InCoefficient, double InConstant) : Coefficient(InCoefficient) , Constant(InConstant) , Origin(InOrigin) {} /** * Evaluate the expression */ MOVIESCENE_API double Evaluate(FFrameTime InTime) const; /** * Attempt to solve this interpolation for x */ MOVIESCENE_API int32 Solve(double Value, TInterpSolutions OutResults) const; /** * Attempt to solve this interpolation for x within limits */ MOVIESCENE_API int32 SolveWithin(FFrameTime Start, FFrameTime End, double Value, TInterpSolutions OutResults) const; /** * Compute this expression's derivative */ MOVIESCENE_API FConstantValue Derivative() const; /** * Compute this expression's integral with an optional constant offset */ MOVIESCENE_API FQuadraticInterpolation Integral(double ConstantOffset = 0.0) const; }; /** * Structure representing a quadratic interpolation of the form f(x) = g(x-o) and g(x) = ax^2 + bx + c. */ struct FQuadraticInterpolation { /** The coeffients a and b in g(x) = ax^2 + bx + c */ double A, B; /** The constant 'c' in g(x) = ax^2 + bx + c */ double Constant; /** The origin 'o' in f(x) = g(x-o) */ FFrameNumber Origin; FQuadraticInterpolation(FFrameNumber InOrigin, double InA, double InB, double InConstant) : A(InA), B(InB) , Constant(InConstant) , Origin(InOrigin) {} /** * Evaluate the expression */ MOVIESCENE_API double Evaluate(FFrameTime InTime) const; /** * Attempt to solve this interpolation for x */ MOVIESCENE_API int32 Solve(double Value, TInterpSolutions OutResults) const; /** * Attempt to solve this interpolation for x within limits */ MOVIESCENE_API int32 SolveWithin(FFrameTime Start, FFrameTime End, double Value, TInterpSolutions OutResults) const; /** * Compute this expression's derivative */ MOVIESCENE_API FLinearInterpolation Derivative() const; /** * Compute this expression's integral with an optional constant offset */ MOVIESCENE_API FCubicInterpolation Integral(double ConstantOffset = 0.0) const; }; /** * Structure representing a cubic interpolation of the form f(x) = g(x-o) and g(x) = ax^3 + bx^2 + cx + d. */ struct FCubicInterpolation { /** The coeffients a, b, and c in g(x) = ax^3 + bx^2 + cx + d */ double A, B, C; /** The constant 'd' in g(x) = ax^3 + bx^2 + cx + d */ double Constant; double DX; /** The origin 'o' in f(x) = g(x-o) */ FFrameNumber Origin; FCubicInterpolation(FFrameNumber InOrigin, double InA, double InB, double InC, double InConstant, double InDX = 1.0) : A(InA), B(InB), C(InC) , Constant(InConstant) , DX(InDX) , Origin(InOrigin) {} /** * Evaluate the expression */ MOVIESCENE_API double Evaluate(FFrameTime InTime) const; /** * Attempt to solve this interpolation for x */ MOVIESCENE_API int32 Solve(double Value, TInterpSolutions OutResults) const; /** * Attempt to solve this interpolation for x within limits */ MOVIESCENE_API int32 SolveWithin(FFrameTime Start, FFrameTime End, double Value, TInterpSolutions OutResults) const; /** * Compute this expression's derivative */ MOVIESCENE_API FQuadraticInterpolation Derivative() const; /** * Compute this expression's integral with an optional constant offset */ MOVIESCENE_API FQuarticInterpolation Integral(double ConstantOffset = 0.0) const; }; /** * Structure representing a quartic interpolation of the form f(x) = g(x-o) and g(x) = ax^4 + bx^3 + cx^2 + dx + e. */ struct FQuarticInterpolation { /** The coeffients a, b, c, and d in g(x) = ax^4 + bx^3 + cx^2 + dx + e */ double A, B, C, D; /** The constant 'e' in g(x) = ax^4 + bx^3 + cx^2 + dx + e */ double Constant; double DX; /** The origin 'o' in f(x) = g(x-o) */ FFrameNumber Origin; FQuarticInterpolation(FFrameNumber InOrigin, double InA, double InB, double InC, double InD, double InConstant, double InDX = 1.0) : A(InA), B(InB), C(InC), D(InD) , Constant(InConstant) , DX(InDX) , Origin(InOrigin) {} /** * Evaluate the expression */ MOVIESCENE_API double Evaluate(FFrameTime InTime) const; /** * Attempt to solve this interpolation for x */ MOVIESCENE_API int32 Solve(double Value, TInterpSolutions OutResults) const; /** * Attempt to solve this interpolation for x within limits */ MOVIESCENE_API int32 SolveWithin(FFrameTime Start, FFrameTime End, double Value, TInterpSolutions OutResults) const; /** * Compute this expression's derivative */ MOVIESCENE_API FCubicInterpolation Derivative() const; }; /** * A cubic bezier interpolation between 2 control points with tangents, represented as 4 control points on a Bezier curve */ struct FCubicBezierInterpolation { /** The delta value between the two control points in the time-domain */ double DX; /** The four control points that should be passed to BezierInterp */ double P0, P1, P2, P3; /** The origin time of the first control point */ FFrameNumber Origin; MOVIESCENE_API FCubicBezierInterpolation( FFrameNumber InOrigin, double InDX, double InStartValue, double InEndValue, double InStartTangent, double InEndTangent); MOVIESCENE_API FCubicInterpolation AsCubic() const; /** * Evaluate the expression */ MOVIESCENE_API double Evaluate(FFrameTime InTime) const; /** * Attempt to solve this interpolation for x */ MOVIESCENE_API int32 Solve(double InValue, TInterpSolutions OutResults) const; /** * Compute this expression's derivative */ MOVIESCENE_API FQuadraticInterpolation Derivative() const; /** * Compute this expression's integral with an optional constant offset */ MOVIESCENE_API FQuarticInterpolation Integral(double ConstantOffset = 0.0) const; }; /** * A weighted cubic bezier interpolation between 2 control points with weighted tangents */ struct FWeightedCubicInterpolation { double DX; double StartKeyValue; double NormalizedStartTanDX; double StartKeyTanY; double StartWeight; double EndKeyValue; double NormalizedEndTanDX; double EndKeyTanY; double EndWeight; FFrameNumber Origin; FWeightedCubicInterpolation( FFrameRate TickResolution, FFrameNumber InOrigin, FFrameNumber StartTime, double StartValue, double StartTangent, double StartTangentWeight, bool bStartIsWeighted, FFrameNumber EndTime, double EndValue, double EndTangent, double EndTangentWeight, bool bEndIsWeighted); double Evaluate(FFrameTime InTime) const; /** * Attempt to solve this interpolation for x */ MOVIESCENE_API int32 Solve(double Value, TInterpSolutions OutResults) const; }; /** * Simple 1 dimensional range based off a FFrameNumber to define the range within which a cached interpolation is valid */ struct FCachedInterpolationRange { /** Make an empty range */ static MOVIESCENE_API FCachedInterpolationRange Empty(); /** Make finite range from InStart to InEnd, not including the end frame */ static MOVIESCENE_API FCachedInterpolationRange Finite(FFrameNumber InStart, FFrameNumber InEnd); /** Make an infinite range */ static MOVIESCENE_API FCachedInterpolationRange Infinite(); /** Make a range that only contains the specified time */ static MOVIESCENE_API FCachedInterpolationRange Only(FFrameNumber InTime); /** Make a range that covers all times from (and including) the specified start */ static MOVIESCENE_API FCachedInterpolationRange From(FFrameNumber InStart); /** Make a range that covers all times up to (but not including) the specified end */ static MOVIESCENE_API FCachedInterpolationRange Until(FFrameNumber InEnd); FFrameNumber Clamp(FFrameNumber In) const { return FMath::Clamp( In.Value, Start.Value, End.Value); } FFrameTime Clamp(FFrameTime In) const { return FMath::Clamp( In, FFrameTime(Start), FFrameTime(End)); } MOVIESCENE_API bool Contains(FFrameNumber FrameNumber) const; MOVIESCENE_API bool IsEmpty() const; /** Inclusive start frame */ FFrameNumber Start; /** Exclusive end frame (unless End == Max()) */ FFrameNumber End; }; /** * Variant structure that wraps an interpolation and the range within which it is valid. * ~96 bytes */ struct FCachedInterpolation { /** Default construction to an invalid state. Calling Evaluate will always return false */ MOVIESCENE_API FCachedInterpolation(); /** Construction as a constant value */ MOVIESCENE_API FCachedInterpolation(const FCachedInterpolationRange& InRange, const FConstantValue& Constant); /** Construction as a linear interpolation */ MOVIESCENE_API FCachedInterpolation(const FCachedInterpolationRange& InRange, const FLinearInterpolation& Linear); /** Construction as a quadratic interpolation */ MOVIESCENE_API FCachedInterpolation(const FCachedInterpolationRange& InRange, const FQuadraticInterpolation& Quadratic); /** Construction as a cubic interpolation */ MOVIESCENE_API FCachedInterpolation(const FCachedInterpolationRange& InRange, const FCubicInterpolation& Cubic); /** Construction as a quartic interpolation */ MOVIESCENE_API FCachedInterpolation(const FCachedInterpolationRange& InRange, const FQuarticInterpolation& Quartic); /** Construction as a cubic interpolation */ MOVIESCENE_API FCachedInterpolation(const FCachedInterpolationRange& InRange, const FCubicBezierInterpolation& CubicBezier); /** Construction as a weighted cubic interpolation */ MOVIESCENE_API FCachedInterpolation(const FCachedInterpolationRange& InRange, const FWeightedCubicInterpolation& WeightedCubic); /** * Check whether this cache is valid (ie, has a valid range and interpolation) */ MOVIESCENE_API bool IsValid() const; /** * Check whether this cache is still valid for the specified frame number. * @return true if this interpolation is relevant to the frame, or false if it should be re-generated */ MOVIESCENE_API bool IsCacheValidForTime(FFrameNumber FrameNumber) const; /** * Retrieve the range that this interpolation applies to */ MOVIESCENE_API FCachedInterpolationRange GetRange() const; /** * Evaluate this interpolation for the specified frame time * * @param FrameTime The time to evaluate at * @param OutResult Reference to recieve the resulting value. Only written to if this function returns true. * @return true if this interpolation evaluated successfully (and the result written to OutResult), false otherwise. */ MOVIESCENE_API bool Evaluate(FFrameTime FrameTime, double& OutResult) const; MOVIESCENE_API TOptional ComputeIntegral(double ConstantOffset = 0.0) const; MOVIESCENE_API TOptional ComputeDerivative() const; MOVIESCENE_API int32 InverseEvaluate(double InValue, TInterpSolutions OutResults) const; MOVIESCENE_API FInterpolationExtents ComputeExtents(FFrameTime From, FFrameTime To) const; MOVIESCENE_API FInterpolationExtents ComputeExtents() const; MOVIESCENE_API void Offset(double Amount); private: /** Variant containing the actual interpolation implementation */ TVariant Data; /** Structure representint the range of times this interpolation applies to */ FCachedInterpolationRange Range; }; } // namespace UE::MovieScene