Skip to content

Commit

Permalink
fix for unexpected triggered events
Browse files Browse the repository at this point in the history
fixes #7226
This PR fixes reports of events when animations are playing backward.
It considers both the timeline speed and the state machine state speed to determine direction.
It also handles not reporting an event twice if it comes from a "ping pong" loop.
It also adds a small performance improvement, skipping the binary search if possible.

Diffs=
265c00985 fix for unexpected triggered events (#7227)
8a538c243 validating core objects property keys on load (#7298)
cd6210f42 Make ContourMeasure more robust (#7294)
405b8ef90 use world bounds for coarse grained collision test (#7287)
3734d9bac initialize seed with chrono (#7285)
45b998e68 Simple libjpeg (#7277)

Co-authored-by: hernan <hernan@rive.app>
  • Loading branch information
bodymovin and bodymovin committed May 22, 2024
1 parent 2a20c30 commit 23f0ac6
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 22 deletions.
2 changes: 1 addition & 1 deletion .rive_head
Original file line number Diff line number Diff line change
@@ -1 +1 @@
67ed7133f0fa1f390afa1829b074fa8c686fd648
265c009852e86492deb69277ad18aa2c394c93e5
4 changes: 2 additions & 2 deletions lib/src/rive_core/animation/keyed_object.dart
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ class KeyedObject extends KeyedObjectBase<RuntimeArtboard> {
double secondsFrom,
double secondsTo, {
required KeyedCallbackReporter reporter,
int secondsFromExactOffset = 1,
bool isAtStartFrame = false,
}) {
for (final keyedProperty
in _keyedProperties.values.where((property) => property.isCallback)) {
Expand All @@ -80,7 +80,7 @@ class KeyedObject extends KeyedObjectBase<RuntimeArtboard> {
secondsFrom,
secondsTo,
reporter: reporter,
secondsFromExactOffset: secondsFromExactOffset,
isAtStartFrame: isAtStartFrame,
);
}
}
Expand Down
31 changes: 24 additions & 7 deletions lib/src/rive_core/animation/keyed_property.dart
Original file line number Diff line number Diff line change
Expand Up @@ -115,14 +115,18 @@ class KeyedProperty extends KeyedPropertyBase<RuntimeArtboard>
KeyFrame getFrameAt(int index) => _keyframes[index];

int closestFrameIndex(double seconds, {int exactOffset = 0}) {
int idx = 0;
// Binary find the keyframe index (use timeInSeconds here as opposed to the
// finder above which operates in frames).
int mid = 0;
double closestSeconds = 0;
int start = 0;
int end = _keyframes.length - 1;

// If it's the last keyframe, we skip the binary search
if (seconds > _keyframes[end].seconds) {
return end + 1;
}

while (start <= end) {
mid = (start + end) >> 1;
closestSeconds = _keyframes[mid].seconds;
Expand All @@ -133,9 +137,8 @@ class KeyedProperty extends KeyedPropertyBase<RuntimeArtboard>
} else {
return mid + exactOffset;
}
idx = start;
}
return idx;
return start;
}

bool get isCallback => RiveCoreContext.isCallback(propertyKey);
Expand All @@ -146,11 +149,25 @@ class KeyedProperty extends KeyedPropertyBase<RuntimeArtboard>
double secondsFrom,
double secondsTo, {
required KeyedCallbackReporter reporter,
int secondsFromExactOffset = 1,
bool isAtStartFrame = false,
}) {
int idx =
closestFrameIndex(secondsFrom, exactOffset: secondsFromExactOffset);
int idxTo = closestFrameIndex(secondsTo, exactOffset: 1);
if (secondsFrom == secondsTo) {
return;
}
bool isForward = secondsFrom <= secondsTo;
int fromExactOffset = 0;
int toExactOffset = isForward ? 1 : 0;
if (isForward) {
if (!isAtStartFrame) {
fromExactOffset = 1;
}
} else {
if (isAtStartFrame) {
fromExactOffset = 1;
}
}
int idx = closestFrameIndex(secondsFrom, exactOffset: fromExactOffset);
int idxTo = closestFrameIndex(secondsTo, exactOffset: toExactOffset);

// going backwards?
if (idxTo < idx) {
Expand Down
27 changes: 16 additions & 11 deletions lib/src/rive_core/animation/linear_animation.dart
Original file line number Diff line number Diff line change
Expand Up @@ -77,18 +77,23 @@ class LinearAnimation extends LinearAnimationBase {
double secondsFrom,
double secondsTo, {
required KeyedCallbackReporter reporter,
int speedDirection = 1,
bool fromPong = false,
}) {
int secondsFromExactOffset = startTime == secondsFrom &&
(speed >= 0 ? secondsFrom < secondsTo : secondsFrom < secondsTo)
? 0
: 1;
for (final keyedObject in _keyedObjects.values) {
keyedObject.reportKeyedCallbacks(
secondsFrom,
secondsTo,
reporter: reporter,
secondsFromExactOffset: secondsFromExactOffset,
);
// We have to account for the state machine speed multiplier and the speed
double startingTime =
((speed * speedDirection) >= 0) ? startSeconds : endSeconds;
bool isAtStartFrame = startingTime == secondsFrom;

if (!isAtStartFrame || !fromPong) {
for (final keyedObject in _keyedObjects.values) {
keyedObject.reportKeyedCallbacks(
secondsFrom,
secondsTo,
reporter: reporter,
isAtStartFrame: isAtStartFrame,
);
}
}
}

Expand Down
12 changes: 11 additions & 1 deletion lib/src/rive_core/animation/linear_animation_instance.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import 'package:rive/src/rive_core/animation/loop.dart';

class LinearAnimationInstance {
final LinearAnimation animation;
final int _speedDirection;

double _time = 0;
double _totalTime = 0;
Expand All @@ -20,7 +21,8 @@ class LinearAnimationInstance {

LinearAnimationInstance(this.animation, {double speedMultiplier = 1.0})
: _time =
(speedMultiplier >= 0) ? animation.startTime : animation.endTime;
(speedMultiplier >= 0) ? animation.startTime : animation.endTime,
_speedDirection = (speedMultiplier >= 0) ? 1 : -1;

/// NOTE: that when time is set, the direction will be changed to 1
set time(double value) {
Expand Down Expand Up @@ -107,6 +109,7 @@ class LinearAnimationInstance {
lastTime,
_time,
reporter: callbackReporter,
speedDirection: _speedDirection,
);
}

Expand Down Expand Up @@ -146,6 +149,7 @@ class LinearAnimationInstance {
lastTime,
_time,
reporter: callbackReporter,
speedDirection: _speedDirection,
);
}
didLoop = true;
Expand All @@ -160,13 +164,16 @@ class LinearAnimationInstance {
lastTime,
_time,
reporter: callbackReporter,
speedDirection: _speedDirection,
);
}
didLoop = true;
}
break;
case Loop.pingPong:
// ignore: literal_only_boolean_expressions
// In ping-pong we only want to report callbacks once per side
bool fromPong = true;
while (true) {
if (direction == 1 && frames >= end) {
_spilledTime = (frames - end) / animation.fps;
Expand All @@ -193,8 +200,11 @@ class LinearAnimationInstance {
lastTime,
_time,
reporter: callbackReporter,
speedDirection: _speedDirection,
fromPong: fromPong,
);
}
fromPong = !fromPong;
}
break;
}
Expand Down

0 comments on commit 23f0ac6

Please sign in to comment.