You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

359 lines
8.8 KiB

/**
Copyright (c) 2014-present, Facebook, Inc.
All rights reserved.
This source code is licensed under the BSD-style license found in the
LICENSE file in the root directory of this source tree. An additional grant
of patent rights can be found in the PATENTS file in the same directory.
*/
#import "POPAnimationInternal.h"
#import "POPPropertyAnimation.h"
static void clampValue(CGFloat &value, CGFloat fromValue, CGFloat toValue, NSUInteger clamp)
{
BOOL increasing = (toValue > fromValue);
// Clamp start of animation.
if ((kPOPAnimationClampStart & clamp) &&
((increasing && (value < fromValue)) || (!increasing && (value > fromValue)))) {
value = fromValue;
}
// Clamp end of animation.
if ((kPOPAnimationClampEnd & clamp) &&
((increasing && (value > toValue)) || (!increasing && (value < toValue)))) {
value = toValue;
}
}
struct _POPPropertyAnimationState : _POPAnimationState
{
POPAnimatableProperty *property;
POPValueType valueType;
NSUInteger valueCount;
VectorRef fromVec;
VectorRef toVec;
VectorRef currentVec;
VectorRef previousVec;
VectorRef previous2Vec;
VectorRef velocityVec;
VectorRef originalVelocityVec;
VectorRef distanceVec;
CGFloat roundingFactor;
NSUInteger clampMode;
NSArray *progressMarkers;
POPProgressMarker *progressMarkerState;
NSUInteger progressMarkerCount;
NSUInteger nextProgressMarkerIdx;
CGFloat dynamicsThreshold;
_POPPropertyAnimationState(id __unsafe_unretained anim) : _POPAnimationState(anim),
property(nil),
valueType((POPValueType)0),
valueCount(0),
fromVec(nullptr),
toVec(nullptr),
currentVec(nullptr),
previousVec(nullptr),
previous2Vec(nullptr),
velocityVec(nullptr),
originalVelocityVec(nullptr),
distanceVec(nullptr),
roundingFactor(0),
clampMode(0),
progressMarkers(nil),
progressMarkerState(nil),
progressMarkerCount(0),
nextProgressMarkerIdx(0),
dynamicsThreshold(0)
{
type = kPOPAnimationBasic;
}
~_POPPropertyAnimationState()
{
if (progressMarkerState) {
free(progressMarkerState);
progressMarkerState = NULL;
}
}
bool canProgress() {
return hasValue();
}
bool shouldRound() {
return 0 != roundingFactor;
}
bool hasValue() {
return 0 != valueCount;
}
bool isDone() {
// inherit done
if (_POPAnimationState::isDone()) {
return true;
}
// consider an animation with no values done
if (!hasValue() && !isCustom()) {
return true;
}
return false;
}
// returns a copy of the currentVec, rounding if needed
VectorRef currentValue() {
VectorRef vec = VectorRef(Vector::new_vector(currentVec.get()));
if (shouldRound()) {
vec->subRound(1 / roundingFactor);
}
return vec;
}
void resetProgressMarkerState()
{
for (NSUInteger idx = 0; idx < progressMarkerCount; idx++)
progressMarkerState[idx].reached = false;
nextProgressMarkerIdx = 0;
}
void updatedProgressMarkers()
{
if (progressMarkerState) {
free(progressMarkerState);
progressMarkerState = NULL;
}
progressMarkerCount = progressMarkers.count;
if (0 != progressMarkerCount) {
progressMarkerState = (POPProgressMarker *)malloc(progressMarkerCount * sizeof(POPProgressMarker));
[progressMarkers enumerateObjectsUsingBlock:^(NSNumber *progressMarker, NSUInteger idx, BOOL *stop) {
progressMarkerState[idx].reached = false;
progressMarkerState[idx].progress = [progressMarker floatValue];
}];
}
nextProgressMarkerIdx = 0;
}
virtual void updatedDynamicsThreshold()
{
dynamicsThreshold = property.threshold;
}
void finalizeProgress()
{
progress = 1.0;
NSUInteger count = valueCount;
VectorRef outVec(Vector::new_vector(count, NULL));
if (outVec && toVec) {
*outVec = *toVec;
}
currentVec = outVec;
clampCurrentValue();
delegateProgress();
}
void computeProgress() {
if (!canProgress()) {
return;
}
static ComputeProgressFunctor<Vector4r> func;
Vector4r v = vector4(currentVec);
Vector4r f = vector4(fromVec);
Vector4r t = vector4(toVec);
progress = func(v, f, t);
}
void delegateProgress() {
if (!canProgress()) {
return;
}
if (delegateDidProgress && progressMarkerState) {
while (nextProgressMarkerIdx < progressMarkerCount) {
if (progress < progressMarkerState[nextProgressMarkerIdx].progress)
break;
if (!progressMarkerState[nextProgressMarkerIdx].reached) {
ActionEnabler enabler;
[delegate pop_animation:self didReachProgress:progressMarkerState[nextProgressMarkerIdx].progress];
progressMarkerState[nextProgressMarkerIdx].reached = true;
}
nextProgressMarkerIdx++;
}
}
if (!didReachToValue) {
bool didReachToValue = false;
if (0 == valueCount) {
didReachToValue = true;
} else {
Vector4r distance = toVec->vector4r();
distance -= currentVec->vector4r();
if (0 == distance.squaredNorm()) {
didReachToValue = true;
} else {
// components
if (distanceVec) {
didReachToValue = true;
const CGFloat *distanceValues = distanceVec->data();
for (NSUInteger idx = 0; idx < valueCount; idx++) {
didReachToValue &= (signbit(distance[idx]) != signbit(distanceValues[idx]));
}
}
}
}
if (didReachToValue) {
handleDidReachToValue();
}
}
}
void handleDidReachToValue() {
didReachToValue = true;
if (delegateDidReachToValue) {
ActionEnabler enabler;
[delegate pop_animationDidReachToValue:self];
}
POPAnimationDidReachToValueBlock block = animationDidReachToValueBlock;
if (block != NULL) {
ActionEnabler enabler;
block(self);
}
if (tracing) {
[tracer didReachToValue:POPBox(currentValue(), valueType, true)];
}
}
void readObjectValue(VectorRef *ptrVec, id obj)
{
// use current object value as from value
pop_animatable_read_block read = property.readBlock;
if (NULL != read) {
Vector4r vec = read_values(read, obj, valueCount);
*ptrVec = VectorRef(Vector::new_vector(valueCount, vec));
if (tracing) {
[tracer readPropertyValue:POPBox(*ptrVec, valueType, true)];
}
}
}
virtual void willRun(bool started, id obj) {
// ensure from value initialized
if (NULL == fromVec) {
readObjectValue(&fromVec, obj);
}
// ensure to value initialized
if (NULL == toVec) {
// compute decay to value
if (kPOPAnimationDecay == type) {
[self toValue];
} else {
// read to value
readObjectValue(&toVec, obj);
}
}
// handle one time value initialization on start
if (started) {
// initialize current vec
if (!currentVec) {
currentVec = VectorRef(Vector::new_vector(valueCount, NULL));
// initialize current value with from value
// only do this on initial creation to avoid overwriting current value
// on paused animation continuation
if (currentVec && fromVec) {
*currentVec = *fromVec;
}
}
// ensure velocity values
if (!velocityVec) {
velocityVec = VectorRef(Vector::new_vector(valueCount, NULL));
}
if (!originalVelocityVec) {
originalVelocityVec = VectorRef(Vector::new_vector(valueCount, NULL));
}
}
// ensure distance value initialized
// depends on current value set on one time start
if (NULL == distanceVec) {
// not yet started animations may not have current value
VectorRef fromVec2 = NULL != currentVec ? currentVec : fromVec;
if (fromVec2 && toVec) {
Vector4r distance = toVec->vector4r();
distance -= fromVec2->vector4r();
if (0 != distance.squaredNorm()) {
distanceVec = VectorRef(Vector::new_vector(valueCount, distance));
}
}
}
}
virtual void reset(bool all) {
_POPAnimationState::reset(all);
if (all) {
currentVec = NULL;
previousVec = NULL;
previous2Vec = NULL;
}
progress = 0;
resetProgressMarkerState();
didReachToValue = false;
distanceVec = NULL;
}
void clampCurrentValue(NSUInteger clamp)
{
if (kPOPAnimationClampNone == clamp)
return;
// Clamp all vector values
CGFloat *currentValues = currentVec->data();
const CGFloat *fromValues = fromVec->data();
const CGFloat *toValues = toVec->data();
for (NSUInteger idx = 0; idx < valueCount; idx++) {
clampValue(currentValues[idx], fromValues[idx], toValues[idx], clamp);
}
}
void clampCurrentValue()
{
clampCurrentValue(clampMode);
}
};
typedef struct _POPPropertyAnimationState POPPropertyAnimationState;
@interface POPPropertyAnimation ()
@end