KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > jgoodies > animation > AnimationFunctions


1 /*
2  * Copyright (c) 2001-2004 JGoodies Karsten Lentzsch. All Rights Reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  * o Redistributions of source code must retain the above copyright notice,
8  * this list of conditions and the following disclaimer.
9  *
10  * o Redistributions in binary form must reproduce the above copyright notice,
11  * this list of conditions and the following disclaimer in the documentation
12  * and/or other materials provided with the distribution.
13  *
14  * o Neither the name of JGoodies Karsten Lentzsch nor the names of
15  * its contributors may be used to endorse or promote products derived
16  * from this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
22  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
25  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
27  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */

30
31 package com.jgoodies.animation;
32
33 import java.awt.Color JavaDoc;
34 import java.util.Collections JavaDoc;
35 import java.util.Iterator JavaDoc;
36 import java.util.LinkedList JavaDoc;
37 import java.util.List JavaDoc;
38 import java.util.Random JavaDoc;
39
40 /**
41  * This class consists only of static methods that construct and operate on
42  * {@link AnimationFunction}s.
43  *
44  * @author Karsten Lentzsch
45  * @version $Revision: 1.6 $
46  *
47  * @see AnimationFunction
48  */

49 public final class AnimationFunctions {
50         
51     /**
52      * A constant {@link AnimationFunction} that returns
53      * <code>1</code> all the time.
54      */

55     public static final AnimationFunction ONE =
56         constant(Integer.MAX_VALUE, new Float JavaDoc(1.0f));
57
58     /**
59      * A constant {@link AnimationFunction} that returns
60      * <code>0.0f</code> all the time.
61      */

62     public static final AnimationFunction ZERO =
63         constant(Integer.MAX_VALUE, new Float JavaDoc(0.0f));
64
65
66     private AnimationFunctions() {
67         // Overrides the default constructor; prevents instantiation.
68
}
69
70     
71     /**
72      * Creates and returns an animation function that returns time-based
73      * sRGB colors that are built from a given base color and
74      * an animation function of alpha values.
75      * <p>
76      * Useful for fading effects.
77      *
78      * @param f the animation function of alpha values
79      * @param baseColor the base color
80      * @return an animation function of colors
81      */

82     public static AnimationFunction alphaColor(
83         AnimationFunction f,
84         Color JavaDoc baseColor) {
85         return new AlphaColorAnimationFunction(f, baseColor);
86     }
87
88     /**
89      * Creates a time-based function that wraps the given Float-based animation
90      * function to return the corresponding float values.
91      *
92      * @param f the underlying animation function of floats
93      * @return an animation function the returns Float objects
94      */

95     public static FloatFunction asFloat(AnimationFunction f) {
96         return new FloatFunction(f);
97     }
98
99     /**
100      * Concatenates the fiven animation functions and returns a compound
101      * animation function that represents the concatenation.
102      *
103      * @param first the concatenation's first AnimationFunction
104      * @param second the concatenation's second AnimationFunction
105      * @return the concatenated animation function
106      */

107     public static AnimationFunction concat(
108         AnimationFunction first,
109         AnimationFunction second) {
110         List JavaDoc sequence = new LinkedList JavaDoc();
111         sequence.add(first);
112         sequence.add(second);
113         return new SequencedAnimationFunction(sequence);
114     }
115
116     /**
117      * Creates and returns an animation function that returns a constant value
118      * over the given duration.
119      *
120      * @param duration the function's duration
121      * @param value the Object that will be returned all the time
122      * @return a constant animation function
123      */

124     public static AnimationFunction constant(long duration, Object JavaDoc value) {
125         return discrete(duration, new Object JavaDoc[] { value });
126     }
127
128     /**
129      * Creates and returns a discrete animation function for the
130      * given duration and values. The values are equally distributed
131      * over the duration.
132      *
133      * @param duration the function's duration
134      * @param values an array of discrete result values
135      * @return a discrete animation function
136      */

137     public static AnimationFunction discrete(
138         long duration,
139         Object JavaDoc[] values) {
140         return discrete(duration, values, null);
141     }
142
143     /**
144      * Creates and returns a discrete animation function for the given duration,
145      * values and interpolation key times.
146      *
147      * @param duration the function's duration
148      * @param values an array of discrete result values
149      * @param keyTimes an array of key times used to distribute the
150      * result values over the time
151      * @return a discrete animation function
152      */

153     public static AnimationFunction discrete(
154         long duration,
155         Object JavaDoc[] values,
156         float[] keyTimes) {
157         return new InterpolatedAnimationFunction(
158             duration,
159             values,
160             keyTimes,
161             InterpolatedAnimationFunction.MODE_DISCRETE);
162     }
163
164     /**
165      * Creates and returns a linear animation function for the given duration
166      * that returns Float in interval [from, from + by].
167      *
168      * @param duration the animation duration
169      * @param from the initial result value
170      * @param by the difference that is added to the initial value
171      * @return a linear animation function with values in [from, from + by]
172      */

173     public static AnimationFunction fromBy(
174         long duration,
175         float from,
176         float by) {
177         return fromTo(duration, from, from + by);
178     }
179
180     /**
181      * Ceates and returns a linear animation function with the given duration.
182      * The function values are Floats in the intervall [from, to].
183      *
184      * @param duration the animation duration
185      * @param from the initial result value
186      * @param to the last result value
187      * @return a linear animation function with values in [from, to]
188      */

189     public static AnimationFunction fromTo(
190         long duration,
191         float from,
192         float to) {
193         return linear(
194             duration,
195             new Float JavaDoc[] { new Float JavaDoc(from), new Float JavaDoc(to)});
196     }
197
198     /**
199      * Creates and returns a linear animation function that is defined
200      * by an array of numeric values; these are distributed equally
201      * over the duration.
202      *
203      * @param duration the animation duration
204      * @param values an array of values
205      * @return a linear animation function for the given values
206      */

207     public static AnimationFunction linear(long duration, Object JavaDoc[] values) {
208         return linear(duration, values, null);
209     }
210
211     /**
212      * Creates and returns a linear animation function that is defined
213      * by an array of numeric values and an array of relative key times.
214      *
215      * @param duration the animation duration
216      * @param values an array of values
217      * @param keyTimes an array of key times used to distribute the
218      * result values over the time
219      * @return a linear animation function for the given values
220      */

221     public static AnimationFunction linear(
222         long duration,
223         Object JavaDoc[] values,
224         float[] keyTimes) {
225         return new InterpolatedAnimationFunction(
226             duration,
227             values,
228             keyTimes,
229             InterpolatedAnimationFunction.MODE_LINEAR);
230     }
231     
232
233     /**
234      * Creates an <code>AnimationFunction</code> that maps times
235      * to instances of <code>Color</code>. The mapping is interpolated
236      * from an array of Colors using an array of key times.
237      *
238      * @param duration the duration of this animation function
239      * @param colors the colors to interpolate.
240      * @param keyTimes an array of key times used to distribute
241      * the result values over the time.
242      * @return An {@link AnimationFunction} that maps times to sRGB colors.
243      * This mapping is defined by an arry of <code>Color</code> values
244      * and a corresponding array of key times that is used to interpolate
245      * sub-AnimationFunction for the red, green, blue and alpha values.
246      */

247     public static AnimationFunction linearColors(
248                 long duration, Color JavaDoc[] colors, float[] keyTimes) {
249         return new ColorFunction(duration, colors, keyTimes);
250     }
251     
252     
253     /**
254      * Creates and returns an animation function that returns random values
255      * from the interval [min, max] with a given change probability.
256      *
257      * @param min the minimum result value
258      * @param max the maximum result value
259      * @param changeProbability the probability that the value changes
260      * @return an animation function with random values in [min, max]
261      */

262     public static AnimationFunction random(
263         int min,
264         int max,
265         float changeProbability) {
266         return new RandomAnimationFunction(min, max, changeProbability);
267     }
268
269     /**
270      * Creates and returns an animation function that is defined
271      * by repeating the specified animation function.
272      *
273      * @param f the animation function to repeat
274      * @param repeatTime the time to repeat the function
275      * @return the repeated animation function
276      */

277     public static AnimationFunction repeat(
278         AnimationFunction f,
279         long repeatTime) {
280         return new RepeatedAnimationFunction(f, repeatTime);
281     }
282
283     /**
284      * Creates and returns an animation function that is defined
285      * by reverting the given animation function in time.
286      *
287      * @param f the animation function to reverse
288      * @return the reversed animation function
289      */

290     public static AnimationFunction reverse(AnimationFunction f) {
291         return new ReversedAnimationFunction(f);
292     }
293     
294     
295     // Helper Classes *********************************************************
296

297     // Helper class for animation functions that answer translucent colors.
298
private static class AlphaColorAnimationFunction
299         implements AnimationFunction {
300         private final Color JavaDoc baseColor;
301         private final AnimationFunction fAlpha;
302
303         private AlphaColorAnimationFunction(
304             AnimationFunction fAlpha,
305             Color JavaDoc baseColor) {
306             this.fAlpha = fAlpha;
307             this.baseColor = baseColor;
308         }
309
310         public long duration() {
311             return fAlpha.duration();
312         }
313
314         // Constructs colors from the sRGB color space.
315
public Object JavaDoc valueAt(long time) {
316             return new Color JavaDoc(
317                 baseColor.getRed(),
318                 baseColor.getGreen(),
319                 baseColor.getBlue(),
320                 ((Float JavaDoc) fAlpha.valueAt(time)).intValue());
321         }
322     }
323
324     // Helper class for interpolating colors.
325
private static class ColorFunction extends AbstractAnimationFunction {
326
327         /**
328          * Refers to an AnimationFunction of float values that maps
329          * a time to the red component of an sRGB color value.
330          */

331         private final AnimationFunctions.FloatFunction redFunction;
332         
333         /**
334          * Refers to an AnimationFunction of float values that maps
335          * a time to the green component of an sRGB color value.
336          */

337         private final AnimationFunctions.FloatFunction greenFunction;
338         
339         /**
340          * Refers to an AnimationFunction of float values that maps
341          * a time to the blue component of an sRGB color value.
342          */

343         private final AnimationFunctions.FloatFunction blueFunction;
344         
345         /**
346          * Refers to an AnimationFunction of float values that maps
347          * a time to the alpha value of an sRGB color value.
348          */

349         private final AnimationFunctions.FloatFunction alphaFunction;
350         
351
352         // Instance creation ******************************************************
353

354         /**
355          * Creates an <code>AnimationFunction</code> that maps times
356          * to instances of <code>Color</code>. The mapping is interpolated
357          * from an array of Colors using an array of key times.
358          *
359          * @param duration the duration of this animation function
360          * @param colors the colors to interpolate.
361          * @param keyTimes an array of key times used to distribute
362          * the result values over the time.
363          */

364         private ColorFunction(long duration, Color JavaDoc[] colors, float[] keyTimes) {
365             super(duration);
366             Float JavaDoc[] red = new Float JavaDoc[colors.length];
367             Float JavaDoc[] green = new Float JavaDoc[colors.length];
368             Float JavaDoc[] blue = new Float JavaDoc[colors.length];
369             Float JavaDoc[] alpha = new Float JavaDoc[colors.length];
370
371             for (int i = 0; i < colors.length; i++) {
372                 red[i] = new Float JavaDoc(colors[i].getRed());
373                 green[i] = new Float JavaDoc(colors[i].getGreen());
374                 blue[i] = new Float JavaDoc(colors[i].getBlue());
375                 alpha[i] = new Float JavaDoc(colors[i].getAlpha());
376             }
377
378             redFunction = AnimationFunctions.asFloat(AnimationFunctions.linear(
379                             duration, red, keyTimes));
380             greenFunction = AnimationFunctions.asFloat(AnimationFunctions.linear(
381                             duration, green, keyTimes));
382             blueFunction = AnimationFunctions.asFloat(AnimationFunctions.linear(
383                             duration, blue, keyTimes));
384             alphaFunction = AnimationFunctions.asFloat(AnimationFunctions.linear(
385                             duration, alpha, keyTimes));
386         }
387         
388         
389         // AnimationFunction Implementation ***************************************
390

391         /**
392          * Returns the interpolated color for a given time in the valid
393          * time interval. This method is required to implement the
394          * AnimationFunction interface and just forwards to the type-safe
395          * counterpart <code>#colorValueAt</code>
396          *
397          * @param time the time used to determine the interpolated color
398          * @return the interpolated color for the given time
399          * @see ColorFunction#colorValueAt(long)
400          */

401         public Object JavaDoc valueAt(long time) {
402             return colorValueAt(time);
403         }
404         
405         /**
406          * Returns the interpolated color for a given time in the valid
407          * time interval.
408          *
409          * @param time the time used to determine the interpolated color.
410          * @return the interpolated color for the given time
411          * @see ColorFunction#valueAt(long)
412          */

413         private Color JavaDoc colorValueAt(long time) {
414             checkTimeRange(time);
415             return new Color JavaDoc(
416                     (int) redFunction.valueAt(time),
417                     (int) greenFunction.valueAt(time),
418                     (int) blueFunction.valueAt(time),
419                     (int) alphaFunction.valueAt(time));
420         }
421         
422     }
423     
424     /**
425      * Helper class that wraps a Float-based animation function to answer floats.
426      */

427     public static class FloatFunction {
428         private final AnimationFunction f;
429
430         FloatFunction(AnimationFunction f) {
431             this.f = f;
432         }
433
434         public long duration() {
435             return f.duration();
436         }
437         public float valueAt(long time) {
438             return ((Number JavaDoc) f.valueAt(time)).floatValue();
439         }
440     }
441
442     // Helper class for interpolation based animation functions.
443
private static class InterpolatedAnimationFunction
444         extends AbstractAnimationFunction {
445         static final int MODE_DISCRETE = 1;
446         static final int MODE_LINEAR = 2;
447         private final float[] keyTimes;
448         private final int mode;
449
450         /* Left for long winter nights in northern Germany.
451         static final int MODE_PACED = 4;
452         static final int MODE_SPLINE = 8;
453         */

454
455         private final Object JavaDoc[] values;
456
457         private InterpolatedAnimationFunction(
458             long duration,
459             Object JavaDoc[] values,
460             float[] keyTimes,
461             int mode) {
462             super(duration);
463             this.values = values;
464             this.keyTimes = keyTimes;
465             this.mode = mode;
466             checkValidKeyTimes(values.length, keyTimes);
467         }
468
469         private void checkValidKeyTimes(int valuesLength, float[] theKeyTimes) {
470             if (theKeyTimes == null)
471                 return;
472
473             if (valuesLength < 2 || valuesLength != theKeyTimes.length)
474                 throw new IllegalArgumentException JavaDoc("The values and key times arrays must be non-empty and must have equal length.");
475
476             for (int index = 0; index < theKeyTimes.length - 2; index++) {
477                 if (theKeyTimes[index] >= theKeyTimes[index + 1])
478                     throw new IllegalArgumentException JavaDoc("The key times must be increasing.");
479             }
480         }
481
482         private Object JavaDoc discreteValueAt(long time) {
483             return values[indexAt(time, values.length)];
484         }
485
486         private int indexAt(long time, int intervalCount) {
487             long duration = duration();
488             // Gleichlange Zeitabschnitte
489
if (keyTimes == null) {
490                 return (int) (time * intervalCount / duration);
491             }
492             for (int index = keyTimes.length - 1; index > 0; index--) {
493                 if (time > duration * keyTimes[index])
494                     return index;
495             }
496             return 0;
497         }
498
499         /**
500          * Currently we provide only linear interpolations that are based on floats.
501          *
502          * @param value1 the first interpolation key point
503          * @param value2 the second interpolation key point
504          * @param time the time to get an interpolated value for
505          * @param duration the duration of the whole animation
506          * @return the interpolated value at the given time
507          */

508         private Object JavaDoc interpolateLinear(
509             Object JavaDoc value1,
510             Object JavaDoc value2,
511             long time,
512             long duration) {
513             float f1 = ((Number JavaDoc) value1).floatValue();
514             float f2 = ((Number JavaDoc) value2).floatValue();
515             float value = f1 + (f2 - f1) * time / duration;
516             return new Float JavaDoc(value);
517         }
518
519         private Object JavaDoc linearValueAt(long time) {
520             int segments = values.length - 1;
521             int beginIndex = indexAt(time, segments);
522             int endIndex = beginIndex + 1;
523             long lastTime = duration() - 1;
524             long beginTime =
525                 keyTimes == null
526                     ? beginIndex * lastTime / segments
527                     : (long) (keyTimes[beginIndex] * lastTime);
528             long endTime =
529                 keyTimes == null
530                     ? endIndex * lastTime / segments
531                     : (long) (keyTimes[endIndex] * lastTime);
532
533             return interpolateLinear(
534                 values[beginIndex],
535                 values[endIndex],
536                 time - beginTime,
537                 endTime - beginTime);
538         }
539
540         public Object JavaDoc valueAt(long time) {
541             checkTimeRange(time);
542             switch (mode) {
543                 case MODE_DISCRETE :
544                     return discreteValueAt(time);
545
546                 case MODE_LINEAR :
547                     return linearValueAt(time);
548
549                 default :
550                     throw new IllegalStateException JavaDoc("Unsupported interpolation mode.");
551             }
552         }
553     }
554
555     // Helper class for animation function that answer random values.
556
private static class RandomAnimationFunction
557         implements AnimationFunction {
558         private final float changeProbability;
559         private final int max;
560         private final int min;
561         private final Random JavaDoc random;
562
563         private Object JavaDoc value;
564
565         private RandomAnimationFunction(
566             int min,
567             int max,
568             float changeProbability) {
569             this.random = new Random JavaDoc();
570             this.min = min;
571             this.max = max;
572             this.changeProbability = changeProbability;
573         }
574
575         public long duration() {
576             return Integer.MAX_VALUE;
577         }
578
579         public Object JavaDoc valueAt(long time) {
580             if ((value == null)
581                 || (random.nextFloat() < changeProbability)) {
582                 value = new Integer JavaDoc(min + random.nextInt(max - min));
583             }
584             return value;
585         }
586     }
587
588     // Helper class used to repeat or sequence an animation function.
589
private static class RepeatedAnimationFunction
590         extends AbstractAnimationFunction {
591         private final AnimationFunction f;
592         private final long simpleDuration;
593
594         private RepeatedAnimationFunction(
595             AnimationFunction f,
596             long repeatTime) {
597             super(repeatTime);
598             this.f = f;
599             this.simpleDuration = f.duration();
600         }
601
602         public Object JavaDoc valueAt(long time) {
603             return f.valueAt(time % simpleDuration);
604         }
605     }
606
607     // Helper class for reversing an animation function.
608
private static class ReversedAnimationFunction
609         extends AbstractAnimationFunction {
610         private final AnimationFunction f;
611
612         private ReversedAnimationFunction(AnimationFunction f) {
613             super(f.duration());
614             this.f = f;
615         }
616
617         public Object JavaDoc valueAt(long time) {
618             return f.valueAt(duration() - time);
619         }
620     }
621
622     // Helper class to compose an animation functions from a sequences of such functions.
623
private static class SequencedAnimationFunction
624         implements AnimationFunction {
625         private final List JavaDoc functions;
626
627         private SequencedAnimationFunction(List JavaDoc functions) {
628             this.functions = Collections.unmodifiableList(functions);
629             if (this.functions.isEmpty())
630                 throw new IllegalArgumentException JavaDoc("The list of functions must not be empty.");
631         }
632
633         public long duration() {
634             long cumulatedDuration = 0;
635             for (Iterator JavaDoc i = functions.iterator(); i.hasNext();) {
636                 AnimationFunction f = (AnimationFunction) i.next();
637                 cumulatedDuration += f.duration();
638             }
639             return cumulatedDuration;
640         }
641
642         public Object JavaDoc valueAt(long time) {
643             if (time < 0)
644                 throw new IllegalArgumentException JavaDoc("The time must be positive.");
645
646             long begin = 0;
647             long end;
648             for (Iterator JavaDoc i = functions.iterator(); i.hasNext();) {
649                 AnimationFunction f = (AnimationFunction) i.next();
650                 end = begin + f.duration();
651                 if (time < end)
652                     return f.valueAt(time - begin);
653                 begin = end;
654             }
655             throw new IllegalArgumentException JavaDoc("The time must be smaller than the total duration.");
656         }
657     }
658     
659 }
Popular Tags