Spec-Zone .ru
спецификации, руководства, описания, API
001/*
002 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.  Oracle designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Oracle in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
022 * or visit www.oracle.com if you need additional information or have any
023 * questions.
024 */
025
026package javafx.animation;
027
028import javafx.util.Duration;
029
030import com.sun.scenario.animation.NumberTangentInterpolator;
031import com.sun.scenario.animation.SplineInterpolator;
032
033/**
034 * The abstract class defines several {@code interpolate} methods, which are
035 * used to calculate interpolated values. Various built-in implementations of
036 * this class are offered. Applications may choose to implement their own
037 * {@code Interpolator} to get custom interpolation behavior.
038 * <p>
039 * A custom {@code Interpolator} has to be defined in terms of a "
040 * {@link #curve(double) curve()}".
041 */
042public abstract class Interpolator {
043
044    private static final double EPSILON = 1e-12;
045
046    /**
047     * The constructor of {@code Interpolator}.
048     */
049    protected Interpolator() {
050    }
051
052    /**
053     * Built-in interpolator that provides discrete time interpolation. The
054     * return value of {@code interpolate()} is {@code endValue} only when the
055     * input {@code fraction} is 1.0, and {@code startValue} otherwise.
056     */
057    public static final Interpolator DISCRETE = new Interpolator() {
058        @Override
059        protected double curve(double t) {
060            return (Math.abs(t - 1.0) < EPSILON) ? 1.0 : 0.0;
061        }
062
063        @Override
064        public String toString() {
065            return "Interpolator.DISCRETE";
066        }
067    };
068
069    /**
070     * Built-in interpolator that provides linear time interpolation. The return
071     * value of {@code interpolate()} is {@code startValue} + ({@code endValue}
072     * - {@code startValue}) * {@code fraction}.
073     */
074    public static final Interpolator LINEAR = new Interpolator() {
075        @Override
076        protected double curve(double t) {
077            return t;
078        }
079
080        @Override
081        public String toString() {
082            return "Interpolator.LINEAR";
083        }
084    };
085
086    /*
087     * Easing is calculated with the following algorithm (taken from SMIL 3.0
088     * specs). The result is clamped because of possible rounding errors.
089     * 
090     * double runRate = 1.0 / (1.0 - acceleration/2.0 - deceleration/2.0); if
091     * (fraction < acceleration) { double averageRunRate = runRate * (fraction /
092     * acceleration) / 2; fraction *= averageRunRate; } else if (fraction > (1.0
093     * - deceleration)) { // time spent in deceleration portion double tdec =
094     * fraction - (1.0 - deceleration); // proportion of tdec to total
095     * deceleration time double pdec = tdec / deceleration; fraction = runRate *
096     * (1.0 - ( acceleration / 2) - deceleration + tdec * (2 - pdec) / 2); }
097     * else { fraction = runRate * (fraction - (acceleration / 2)); }
098     */
099
100    /**
101     * Built-in interpolator instance that provides ease in/out behavior.
102     * <p>
103     * An ease-both interpolator will make an animation start slow, then
104     * accelerate and slow down again towards the end, all in a smooth manner.
105     * <p>
106     * The implementation uses the algorithm for easing defined in SMIL 3.0
107     * with an acceleration and deceleration factor of 0.2, respectively.
108     */
109    public static final Interpolator EASE_BOTH = new Interpolator() {
110        @Override
111        protected double curve(double t) {
112            // See the SMIL 3.1 specification for details on this calculation
113            // acceleration = 0.2, deceleration = 0.2
114            return Interpolator.clamp((t < 0.2) ? 3.125 * t * t
115                    : (t > 0.8) ? -3.125 * t * t + 6.25 * t - 2.125
116                            : 1.25 * t - 0.125);
117        }
118
119        @Override
120        public String toString() {
121            return "Interpolator.EASE_BOTH";
122        }
123    };
124    /**
125     * Built-in interpolator instance that provides ease in behavior.
126     * <p>
127     * An ease-in interpolator will make an animation start slow and then
128     * accelerate smoothly.
129     * <p>
130     * The implementation uses the algorithm for easing defined in SMIL 3.0
131     * with an acceleration factor of 0.2.
132     */
133    public static final Interpolator EASE_IN = new Interpolator() {
134        private static final double S1 = 25.0 / 9.0;
135        private static final double S3 = 10.0 / 9.0;
136        private static final double S4 = 1.0 / 9.0;
137
138        @Override
139        protected double curve(double t) {
140            // See the SMIL 3.1 specification for details on this calculation
141            // acceleration = 0.2, deceleration = 0.0
142            return Interpolator.clamp((t < 0.2) ? S1 * t * t : S3 * t - S4);
143        }
144
145        @Override
146        public String toString() {
147            return "Interpolator.EASE_IN";
148        }
149    };
150
151    /**
152     * Built-in interpolator instance that provides ease out behavior.
153     * <p>
154     * An ease-out interpolator will make an animation slow down toward the
155     * end smoothly.
156     * <p>
157     * The implementation uses the algorithm for easing defined in SMIL 3.0 
158     * with an deceleration factor of 0.2.
159     */
160    public static final Interpolator EASE_OUT = new Interpolator() {
161        private static final double S1 = -25.0 / 9.0;
162        private static final double S2 = 50.0 / 9.0;
163        private static final double S3 = -16.0 / 9.0;
164        private static final double S4 = 10.0 / 9.0;
165
166        @Override
167        protected double curve(double t) {
168            // See the SMIL 3.1 specification for details on this calculation
169            // acceleration = 0.2, deceleration = 0.0
170            return Interpolator.clamp((t > 0.8) ? S1 * t * t + S2 * t + S3 : S4
171                    * t);
172        }
173
174        @Override
175        public String toString() {
176            return "Interpolator.EASE_OUT";
177        }
178    };
179
180    /**
181     * Creates an {@code Interpolator}, which {@link #curve(double) curve()} is
182     * shaped using the spline control points defined by ({@code x1}, {@code y1}
183     * ) and ({@code x2}, {@code y2}). The anchor points of the spline are
184     * implicitly defined as ({@code 0.0}, {@code 0.0}) and ({@code 1.0},
185     * {@code 1.0}).
186     * 
187     * @param x1
188     *            x coordinate of the first control point
189     * @param y1
190     *            y coordinate of the first control point
191     * @param x2
192     *            x coordinate of the second control point
193     * @param y2
194     *            y coordinate of the second control point
195     * @return A spline interpolator
196     */
197    public static Interpolator SPLINE(double x1, double y1, double x2, double y2) {
198        return new SplineInterpolator(x1, y1, x2, y2);
199    }
200
201    /**
202     * Create a tangent interpolator. A tangent interpolator allows to define
203     * the behavior of an animation curve very precisely by defining the
204     * tangents close to a key frame.
205     * 
206     * A tangent interpolator defines the behavior to the left and to the right
207     * of a key frame, therefore it is only useful within a {@link Timeline}.
208     * If used in a {@link KeyFrame} after a KeyFrame that has different interpolator,
209     * it's treated as if the out-tangent of that KeyFrame was equal to the value in the KeyFrame.
210     *
211     * <p>
212     * <img src="doc-files/tangent_interpolator.png"/>
213     * 
214     * <p>
215     * The parameters define the tangent of the animation curve for the in
216     * tangent (before a key frame) and out tangent (after a key frame). Each
217     * tangent is specified with a pair, the distance to the key frame and the
218     * value of the tangent at this moment.
219     * <p>
220     * The interpolation then follows a bezier curve, with 2 control points defined by the specified tangent and
221     * positioned at 1/3 of the duration before the second KeyFrame or after the first KeyFrame. See the picture above.
222     * 
223     * @param t1
224     *            The delta time of the in-tangent, relative to the KeyFrame
225     * @param v1
226     *            The value of the in-tangent
227     * @param t2
228     *            The delta time of the out-tangent, relative to the KeyFrame
229     * @param v2
230     *            The value of the out-tangent
231     * @return the new tangent interpolator
232     */
233    public static Interpolator TANGENT(Duration t1, double v1, Duration t2,
234            double v2) {
235        return NumberTangentInterpolator.create(v1, t1, v2, t2);
236    }
237
238    /**
239     * Creates a tangent interpolator, for which in-tangent and out-tangent are
240     * identical. This is especially useful for the first and the last key frame
241     * of a {@link Timeline}, because for these key frames only one tangent is
242     * used.
243     * 
244     * @see #TANGENT(Duration, double, Duration, double)
245     * 
246     * @param t
247     *            The delta time of the tangent
248     * @param v
249     *            The value of the tangent
250     * @return the new Tangent interpolator
251     */
252    public static Interpolator TANGENT(Duration t, double v) {
253        return NumberTangentInterpolator.create(v, t);
254    }
255
256    /**
257     * This method takes two {@code Objects} along with a {@code fraction}
258     * between {@code 0.0} and {@code 1.0} and returns the interpolated value.
259     * <p>
260     * If both {@code Objects} implement {@code Number}, their values are
261     * interpolated. If {@code startValue} implements {@link Interpolatable} the
262     * calculation defined in {@link Interpolatable#interpolate(Object, double)
263     * interpolate()} is used. If neither of these conditions are met, a
264     * discrete interpolation is used, i.e. {@code endValue} is returned if and
265     * only if {@code fraction} is {@code 1.0}, otherwise {@code startValue} is
266     * returned.
267     * <p>
268     * Before calculating the interpolated value, the fraction is altered
269     * according to the function defined in {@link #curve(double) curve()}.
270     * 
271     * @param startValue
272     *            start value
273     * @param endValue
274     *            end value
275     * @param fraction
276     *            a value between 0.0 and 1.0
277     * @return interpolated value
278     */
279    @SuppressWarnings({ "unchecked", "rawtypes" })
280    public Object interpolate(Object startValue, Object endValue,
281            double fraction) {
282        if ((startValue instanceof Number) && (endValue instanceof Number)) {
283            final double start = ((Number) startValue).doubleValue();
284            final double end = ((Number) endValue).doubleValue();
285            final double val = start + (end - start) * curve(fraction);
286            if ((startValue instanceof Double) || (endValue instanceof Double)) {
287                return Double.valueOf(val);
288            }
289            if ((startValue instanceof Float) || (endValue instanceof Float)) {
290                return Float.valueOf((float) val);
291            }
292            if ((startValue instanceof Long) || (endValue instanceof Long)) {
293                return Long.valueOf(Math.round(val));
294            }
295            return Integer.valueOf((int) Math.round(val));
296        } else if ((startValue instanceof Interpolatable) && (endValue instanceof Interpolatable)) {
297            return ((Interpolatable) startValue).interpolate(endValue,
298                    curve(fraction));
299        } else {
300            // discrete
301            return (curve(fraction) == 1.0) ? endValue : startValue;
302        }
303    }
304
305    /**
306     * This method takes two {@code boolean} values along with a
307     * {@code fraction} between {@code 0.0} and {@code 1.0} and returns the
308     * interpolated value.
309     * <p>
310     * Before calculating the interpolated value, the fraction is altered
311     * according to the function defined in {@link #curve(double) curve()}.
312     * 
313     * @param startValue
314     *            the first data point
315     * @param endValue
316     *            the second data point
317     * @param fraction
318     *            the fraction in {@code [0.0...1.0]}
319     */
320    public boolean interpolate(boolean startValue, boolean endValue,
321            double fraction) {
322        return (Math.abs(curve(fraction) - 1.0) < EPSILON) ? endValue
323                : startValue;
324    }
325
326    /**
327     * This method takes two {@code double} values along with a {@code fraction}
328     * between {@code 0.0} and {@code 1.0} and returns the interpolated value.
329     * <p>
330     * Before calculating the interpolated value, the fraction is altered
331     * according to the function defined in {@link #curve(double) curve()}.
332     * 
333     * @param startValue
334     *            the first data point
335     * @param endValue
336     *            the second data point
337     * @param fraction
338     *            the fraction in {@code [0.0...1.0]}
339     */
340    public double interpolate(double startValue, double endValue,
341            double fraction) {
342        return startValue + (endValue - startValue) * curve(fraction);
343    }
344
345    /**
346     * This method takes two {@code int} values along with a {@code fraction}
347     * between {@code 0.0} and {@code 1.0} and returns the interpolated value.
348     * <p>
349     * Before calculating the interpolated value, the fraction is altered
350     * according to the function defined in {@link #curve(double) curve()}.
351     * 
352     * @param startValue
353     *            the first data point
354     * @param endValue
355     *            the second data point
356     * @param fraction
357     *            the fraction in {@code [0.0...1.0]}
358     */
359    public int interpolate(int startValue, int endValue, double fraction) {
360        return startValue
361                + (int) Math.round((endValue - startValue) * curve(fraction));
362    }
363
364    /**
365     * This method takes two {@code int} values along with a {@code fraction}
366     * between {@code 0.0} and {@code 1.0} and returns the interpolated value.
367     * <p>
368     * Before calculating the interpolated value, the fraction is altered
369     * according to the function defined in {@link #curve(double) curve()}.
370     * 
371     * @param startValue
372     *            the first data point
373     * @param endValue
374     *            the second data point
375     * @param fraction
376     *            the fraction in {@code [0.0...1.0]}
377     */
378    public long interpolate(long startValue, long endValue,
379            double fraction) {
380        return startValue
381                + Math.round((endValue - startValue) * curve(fraction));
382    }
383
384    private static double clamp(double t) {
385        return (t < 0.0) ? 0.0 : (t > 1.0) ? 1.0 : t;
386    }
387
388    /**
389     * Mapping from [0.0..1.0] to itself.
390     * 
391     * @param t
392     *            time, but normalized to the range [0.0..1.0], where 0.0 is the
393     *            start of the current interval, while 1.0 is the end of the
394     *            current interval. Usually a function that increases
395     *            monotonically.
396     */
397    protected abstract double curve(double t);
398
399}