< prev index next >

modules/graphics/src/main/java/javafx/animation/Animation.java

Print this page


   1 /*
   2  * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any


 166         if (paused) {
 167             final long deltaTime = now() - pauseTime;
 168             startTime += deltaTime;
 169             paused = false;
 170             addPulseReceiver();
 171         }
 172     }
 173 
 174     // package private only for the sake of testing
 175     final PulseReceiver pulseReceiver = new PulseReceiver() {
 176         @Override public void timePulse(long now) {
 177             final long elapsedTime = now - startTime;
 178             if (elapsedTime < 0) {
 179                 return;
 180             }
 181             if (accessCtrlCtx == null) {
 182                 throw new IllegalStateException("Error: AccessControlContext not captured");
 183             }
 184 
 185             AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
 186                 impl_timePulse(elapsedTime);
 187                 return null;
 188             }, accessCtrlCtx);
 189         }
 190     };
 191 
 192     private class CurrentRateProperty extends ReadOnlyDoublePropertyBase {
 193         private double value;
 194 
 195         @Override
 196         public Object getBean() {
 197             return Animation.this;
 198         }
 199 
 200         @Override
 201         public String getName() {
 202             return "currentRate";
 203         }
 204 
 205         @Override
 206         public double get() {


 294 
 295     public final DoubleProperty rateProperty() {
 296         if (rate == null) {
 297             rate = new DoublePropertyBase(DEFAULT_RATE) {
 298 
 299                 @Override
 300                 public void invalidated() {
 301                     final double newRate = getRate();
 302                     if (isRunningEmbedded()) {
 303                         if (isBound()) {
 304                             unbind();
 305                         }
 306                         set(oldRate);
 307                         throw new IllegalArgumentException("Cannot set rate of embedded animation while running.");
 308                     } else {
 309                         if (Math.abs(newRate) < EPSILON) {
 310                             if (getStatus() == Status.RUNNING) {
 311                                 lastPlayedForward = (Math.abs(getCurrentRate()
 312                                         - oldRate) < EPSILON);
 313                             }
 314                             setCurrentRate(0.0);
 315                             pauseReceiver();
 316                         } else {
 317                             if (getStatus() == Status.RUNNING) {
 318                                 final double currentRate = getCurrentRate();
 319                                 if (Math.abs(currentRate) < EPSILON) {
 320                                     setCurrentRate(lastPlayedForward ? newRate : -newRate);
 321                                     resumeReceiver();
 322                                 } else {
 323                                     final boolean playingForward = Math.abs(currentRate - oldRate) < EPSILON;
 324                                     setCurrentRate(playingForward ? newRate : -newRate);
 325                                 }
 326                             }
 327                             oldRate = newRate;
 328                         }
 329                         clipEnvelope.setRate(newRate);
 330                     }
 331                 }
 332 
 333                 @Override
 334                 public Object getBean() {
 335                     return Animation.this;
 336                 }
 337 
 338                 @Override
 339                 public String getName() {
 340                     return "rate";
 341                 }
 342             };
 343         }
 344         return rate;


 349             return false;
 350         }
 351         return parent.getStatus() != Status.STOPPED || parent.isRunningEmbedded();
 352     }
 353 
 354     private double oldRate = 1.0;
 355     /**
 356      * Read-only variable to indicate current direction/speed at which the
 357      * {@code Animation} is being played.
 358      * <p>
 359      * {@code currentRate} is not necessary equal to {@code rate}.
 360      * {@code currentRate} is set to {@code 0.0} when animation is paused or
 361      * stopped. {@code currentRate} may also point to different direction during
 362      * reverse cycles when {@code autoReverse} is {@code true}
 363      *
 364      * @defaultValue 0.0
 365      */
 366     private ReadOnlyDoubleProperty currentRate;
 367     private static final double DEFAULT_CURRENT_RATE = 0.0;
 368 
 369     private void setCurrentRate(double value) {
 370         if ((currentRate != null) || (Math.abs(value - DEFAULT_CURRENT_RATE) > EPSILON)) {
 371             ((CurrentRateProperty)currentRateProperty()).set(value);
 372         }
 373     }
 374 
 375     public final double getCurrentRate() {
 376         return (currentRate == null)? DEFAULT_CURRENT_RATE : currentRate.get();
 377     }
 378 
 379     public final ReadOnlyDoubleProperty currentRateProperty() {
 380         if (currentRate == null) {
 381             currentRate = new CurrentRateProperty();
 382         }
 383         return currentRate;
 384     }
 385 
 386     /**
 387      * Read-only variable to indicate the duration of one cycle of this
 388      * {@code Animation}: the time it takes to play from time 0 to the
 389      * end of the Animation (at the default {@code rate} of


 887      * <code>
 888      *  animation.setRate(negative rate);<br>
 889      *  animation.jumpTo(overall duration of animation);<br>
 890      *  animation.play();<br>
 891      * </code>
 892      * <p>
 893      * Note: <ul>
 894      * <li>{@code play()} is an asynchronous call, the {@code Animation} may not
 895      * start immediately. </ul>
 896      *
 897      * @throws IllegalStateException
 898      *             if embedded in another animation,
 899      *                such as {@link SequentialTransition} or {@link ParallelTransition}
 900      */
 901     public void play() {
 902         if (parent != null) {
 903             throw new IllegalStateException("Cannot start when embedded in another animation");
 904         }
 905         switch (getStatus()) {
 906             case STOPPED:
 907                 if (impl_startable(true)) {
 908                     final double rate = getRate();
 909                     if (lastPlayedFinished) {
 910                         jumpTo((rate < 0)? getTotalDuration() : Duration.ZERO);
 911                     }
 912                     impl_start(true);
 913                     startReceiver(TickCalculation.fromDuration(getDelay()));
 914                     if (Math.abs(rate) < EPSILON) {
 915                         pauseReceiver();
 916                     } else {
 917 
 918                     }
 919                 } else {
 920                     final EventHandler<ActionEvent> handler = getOnFinished();
 921                     if (handler != null) {
 922                         handler.handle(new ActionEvent(this, null));
 923                     }
 924                 }
 925                 break;
 926             case PAUSED:
 927                 impl_resume();
 928                 if (Math.abs(getRate()) >= EPSILON) {
 929                     resumeReceiver();
 930                 }
 931                 break;
 932         }
 933     }
 934 
 935     /**
 936      * Plays an {@code Animation} from initial position in forward direction.
 937      * <p>
 938      * It is equivalent to
 939      * <p>
 940      * <code>
 941      *      animation.stop();<br>
 942      *      animation.setRate = setRate(Math.abs(animation.getRate())); </br>
 943      *      animation.jumpTo(Duration.ZERO);<br>
 944      *      animation.play();<br>
 945      *  </code>
 946      *
 947      * <p>


 961         play();
 962     }
 963 
 964     /**
 965      * Stops the animation and resets the play head to its initial position. If
 966      * the animation is not currently running, this method has no effect.
 967      * <p>
 968      * Note: <ul>
 969      * <li>{@code stop()} is an asynchronous call, the {@code Animation} may not stop
 970      * immediately. </ul>
 971      * @throws IllegalStateException
 972      *             if embedded in another animation,
 973      *                such as {@link SequentialTransition} or {@link ParallelTransition}
 974      */
 975     public void stop() {
 976         if (parent != null) {
 977             throw new IllegalStateException("Cannot stop when embedded in another animation");
 978         }
 979         if (getStatus() != Status.STOPPED) {
 980             clipEnvelope.abortCurrentPulse();
 981             impl_stop();
 982             jumpTo(Duration.ZERO);
 983         }
 984     }
 985 
 986     /**
 987      * Pauses the animation. If the animation is not currently running, this
 988      * method has no effect.
 989      * <p>
 990      * Note: <ul>
 991      * <li>{@code pause()} is an asynchronous call, the {@code Animation} may not pause
 992      * immediately. </ul>
 993      * @throws IllegalStateException
 994      *             if embedded in another animation,
 995      *                such as {@link SequentialTransition} or {@link ParallelTransition}
 996      */
 997     public void pause() {
 998         if (parent != null) {
 999             throw new IllegalStateException("Cannot pause when embedded in another animation");
1000         }
1001         if (getStatus() == Status.RUNNING) {
1002             clipEnvelope.abortCurrentPulse();
1003             pauseReceiver();
1004             impl_pause();
1005         }
1006     }
1007 
1008     /**
1009      * The constructor of {@code Animation}.
1010      *
1011      * This constructor allows to define a target framerate.
1012      *
1013      * @param targetFramerate
1014      *            The custom target frame rate for this {@code Animation}
1015      * @see #getTargetFramerate()
1016      */
1017     protected Animation(double targetFramerate) {
1018         this.targetFramerate = targetFramerate;
1019         this.resolution = (int) Math.max(1, Math.round(TickCalculation.TICKS_PER_SECOND / targetFramerate));
1020         this.clipEnvelope = ClipEnvelope.create(this);
1021         this.timer = Toolkit.getToolkit().getMasterTimer();
1022     }
1023 
1024     /**


1030         this.clipEnvelope = ClipEnvelope.create(this);
1031         this.timer = Toolkit.getToolkit().getMasterTimer();
1032     }
1033 
1034     // These constructors are only for testing purposes
1035     Animation(AbstractMasterTimer timer) {
1036         this.resolution = 1;
1037         this.targetFramerate = TickCalculation.TICKS_PER_SECOND / timer.getDefaultResolution();
1038         this.clipEnvelope = ClipEnvelope.create(this);
1039         this.timer = timer;
1040     }
1041 
1042     // These constructors are only for testing purposes
1043     Animation(AbstractMasterTimer timer, ClipEnvelope clipEnvelope, int resolution) {
1044         this.resolution = resolution;
1045         this.targetFramerate = TickCalculation.TICKS_PER_SECOND / resolution;
1046         this.clipEnvelope = clipEnvelope;
1047         this.timer = timer;
1048     }
1049 
1050     boolean impl_startable(boolean forceSync) {
1051         return (fromDuration(getCycleDuration()) > 0L)
1052                 || (!forceSync && clipEnvelope.wasSynched());
1053     }
1054 
1055     void impl_sync(boolean forceSync) {
1056         if (forceSync || !clipEnvelope.wasSynched()) {
1057             syncClipEnvelope();
1058         }
1059     }
1060 
1061     private void syncClipEnvelope() {
1062         final int publicCycleCount = getCycleCount();
1063         final int internalCycleCount = (publicCycleCount <= 0)
1064                 && (publicCycleCount != INDEFINITE) ? 1 : publicCycleCount;
1065         clipEnvelope = clipEnvelope.setCycleCount(internalCycleCount);
1066         clipEnvelope.setCycleDuration(getCycleDuration());
1067         clipEnvelope.setAutoReverse(isAutoReverse());
1068     }
1069 
1070     void impl_start(boolean forceSync) {
1071         impl_sync(forceSync);
1072         setStatus(Status.RUNNING);
1073         clipEnvelope.start();
1074         setCurrentRate(clipEnvelope.getCurrentRate());
1075         lastPulse = 0;
1076     }
1077 
1078     void impl_pause() {
1079         final double currentRate = getCurrentRate();
1080         if (Math.abs(currentRate) >= EPSILON) {
1081             lastPlayedForward = Math.abs(getCurrentRate() - getRate()) < EPSILON;
1082         }
1083         setCurrentRate(0.0);
1084         setStatus(Status.PAUSED);
1085     }
1086 
1087     void impl_resume() {
1088         setStatus(Status.RUNNING);
1089         setCurrentRate(lastPlayedForward ? getRate() : -getRate());
1090     }
1091 
1092     void impl_stop() {
1093         if (!paused) {
1094             timer.removePulseReceiver(pulseReceiver);
1095         }
1096         setStatus(Status.STOPPED);
1097         setCurrentRate(0.0);
1098     }
1099 
1100     void impl_timePulse(long elapsedTime) {
1101         if (resolution == 1) { // fullspeed
1102             clipEnvelope.timePulse(elapsedTime);
1103         } else if (elapsedTime - lastPulse >= resolution) {
1104             lastPulse = (elapsedTime / resolution) * resolution;
1105             clipEnvelope.timePulse(elapsedTime);
1106         }
1107     }
1108 
1109     abstract void impl_playTo(long currentTicks, long cycleTicks);
1110 
1111     abstract void impl_jumpTo(long currentTicks, long cycleTicks, boolean forceJump);
1112 
1113     void impl_setCurrentTicks(long ticks) {
1114         currentTicks = ticks;
1115         if (currentTime != null) {
1116             currentTime.fireValueChangedEvent();
1117         }
1118     }
1119 
1120     void impl_setCurrentRate(double currentRate) {
1121 //        if (getStatus() == Status.RUNNING) {
1122             setCurrentRate(currentRate);
1123 //        }
1124     }
1125 
1126     final void impl_finished() {
1127         lastPlayedFinished = true;
1128         impl_stop();
1129         final EventHandler<ActionEvent> handler = getOnFinished();
1130         if (handler != null) {
1131             try {
1132                 handler.handle(new ActionEvent(this, null));
1133             } catch (Exception ex) {
1134                 Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), ex);
1135             }
1136         }
1137     }
1138 }
   1 /*
   2  * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any


 166         if (paused) {
 167             final long deltaTime = now() - pauseTime;
 168             startTime += deltaTime;
 169             paused = false;
 170             addPulseReceiver();
 171         }
 172     }
 173 
 174     // package private only for the sake of testing
 175     final PulseReceiver pulseReceiver = new PulseReceiver() {
 176         @Override public void timePulse(long now) {
 177             final long elapsedTime = now - startTime;
 178             if (elapsedTime < 0) {
 179                 return;
 180             }
 181             if (accessCtrlCtx == null) {
 182                 throw new IllegalStateException("Error: AccessControlContext not captured");
 183             }
 184 
 185             AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
 186                 doTimePulse(elapsedTime);
 187                 return null;
 188             }, accessCtrlCtx);
 189         }
 190     };
 191 
 192     private class CurrentRateProperty extends ReadOnlyDoublePropertyBase {
 193         private double value;
 194 
 195         @Override
 196         public Object getBean() {
 197             return Animation.this;
 198         }
 199 
 200         @Override
 201         public String getName() {
 202             return "currentRate";
 203         }
 204 
 205         @Override
 206         public double get() {


 294 
 295     public final DoubleProperty rateProperty() {
 296         if (rate == null) {
 297             rate = new DoublePropertyBase(DEFAULT_RATE) {
 298 
 299                 @Override
 300                 public void invalidated() {
 301                     final double newRate = getRate();
 302                     if (isRunningEmbedded()) {
 303                         if (isBound()) {
 304                             unbind();
 305                         }
 306                         set(oldRate);
 307                         throw new IllegalArgumentException("Cannot set rate of embedded animation while running.");
 308                     } else {
 309                         if (Math.abs(newRate) < EPSILON) {
 310                             if (getStatus() == Status.RUNNING) {
 311                                 lastPlayedForward = (Math.abs(getCurrentRate()
 312                                         - oldRate) < EPSILON);
 313                             }
 314                             doSetCurrentRate(0.0);
 315                             pauseReceiver();
 316                         } else {
 317                             if (getStatus() == Status.RUNNING) {
 318                                 final double currentRate = getCurrentRate();
 319                                 if (Math.abs(currentRate) < EPSILON) {
 320                                     doSetCurrentRate(lastPlayedForward ? newRate : -newRate);
 321                                     resumeReceiver();
 322                                 } else {
 323                                     final boolean playingForward = Math.abs(currentRate - oldRate) < EPSILON;
 324                                     doSetCurrentRate(playingForward ? newRate : -newRate);
 325                                 }
 326                             }
 327                             oldRate = newRate;
 328                         }
 329                         clipEnvelope.setRate(newRate);
 330                     }
 331                 }
 332 
 333                 @Override
 334                 public Object getBean() {
 335                     return Animation.this;
 336                 }
 337 
 338                 @Override
 339                 public String getName() {
 340                     return "rate";
 341                 }
 342             };
 343         }
 344         return rate;


 349             return false;
 350         }
 351         return parent.getStatus() != Status.STOPPED || parent.isRunningEmbedded();
 352     }
 353 
 354     private double oldRate = 1.0;
 355     /**
 356      * Read-only variable to indicate current direction/speed at which the
 357      * {@code Animation} is being played.
 358      * <p>
 359      * {@code currentRate} is not necessary equal to {@code rate}.
 360      * {@code currentRate} is set to {@code 0.0} when animation is paused or
 361      * stopped. {@code currentRate} may also point to different direction during
 362      * reverse cycles when {@code autoReverse} is {@code true}
 363      *
 364      * @defaultValue 0.0
 365      */
 366     private ReadOnlyDoubleProperty currentRate;
 367     private static final double DEFAULT_CURRENT_RATE = 0.0;
 368 
 369     private void doSetCurrentRate(double value) {
 370         if ((currentRate != null) || (Math.abs(value - DEFAULT_CURRENT_RATE) > EPSILON)) {
 371             ((CurrentRateProperty)currentRateProperty()).set(value);
 372         }
 373     }
 374 
 375     public final double getCurrentRate() {
 376         return (currentRate == null)? DEFAULT_CURRENT_RATE : currentRate.get();
 377     }
 378 
 379     public final ReadOnlyDoubleProperty currentRateProperty() {
 380         if (currentRate == null) {
 381             currentRate = new CurrentRateProperty();
 382         }
 383         return currentRate;
 384     }
 385 
 386     /**
 387      * Read-only variable to indicate the duration of one cycle of this
 388      * {@code Animation}: the time it takes to play from time 0 to the
 389      * end of the Animation (at the default {@code rate} of


 887      * <code>
 888      *  animation.setRate(negative rate);<br>
 889      *  animation.jumpTo(overall duration of animation);<br>
 890      *  animation.play();<br>
 891      * </code>
 892      * <p>
 893      * Note: <ul>
 894      * <li>{@code play()} is an asynchronous call, the {@code Animation} may not
 895      * start immediately. </ul>
 896      *
 897      * @throws IllegalStateException
 898      *             if embedded in another animation,
 899      *                such as {@link SequentialTransition} or {@link ParallelTransition}
 900      */
 901     public void play() {
 902         if (parent != null) {
 903             throw new IllegalStateException("Cannot start when embedded in another animation");
 904         }
 905         switch (getStatus()) {
 906             case STOPPED:
 907                 if (startable(true)) {
 908                     final double rate = getRate();
 909                     if (lastPlayedFinished) {
 910                         jumpTo((rate < 0)? getTotalDuration() : Duration.ZERO);
 911                     }
 912                     doStart(true);
 913                     startReceiver(TickCalculation.fromDuration(getDelay()));
 914                     if (Math.abs(rate) < EPSILON) {
 915                         pauseReceiver();
 916                     } else {
 917 
 918                     }
 919                 } else {
 920                     final EventHandler<ActionEvent> handler = getOnFinished();
 921                     if (handler != null) {
 922                         handler.handle(new ActionEvent(this, null));
 923                     }
 924                 }
 925                 break;
 926             case PAUSED:
 927                 doResume();
 928                 if (Math.abs(getRate()) >= EPSILON) {
 929                     resumeReceiver();
 930                 }
 931                 break;
 932         }
 933     }
 934 
 935     /**
 936      * Plays an {@code Animation} from initial position in forward direction.
 937      * <p>
 938      * It is equivalent to
 939      * <p>
 940      * <code>
 941      *      animation.stop();<br>
 942      *      animation.setRate = setRate(Math.abs(animation.getRate())); </br>
 943      *      animation.jumpTo(Duration.ZERO);<br>
 944      *      animation.play();<br>
 945      *  </code>
 946      *
 947      * <p>


 961         play();
 962     }
 963 
 964     /**
 965      * Stops the animation and resets the play head to its initial position. If
 966      * the animation is not currently running, this method has no effect.
 967      * <p>
 968      * Note: <ul>
 969      * <li>{@code stop()} is an asynchronous call, the {@code Animation} may not stop
 970      * immediately. </ul>
 971      * @throws IllegalStateException
 972      *             if embedded in another animation,
 973      *                such as {@link SequentialTransition} or {@link ParallelTransition}
 974      */
 975     public void stop() {
 976         if (parent != null) {
 977             throw new IllegalStateException("Cannot stop when embedded in another animation");
 978         }
 979         if (getStatus() != Status.STOPPED) {
 980             clipEnvelope.abortCurrentPulse();
 981             doStop();
 982             jumpTo(Duration.ZERO);
 983         }
 984     }
 985 
 986     /**
 987      * Pauses the animation. If the animation is not currently running, this
 988      * method has no effect.
 989      * <p>
 990      * Note: <ul>
 991      * <li>{@code pause()} is an asynchronous call, the {@code Animation} may not pause
 992      * immediately. </ul>
 993      * @throws IllegalStateException
 994      *             if embedded in another animation,
 995      *                such as {@link SequentialTransition} or {@link ParallelTransition}
 996      */
 997     public void pause() {
 998         if (parent != null) {
 999             throw new IllegalStateException("Cannot pause when embedded in another animation");
1000         }
1001         if (getStatus() == Status.RUNNING) {
1002             clipEnvelope.abortCurrentPulse();
1003             pauseReceiver();
1004             doPause();
1005         }
1006     }
1007 
1008     /**
1009      * The constructor of {@code Animation}.
1010      *
1011      * This constructor allows to define a target framerate.
1012      *
1013      * @param targetFramerate
1014      *            The custom target frame rate for this {@code Animation}
1015      * @see #getTargetFramerate()
1016      */
1017     protected Animation(double targetFramerate) {
1018         this.targetFramerate = targetFramerate;
1019         this.resolution = (int) Math.max(1, Math.round(TickCalculation.TICKS_PER_SECOND / targetFramerate));
1020         this.clipEnvelope = ClipEnvelope.create(this);
1021         this.timer = Toolkit.getToolkit().getMasterTimer();
1022     }
1023 
1024     /**


1030         this.clipEnvelope = ClipEnvelope.create(this);
1031         this.timer = Toolkit.getToolkit().getMasterTimer();
1032     }
1033 
1034     // These constructors are only for testing purposes
1035     Animation(AbstractMasterTimer timer) {
1036         this.resolution = 1;
1037         this.targetFramerate = TickCalculation.TICKS_PER_SECOND / timer.getDefaultResolution();
1038         this.clipEnvelope = ClipEnvelope.create(this);
1039         this.timer = timer;
1040     }
1041 
1042     // These constructors are only for testing purposes
1043     Animation(AbstractMasterTimer timer, ClipEnvelope clipEnvelope, int resolution) {
1044         this.resolution = resolution;
1045         this.targetFramerate = TickCalculation.TICKS_PER_SECOND / resolution;
1046         this.clipEnvelope = clipEnvelope;
1047         this.timer = timer;
1048     }
1049 
1050     boolean startable(boolean forceSync) {
1051         return (fromDuration(getCycleDuration()) > 0L)
1052                 || (!forceSync && clipEnvelope.wasSynched());
1053     }
1054 
1055     void sync(boolean forceSync) {
1056         if (forceSync || !clipEnvelope.wasSynched()) {
1057             syncClipEnvelope();
1058         }
1059     }
1060 
1061     private void syncClipEnvelope() {
1062         final int publicCycleCount = getCycleCount();
1063         final int internalCycleCount = (publicCycleCount <= 0)
1064                 && (publicCycleCount != INDEFINITE) ? 1 : publicCycleCount;
1065         clipEnvelope = clipEnvelope.setCycleCount(internalCycleCount);
1066         clipEnvelope.setCycleDuration(getCycleDuration());
1067         clipEnvelope.setAutoReverse(isAutoReverse());
1068     }
1069 
1070     void doStart(boolean forceSync) {
1071         sync(forceSync);
1072         setStatus(Status.RUNNING);
1073         clipEnvelope.start();
1074         doSetCurrentRate(clipEnvelope.getCurrentRate());
1075         lastPulse = 0;
1076     }
1077 
1078     void doPause() {
1079         final double currentRate = getCurrentRate();
1080         if (Math.abs(currentRate) >= EPSILON) {
1081             lastPlayedForward = Math.abs(getCurrentRate() - getRate()) < EPSILON;
1082         }
1083         doSetCurrentRate(0.0);
1084         setStatus(Status.PAUSED);
1085     }
1086 
1087     void doResume() {
1088         setStatus(Status.RUNNING);
1089         doSetCurrentRate(lastPlayedForward ? getRate() : -getRate());
1090     }
1091 
1092     void doStop() {
1093         if (!paused) {
1094             timer.removePulseReceiver(pulseReceiver);
1095         }
1096         setStatus(Status.STOPPED);
1097         doSetCurrentRate(0.0);
1098     }
1099 
1100     void doTimePulse(long elapsedTime) {
1101         if (resolution == 1) { // fullspeed
1102             clipEnvelope.timePulse(elapsedTime);
1103         } else if (elapsedTime - lastPulse >= resolution) {
1104             lastPulse = (elapsedTime / resolution) * resolution;
1105             clipEnvelope.timePulse(elapsedTime);
1106         }
1107     }
1108 
1109     abstract void doPlayTo(long currentTicks, long cycleTicks);
1110 
1111     abstract void doJumpTo(long currentTicks, long cycleTicks, boolean forceJump);
1112 
1113     void setCurrentTicks(long ticks) {
1114         currentTicks = ticks;
1115         if (currentTime != null) {
1116             currentTime.fireValueChangedEvent();
1117         }
1118     }
1119 
1120     void setCurrentRate(double currentRate) {
1121 //        if (getStatus() == Status.RUNNING) {
1122             doSetCurrentRate(currentRate);
1123 //        }
1124     }
1125 
1126     final void finished() {
1127         lastPlayedFinished = true;
1128         doStop();
1129         final EventHandler<ActionEvent> handler = getOnFinished();
1130         if (handler != null) {
1131             try {
1132                 handler.handle(new ActionEvent(this, null));
1133             } catch (Exception ex) {
1134                 Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), ex);
1135             }
1136         }
1137     }
1138 }
< prev index next >