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.
132 lines
3.4 KiB
132 lines
3.4 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 <cmath> |
|
|
|
#import "POPAnimationExtras.h" |
|
#import "POPPropertyAnimationInternal.h" |
|
|
|
struct _POPSpringAnimationState : _POPPropertyAnimationState |
|
{ |
|
SpringSolver4d *solver; |
|
CGFloat springSpeed; |
|
CGFloat springBounciness; // normalized springiness |
|
CGFloat dynamicsTension; // tension |
|
CGFloat dynamicsFriction; // friction |
|
CGFloat dynamicsMass; // mass |
|
|
|
_POPSpringAnimationState(id __unsafe_unretained anim) : _POPPropertyAnimationState(anim), |
|
solver(nullptr), |
|
springSpeed(12.), |
|
springBounciness(4.), |
|
dynamicsTension(0), |
|
dynamicsFriction(0), |
|
dynamicsMass(0) |
|
{ |
|
type = kPOPAnimationSpring; |
|
} |
|
|
|
bool hasConverged() |
|
{ |
|
NSUInteger count = valueCount; |
|
if (shouldRound()) { |
|
return vec_equal(previous2Vec, previousVec) && vec_equal(previousVec, toVec); |
|
} else { |
|
if (!previousVec || !previous2Vec) |
|
return false; |
|
|
|
CGFloat t = dynamicsThreshold / 5; |
|
|
|
const CGFloat *toValues = toVec->data(); |
|
const CGFloat *previousValues = previousVec->data(); |
|
const CGFloat *previous2Values = previous2Vec->data(); |
|
|
|
for (NSUInteger idx = 0; idx < count; idx++) { |
|
if ((std::abs(toValues[idx] - previousValues[idx]) >= t) || (std::abs(previous2Values[idx] - previousValues[idx]) >= t)) { |
|
return false; |
|
} |
|
} |
|
return true; |
|
} |
|
} |
|
|
|
bool isDone() { |
|
if (_POPPropertyAnimationState::isDone()) { |
|
return true; |
|
} |
|
return solver->started() && (hasConverged() || solver->hasConverged()); |
|
} |
|
|
|
void updatedDynamics() |
|
{ |
|
if (NULL != solver) { |
|
solver->setConstants(dynamicsTension, dynamicsFriction, dynamicsMass); |
|
} |
|
} |
|
|
|
void updatedDynamicsThreshold() |
|
{ |
|
_POPPropertyAnimationState::updatedDynamicsThreshold(); |
|
if (NULL != solver) { |
|
solver->setThreshold(dynamicsThreshold); |
|
} |
|
} |
|
|
|
void updatedBouncinessAndSpeed() { |
|
[POPSpringAnimation convertBounciness:springBounciness speed:springSpeed toTension:&dynamicsTension friction:&dynamicsFriction mass:&dynamicsMass]; |
|
updatedDynamics(); |
|
} |
|
|
|
bool advance(CFTimeInterval time, CFTimeInterval dt, id obj) { |
|
// advance past not yet initialized animations |
|
if (NULL == currentVec) { |
|
return false; |
|
} |
|
|
|
CFTimeInterval localTime = time - startTime; |
|
|
|
Vector4d value = vector4d(currentVec); |
|
Vector4d toValue = vector4d(toVec); |
|
Vector4d velocity = vector4d(velocityVec); |
|
|
|
SSState4d state; |
|
state.p = toValue - value; |
|
|
|
// the solver assumes a spring of size zero |
|
// flip the velocity from user perspective to solver perspective |
|
state.v = velocity * -1; |
|
|
|
solver->advance(state, localTime, dt); |
|
value = toValue - state.p; |
|
|
|
// flip velocity back to user perspective |
|
velocity = state.v * -1; |
|
|
|
*currentVec = value; |
|
|
|
if (velocityVec) { |
|
*velocityVec = velocity; |
|
} |
|
|
|
clampCurrentValue(); |
|
|
|
return true; |
|
} |
|
|
|
virtual void reset(bool all) { |
|
_POPPropertyAnimationState::reset(all); |
|
|
|
if (solver) { |
|
solver->setConstants(dynamicsTension, dynamicsFriction, dynamicsMass); |
|
solver->reset(); |
|
} |
|
} |
|
}; |
|
|
|
typedef struct _POPSpringAnimationState POPSpringAnimationState;
|
|
|