6#include "Misc/AssertionMacros.h"
7#include "Containers/Array.h"
8#include "Math/UnrealMathUtility.h"
10#include "Math/Vector2D.h"
11#include "Math/Vector.h"
13#include "Math/TwoVectors.h"
14#include "Math/InterpCurvePoint.h"
17
18
19
20
21
48
49
50
51
52
53
54 int32
AddPoint(
const float InVal,
const T &OutVal );
57
58
59
60
61
62
63
64
65 int32
MovePoint( int32 PointIndex,
float NewInVal );
77
78
79
80 T
Eval(
const float InVal,
const T& Default = T(
ForceInit) )
const;
83
84
88
89
93
94
95
96
97
98
102
103
104
105
106
107
108
112
113
114
115
116
120 void AutoSetTangents(
float Tension = 0.0f,
bool bStationaryEndpoints =
true);
128
129
130
131
132
133
134
151
152
161
162
169
170
176
181 int32 i=0;
for( i=0; i<Points.Num() && Points[i].InVal < InVal; i++);
182 Points.InsertUninitialized(i);
183 Points[i] = FInterpCurvePoint< T >(InVal, OutVal);
191 if( PointIndex < 0 || PointIndex >= Points.Num() )
194 const T OutVal = Points[PointIndex].OutVal;
195 const EInterpCurveMode Mode = Points[PointIndex].InterpMode;
196 const T ArriveTan = Points[PointIndex].ArriveTangent;
197 const T LeaveTan = Points[PointIndex].LeaveTangent;
199 Points.RemoveAt(PointIndex);
201 const int32 NewPointIndex = AddPoint( NewInVal, OutVal );
202 Points[NewPointIndex].InterpMode = Mode;
203 Points[NewPointIndex].ArriveTangent = ArriveTan;
204 Points[NewPointIndex].LeaveTangent = LeaveTan;
206 return NewPointIndex;
221 if (Points.Num() == 0)
227 const float LastInKey = Points.Last().InVal;
228 if (InLoopKey > LastInKey)
232 LoopKeyOffset = InLoopKey - LastInKey;
252 const int32 NumPoints = Points.Num();
253 const int32 LastPoint = NumPoints - 1;
255 check(NumPoints > 0);
257 if (InValue < Points[0].InVal)
262 if (InValue >= Points[LastPoint].InVal)
268 int32 MaxIndex = NumPoints;
270 while (MaxIndex - MinIndex > 1)
272 int32 MidIndex = (MinIndex + MaxIndex) / 2;
274 if (Points[MidIndex].InVal <= InValue)
291 const int32 NumPoints = Points.Num();
292 const int32 LastPoint = NumPoints - 1;
301 const int32 Index = GetPointIndexForInputValue(InVal);
306 return Points[0].OutVal;
310 if (Index == LastPoint)
314 return Points[LastPoint].OutVal;
316 else if (InVal >= Points[LastPoint].InVal + LoopKeyOffset)
319 return Points[0].OutVal;
324 check(Index >= 0 && ((bIsLooped && Index < NumPoints) || (!bIsLooped && Index < LastPoint)));
325 const bool bLoopSegment = (bIsLooped && Index == LastPoint);
326 const int32 NextIndex = bLoopSegment ? 0 : (Index + 1);
328 const auto& PrevPoint = Points[Index];
329 const auto& NextPoint = Points[NextIndex];
331 const float Diff = bLoopSegment ? LoopKeyOffset : (NextPoint.InVal - PrevPoint.InVal);
333 if (Diff > 0.0f && PrevPoint.InterpMode != CIM_Constant)
335 const float Alpha = (InVal - PrevPoint.InVal) / Diff;
336 check(Alpha >= 0.0f && Alpha <= 1.0f);
338 if (PrevPoint.InterpMode == CIM_Linear)
340 return FMath::Lerp(PrevPoint.OutVal, NextPoint.OutVal, Alpha);
344 return FMath::CubicInterp(PrevPoint.OutVal, PrevPoint.LeaveTangent * Diff, NextPoint.OutVal, NextPoint.ArriveTangent * Diff, Alpha);
349 return Points[Index].OutVal;
357 const int32 NumPoints = Points.Num();
358 const int32 LastPoint = NumPoints - 1;
367 const int32 Index = GetPointIndexForInputValue(InVal);
372 return Points[0].LeaveTangent;
376 if (Index == LastPoint)
380 return Points[LastPoint].ArriveTangent;
382 else if (InVal >= Points[LastPoint].InVal + LoopKeyOffset)
385 return Points[0].ArriveTangent;
390 check(Index >= 0 && ((bIsLooped && Index < NumPoints) || (!bIsLooped && Index < LastPoint)));
391 const bool bLoopSegment = (bIsLooped && Index == LastPoint);
392 const int32 NextIndex = bLoopSegment ? 0 : (Index + 1);
394 const auto& PrevPoint = Points[Index];
395 const auto& NextPoint = Points[NextIndex];
397 const float Diff = bLoopSegment ? LoopKeyOffset : (NextPoint.InVal - PrevPoint.InVal);
399 if (Diff > 0.0f && PrevPoint.InterpMode != CIM_Constant)
401 if (PrevPoint.InterpMode == CIM_Linear)
403 return (NextPoint.OutVal - PrevPoint.OutVal) / Diff;
407 const float Alpha = (InVal - PrevPoint.InVal) / Diff;
408 check(Alpha >= 0.0f && Alpha <= 1.0f);
410 return FMath::CubicInterpDerivative(PrevPoint.OutVal, PrevPoint.LeaveTangent * Diff, NextPoint.OutVal, NextPoint.ArriveTangent * Diff, Alpha) / Diff;
424 const int32 NumPoints = Points.Num();
425 const int32 LastPoint = NumPoints - 1;
434 const int32 Index = GetPointIndexForInputValue(InVal);
443 if (Index == LastPoint)
445 if (!bIsLooped || (InVal >= Points[LastPoint].InVal + LoopKeyOffset))
452 check(Index >= 0 && ((bIsLooped && Index < NumPoints) || (!bIsLooped && Index < LastPoint)));
453 const bool bLoopSegment = (bIsLooped && Index == LastPoint);
454 const int32 NextIndex = bLoopSegment ? 0 : (Index + 1);
456 const auto& PrevPoint = Points[Index];
457 const auto& NextPoint = Points[NextIndex];
459 const float Diff = bLoopSegment ? LoopKeyOffset : (NextPoint.InVal - PrevPoint.InVal);
461 if (Diff > 0.0f && PrevPoint.InterpMode != CIM_Constant)
463 if (PrevPoint.InterpMode == CIM_Linear)
470 const float Alpha = (InVal - PrevPoint.InVal) / Diff;
471 check(Alpha >= 0.0f && Alpha <= 1.0f);
473 return FMath::CubicInterpSecondDerivative(PrevPoint.OutVal, PrevPoint.LeaveTangent * Diff, NextPoint.OutVal, NextPoint.ArriveTangent * Diff, Alpha) / (Diff * Diff);
488 return InaccurateFindNearest(PointInSpace, OutDistanceSq, OutSegment);
494 const int32 NumPoints = Points.Num();
495 const int32 NumSegments = bIsLooped ? NumPoints : NumPoints - 1;
499 float BestDistanceSq;
500 float BestResult = InaccurateFindNearestOnSegment(PointInSpace, 0, BestDistanceSq);
501 float BestSegment = 0;
502 for (int32 Segment = 1; Segment < NumSegments; ++Segment)
504 float LocalDistanceSq;
505 float LocalResult = InaccurateFindNearestOnSegment(PointInSpace, Segment, LocalDistanceSq);
506 if (LocalDistanceSq < BestDistanceSq)
508 BestDistanceSq = LocalDistanceSq;
509 BestResult = LocalResult;
510 BestSegment = (
float)Segment;
513 OutDistanceSq = BestDistanceSq;
514 OutSegment = BestSegment;
520 OutDistanceSq =
static_cast<
float>((PointInSpace - Points[0].OutVal).SizeSquared());
522 return Points[0].InVal;
532 const int32 NumPoints = Points.Num();
533 const int32 LastPoint = NumPoints - 1;
534 const int32 NextPtIdx = (bIsLooped && PtIdx == LastPoint) ? 0 : (PtIdx + 1);
535 check(PtIdx >= 0 && ((bIsLooped && PtIdx < NumPoints) || (!bIsLooped && PtIdx < LastPoint)));
537 const float NextInVal = (bIsLooped && PtIdx == LastPoint) ? (Points[LastPoint].InVal + LoopKeyOffset) : Points[NextPtIdx].InVal;
539 if (CIM_Constant == Points[PtIdx].InterpMode)
541 const float Distance1 =
static_cast<
float>((Points[PtIdx].OutVal - PointInSpace).SizeSquared());
542 const float Distance2 =
static_cast<
float>((Points[NextPtIdx].OutVal - PointInSpace).SizeSquared());
543 if (Distance1 < Distance2)
545 OutSquaredDistance = Distance1;
546 return Points[PtIdx].InVal;
548 OutSquaredDistance = Distance2;
552 const float Diff = NextInVal - Points[PtIdx].InVal;
553 if (CIM_Linear == Points[PtIdx].InterpMode)
556 const float A =
static_cast<
float>((Points[PtIdx].OutVal - PointInSpace) | (Points[NextPtIdx].OutVal - Points[PtIdx].OutVal));
557 const float B =
static_cast<
float>((Points[NextPtIdx].OutVal - Points[PtIdx].OutVal).SizeSquared());
558 const float V = FMath::Clamp(-A / B, 0.f, 1.f);
559 OutSquaredDistance =
static_cast<
float>((FMath::Lerp(Points[PtIdx].OutVal, Points[NextPtIdx].OutVal, V) - PointInSpace).SizeSquared());
560 return V * Diff + Points[PtIdx].InVal;
564 const int32 PointsChecked = 3;
565 const int32 IterationNum = 3;
566 const float Scale = 0.75;
569 float ValuesT[PointsChecked];
574 T InitialPoints[PointsChecked];
575 InitialPoints[0] = Points[PtIdx].OutVal;
576 InitialPoints[1] = FMath::CubicInterp(Points[PtIdx].OutVal, Points[PtIdx].LeaveTangent * Diff, Points[NextPtIdx].OutVal, Points[NextPtIdx].ArriveTangent * Diff, ValuesT[1]);
577 InitialPoints[2] = Points[NextPtIdx].OutVal;
579 float DistancesSq[PointsChecked];
581 for (int32 point = 0; point < PointsChecked; ++point)
584 T FoundPoint = InitialPoints[point];
585 float LastMove = 1.0f;
586 for (int32 iter = 0; iter < IterationNum; ++iter)
588 const T LastBestTangent = FMath::CubicInterpDerivative(Points[PtIdx].OutVal, Points[PtIdx].LeaveTangent * Diff, Points[NextPtIdx].OutVal, Points[NextPtIdx].ArriveTangent * Diff, ValuesT[point]);
589 const T Delta = (PointInSpace - FoundPoint);
590 float Move =
static_cast<
float>((LastBestTangent | Delta) / LastBestTangent.SizeSquared());
591 Move = FMath::Clamp(Move, -LastMove*Scale, LastMove*Scale);
592 ValuesT[point] += Move;
593 ValuesT[point] = FMath::Clamp(ValuesT[point], 0.0f, 1.0f);
594 LastMove = FMath::Abs(Move);
595 FoundPoint = FMath::CubicInterp(Points[PtIdx].OutVal, Points[PtIdx].LeaveTangent * Diff, Points[NextPtIdx].OutVal, Points[NextPtIdx].ArriveTangent * Diff, ValuesT[point]);
597 DistancesSq[point] =
static_cast<
float>((FoundPoint - PointInSpace).SizeSquared());
598 ValuesT[point] = ValuesT[point] * Diff + Points[PtIdx].InVal;
601 if (DistancesSq[0] <= DistancesSq[1] && DistancesSq[0] <= DistancesSq[2])
603 OutSquaredDistance = DistancesSq[0];
606 if (DistancesSq[1] <= DistancesSq[2])
608 OutSquaredDistance = DistancesSq[1];
611 OutSquaredDistance = DistancesSq[2];
620 const int32 NumPoints = Points.Num();
621 const int32 LastPoint = NumPoints - 1;
624 for (int32 PointIndex = 0; PointIndex < NumPoints; PointIndex++)
626 const int32 PrevIndex = (PointIndex == 0) ? (bIsLooped ? LastPoint : 0) : (PointIndex - 1);
627 const int32 NextIndex = (PointIndex == LastPoint) ? (bIsLooped ? 0 : LastPoint) : (PointIndex + 1);
629 auto& ThisPoint = Points[PointIndex];
630 const auto& PrevPoint = Points[PrevIndex];
631 const auto& NextPoint = Points[NextIndex];
633 if (ThisPoint.InterpMode == CIM_CurveAuto || ThisPoint.InterpMode == CIM_CurveAutoClamped)
635 if (bStationaryEndpoints && (PointIndex == 0 || (PointIndex == LastPoint && !bIsLooped)))
638 ThisPoint.ArriveTangent = T(ForceInit);
639 ThisPoint.LeaveTangent = T(ForceInit);
641 else if (PrevPoint.IsCurveKey())
643 const bool bWantClamping = (ThisPoint.InterpMode == CIM_CurveAutoClamped);
646 const float PrevTime = (bIsLooped && PointIndex == 0) ? (ThisPoint.InVal - LoopKeyOffset) : PrevPoint.InVal;
647 const float NextTime = (bIsLooped && PointIndex == LastPoint) ? (ThisPoint.InVal + LoopKeyOffset) : NextPoint.InVal;
660 ThisPoint.ArriveTangent = Tangent;
661 ThisPoint.LeaveTangent = Tangent;
666 ThisPoint.ArriveTangent = PrevPoint.ArriveTangent;
667 ThisPoint.LeaveTangent = PrevPoint.LeaveTangent;
670 else if (ThisPoint.InterpMode == CIM_Linear)
672 T Tangent = NextPoint.OutVal - ThisPoint.OutVal;
673 ThisPoint.ArriveTangent = Tangent;
674 ThisPoint.LeaveTangent = Tangent;
676 else if (ThisPoint.InterpMode == CIM_Constant)
678 ThisPoint.ArriveTangent = T(ForceInit);
679 ThisPoint.LeaveTangent = T(ForceInit);
688 const int32 NumPoints = Points.Num();
695 else if (NumPoints == 1)
697 OutMin = Points[0].OutVal;
698 OutMax = Points[0].OutVal;
702 OutMin = Points[0].OutVal;
703 OutMax = Points[0].OutVal;
705 const int32 NumSegments = bIsLooped ? NumPoints : (NumPoints - 1);
707 for (int32 Index = 0; Index < NumSegments; Index++)
709 const int32 NextIndex = (Index == NumPoints - 1) ? 0 : (Index + 1);
710 CurveFindIntervalBounds(Points[Index], Points[NextIndex], OutMin, OutMax, 0.0f);
718
720#define DEFINE_INTERPCURVE_WRAPPER_STRUCT(Name, ElementType)
721 struct Name : FInterpCurve<ElementType>
724 typedef FInterpCurve<ElementType> Super;
732 Name(const Super& Other)
739 struct TIsBitwiseConstructible<Name, FInterpCurve<ElementType>>
741 enum { Value = true };
745 struct TIsBitwiseConstructible<FInterpCurve<ElementType>, Name>
747 enum { Value = true };
#define DEFINE_INTERPCURVE_WRAPPER_STRUCT(Name, ElementType)
void SetLoopKey(float InLoopKey)
int32 GetPointIndexForInputValue(const float InValue) const
void CalcBounds(T &OutMin, T &OutMax, const T &Default=T(ForceInit)) const
float InaccurateFindNearest(const T &PointInSpace, float &OutDistanceSq) const
T Eval(const float InVal, const T &Default=T(ForceInit)) const
friend bool operator==(const FInterpCurve &Curve1, const FInterpCurve &Curve2)
friend bool operator!=(const FInterpCurve &Curve1, const FInterpCurve &Curve2)
TArray< FInterpCurvePoint< T > > Points
float InaccurateFindNearest(const T &PointInSpace, float &OutDistanceSq, float &OutSegment) const
T EvalDerivative(const float InVal, const T &Default=T(ForceInit)) const
T EvalSecondDerivative(const float InVal, const T &Default=T(ForceInit)) const
float InaccurateFindNearestOnSegment(const T &PointInSpace, int32 PtIdx, float &OutSquaredDistance) const
int32 MovePoint(int32 PointIndex, float NewInVal)
int32 AddPoint(const float InVal, const T &OutVal)
void AutoSetTangents(float Tension=0.0f, bool bStationaryEndpoints=true)