// Copyright Epic Games, Inc. All Rights Reserved. #include "CurveModel.h" #include "Containers/Array.h" #include "CurveDataAbstraction.h" #include "Curves/KeyHandle.h" #include "HAL/PlatformCrt.h" #include "Math/NumericLimits.h" void FCurveModel::GetAllKeys(TArray& OutKeyHandles) const { GetKeys( TNumericLimits::Lowest(), TNumericLimits::Max(), TNumericLimits::Lowest(), TNumericLimits::Max(), OutKeyHandles ); } void FCurveModel::PutKeys( TConstArrayView InKeys, TConstArrayView InKeyPositions, TConstArrayView InAttributes ) { const int32 Num = InKeys.Num(); if (ensure(InKeyPositions.Num() == Num && InAttributes.Num() == Num)) { RemoveKeys(InKeys, 0.0); TArray> OptionalAddedHandles; OptionalAddedHandles.SetNum(InKeys.Num()); TArrayView> NewKeyHandlesView = MakeArrayView(OptionalAddedHandles); AddKeys(InKeyPositions, InAttributes, &NewKeyHandlesView); TArray AddedKeyHandles; Algo::TransformIf(OptionalAddedHandles, AddedKeyHandles, [](const TOptional& Handle){ return Handle.IsSet(); }, [](const TOptional& Handle){ return *Handle; } ); if (ensure(AddedKeyHandles.Num() == Num)) { ReplaceKeyHandles(AddedKeyHandles, InKeys); } } } void FCurveModel::GetClosestKeysTo(double InTime, TOptional& OutPreviousKeyHandle, TOptional& OutNextKeyHandle) const { // Default implementation does linear search. Subclasses can override this with a more efficient implementations. double MinTime, MaxTime; GetTimeRange(MinTime, MaxTime); double MinValue, MaxValue; GetValueRange(MinValue, MaxValue); TArray AllHandles; GetKeys(MinTime, MaxTime, MinValue, MaxValue, AllHandles); if (AllHandles.IsEmpty()) { return; } TArray AllPositions; AllPositions.SetNumUninitialized(AllHandles.Num()); GetKeyPositions(AllHandles, AllPositions); // Ideally, we'd use binary search. // However, GetKeyPositions documentation does not dictate that the function returns a sorted array. // For now, this implementation does linear because it's safe. double CurrentClosestLeftTime = MinTime - 1.0; // We'll assume we don't underflow double range double CurrentClosestRightTime = MaxTime + 1.0; // We'll assume we don't overflow double range for (int32 Index = 0; Index < AllPositions.Num(); ++Index) { const FKeyHandle& KeyHandle = AllHandles[Index]; const double CurrentTime = AllPositions[Index].InputValue; if (CurrentTime <= InTime && CurrentClosestLeftTime < CurrentTime) { CurrentClosestLeftTime = CurrentTime; OutPreviousKeyHandle = KeyHandle; } if (CurrentTime >= InTime && CurrentClosestRightTime > CurrentTime) { CurrentClosestRightTime = CurrentTime; OutNextKeyHandle = KeyHandle; } } } void FCurveModel::SetKeyAttributes(TArrayView InKeys, const FKeyAttributes& InKeyAttributes, EPropertyChangeType::Type ChangeType) { TArray ExpandedAttributes; ExpandedAttributes.Reserve(InKeys.Num()); for (FKeyHandle Handle : InKeys) { ExpandedAttributes.Add(InKeyAttributes); } SetKeyAttributes(InKeys, ExpandedAttributes); } TOptional FCurveModel::AddKey(const FKeyPosition& NewKeyPosition, const FKeyAttributes& InAttributes) { TOptional Handle; TArrayView> Handles = MakeArrayView(&Handle, 1); AddKeys(TArrayView(&NewKeyPosition, 1), TArrayView(&InAttributes, 1), &Handles); return Handle; } void FCurveModel::OpenChangeScope() { ++ChangeScopeCounter; if (ChangeScopeCounter == 1) { OnOpenRootChangeScope(); } } void FCurveModel::CloseChangeScope() { if (!ensure(ChangeScopeCounter > 0)) { return; } --ChangeScopeCounter; if (ChangeScopeCounter == 0) { OnCloseRootChangeScope(); } }