RFR: 8237975: Non-embedded Animations do not play backwards after being paused

Nir Lisker nlisker at openjdk.java.net
Wed Jan 29 22:54:28 UTC 2020


On Wed, 29 Jan 2020 20:47:11 GMT, Kevin Rushforth <kcr at openjdk.org> wrote:

>> [8236858](https://bugs.openjdk.java.net/browse/JDK-8236858) (Animations do not play backwards after being paused) has been split to deal with [embedded](https://bugs.openjdk.java.net/browse/JDK-8237974) and [not embedded](https://bugs.openjdk.java.net/browse/JDK-8237975) animations. This is a fix for the latter.
>> The reason for the split is that embedded animations have a much more complex behavior. The current state of the relation between an animation and its clip envelope is already messy and should be corrected, even more so for embedded animations whose parent controls their behavior as well (sometimes in conflict with the child's clip envelope). This will require a redesign which can be discussed for 15. See the parent issue [8210238](https://bugs.openjdk.java.net/browse/JDK-8210238) for the list of bugs that arise from it.
>> 
>> This simple fix allows to change the current rate of a `ClipEnvelope` also when the animations is `PAUSED`. A possible issue with this approach is that it changes the buggy behavior of embedded animations to a different buggy behavior.
>> 
>> A concept test has been added, but it does not work yet since the mock clip envelope does not have sufficient behavior (`doTimePulse` does not actually do a time pulse). Open for ideas on how to make it simple, otherwise I will add a method to set a clip envelope and create a new one ad-hoc.
> 
> modules/javafx.graphics/src/main/java/com/sun/scenario/animation/shared/FiniteClipEnvelope.java line 85:
> 
>> 84:         if (status != Status.STOPPED) {
>> 85:             setInternalCurrentRate((Math.abs(currentRate - this.rate) < EPSILON) ? rate : -rate);
>> 86:             deltaTicks = newTicks - Math.round((ticks - deltaTicks) * Math.abs(rate / this.rate));
> 
> What are the implications of not calling the existing `setCurrentRate` method for animations that are in the `RUNNING` state? It means that `Animation::setCurrentRate` will no longer be called in that case. Should it be?
> 
> Independent of your change, I can't figure out why the existing logic works today, even for animations that are running. The expression `(Math.abs(currentRate - this.rate) < EPSILON) ? rate : -rate` will evaluate either to `rate` or `-rate` depending on whether `currentRate` and `this.rate` are closer to each other than epsilon, irrespective of whether either (or both) of `this.rate` or `currentRate` are positive or negative. I feel I must be missing something.

> What are the implications of not calling the existing `setCurrentRate` method for animations that are in the `RUNNING` state? It means that `Animation::setCurrentRate` will no longer be called in that case. Should it be?

The situation is complicated (to put it politely), as you've realized. `currentRate` is actually being set from `Animation#setRate` first, then `ClipEnvelope.setRate` sets it again. I initially removed `currentRate` from `ClipEnvelope` altogether, but that broke embedded animations whose rate is being set by their parent through their `ClipEnvelope` (doing it through `Animation` will throw an exception). So, instead, I added a "backdoor" for the parent to change its child's `ClipEnvelope.currentRate` without changing its `Animation.currentRate`.

The result is that non-embedded animations now work correctly and embedded animations are still broken, but in a slightly different way. Their `Animation.currentRate` is now always 1 instead of either 1 or -1, but this is incorrect anyway if the rate is not 1 or -1.

> Independent of your change, I can't figure out why the existing logic works today, even for animations that are running. The expression `(Math.abs(currentRate - this.rate) < EPSILON) ? rate : -rate` will evaluate either to `rate` or `-rate` depending on whether `currentRate` and `this.rate` are closer to each other than epsilon, irrespective of whether either (or both) of `this.rate` or `currentRate` are positive or negative. I feel I must be missing something.

There's a lot I couldn't figure out in time for 14, especially with regard to embedded animations (have a look at `ParallelTransition.doPlayTo`).

For non-embedded animations, If the animation is `RUNNING`, then:
* If the animation is during a forward cycle then `currentRate == this.rate`, so the current rate is set to the new rate.
* If the animation is during a reverse cycle, then `currentRate == -this.rate`, so the current rate is set to the negative of the new rate (to accommodate the reverse cycle's rule of `currentRate == -rate`).

However, this check is already done in `Animation.rate`'s `invalidate` method, where the cryptic `Math.abs(currentRate - this.rate) < EPSILON` is at least given the name `playingForward`:
if (getStatus() == Status.RUNNING) {                                             
    final double currentRate = getCurrentRate();                                 
    if (Math.abs(currentRate) < EPSILON) {                                       
        doSetCurrentRate(lastPlayedForward ? newRate : -newRate);                
        resumeReceiver();                                                        
    } else {                                                                     
        final boolean playingForward = Math.abs(currentRate - oldRate) < EPSILON; // HERE
        doSetCurrentRate(playingForward ? newRate : -newRate);  // AND HERE
    }                                                                            
}                                                                                
oldRate = newRate;                                                               

For embedded animations this is even worse, since there is an additional rate property kept in the parent animations (`rates[i]`), so the rate is kept in 3 places...

Overall, the animation area will require a soft redesign. That might weed out the bugs in bulk. The more I was working to find a fix the more bugs I found. I turned [JDK-8210238](https://bugs.openjdk.java.net/browse/JDK-8210238) to an umbrella issue to keep track.

-------------

PR: https://git.openjdk.java.net/jfx/pull/98


More information about the openjfx-dev mailing list