diff --git a/pop-tests/POPDecayAnimationTests.mm b/pop-tests/POPDecayAnimationTests.mm index 03c9690c..e98fb415 100644 --- a/pop-tests/POPDecayAnimationTests.mm +++ b/pop-tests/POPDecayAnimationTests.mm @@ -459,4 +459,29 @@ - (void)testEndValueOnReuse STAssertTrue(lastValue > toValue, @"unexpected last value:%f"); } +- (void)testComputedProperties +{ + POPDecayAnimation *anim = [POPDecayAnimation animationWithPropertyNamed:kPOPLayerPositionX]; + + // set velocity, test duration + anim.velocity = @(100); + CGFloat d1 = anim.duration; + STAssertTrue(d1 > 0, @"unexpected duration %@", anim); + + // set velocity, test duration + anim.velocity = @(1000); + CGFloat d2 = anim.duration; + STAssertTrue(d2 > d1, @"unexpected duration %@", anim); + + // set from value, test to value + anim.fromValue = @(0); + CGFloat p1 = [anim.toValue floatValue]; + STAssertTrue(p1 > [anim.fromValue floatValue], @"unexpected to value %@", anim); + + // set from value, test to value + anim.fromValue = @(10000); + CGFloat p2 = [anim.toValue floatValue]; + STAssertTrue(p2 > [anim.fromValue floatValue] && p2 > p1, @"unexpected to value %@", anim); +} + @end diff --git a/pop/POPDecayAnimation.mm b/pop/POPDecayAnimation.mm index 2a0bbc75..87007668 100644 --- a/pop/POPDecayAnimation.mm +++ b/pop/POPDecayAnimation.mm @@ -99,7 +99,8 @@ - (void)setVelocity:(id)aValue - (void)_ensureComputedProperties { if (NULL == __state->toVec) { - __state->computeDestinationValues(); + __state->computeDuration(); + __state->computeToValue(); } } @@ -113,11 +114,13 @@ - (void)_appendDescription:(NSMutableString *)s debug:(BOOL)debug { [super _appendDescription:s debug:debug]; - if (NULL != __state->toVec) + if (0 != self.duration) { [s appendFormat:@"; duration = %f", self.duration]; + } - if (__state->deceleration) + if (__state->deceleration) { [s appendFormat:@"; deceleration = %f", __state->deceleration]; + } } @end diff --git a/pop/POPDecayAnimationInternal.h b/pop/POPDecayAnimationInternal.h index aca98c43..883d5232 100644 --- a/pop/POPDecayAnimationInternal.h +++ b/pop/POPDecayAnimationInternal.h @@ -64,6 +64,46 @@ struct _POPDecayAnimationState : _POPPropertyAnimationState } + void computeDuration() { + + // compute duration till threshold velocity + Vector4r scaledVelocity = vector4(velocityVec) / 1000.; + + double k = dynamicsThreshold * kPOPAnimationDecayMinimalVelocityFactor / 1000.; + double vx = k / scaledVelocity.x; + double vy = k / scaledVelocity.y; + double vz = k / scaledVelocity.z; + double vw = k / scaledVelocity.w; + double d = log(deceleration) * 1000.; + duration = MAX(MAX(MAX(log(fabs(vx)) / d, log(fabs(vy)) / d), log(fabs(vz)) / d), log(fabs(vw)) / d); + + // ensure velocity threshold is exceeded + if (isnan(duration) || duration < 0) { + duration = 0; + } + } + + void computeToValue() { + // to value assuming final velocity as a factor of dynamics threshold + // derived from v' = v * d^dt used in decay_position + // to compute the to value with maximal dt, p' = p + (v * d) / (1 - d) + VectorRef fromValue = NULL != currentVec ? currentVec : fromVec; + if (!fromValue) { + return; + } + + // ensure duration is computed + if (0 == duration) { + computeDuration(); + } + + // compute to value + VectorRef toValue(Vector::new_vector(fromValue.get())); + Vector4r velocity = velocityVec->vector4r(); + decay_position(toValue->data(), velocity.data(), valueCount, duration, deceleration); + toVec = toValue; + } + void computeDestinationValues() { // to value assuming final velocity as a factor of dynamics threshold // derived from v' = v * d^dt used in decay_position