PathKeyframes.java revision bc68f5adc222e6438588537fba222ea6a13f46e0
1984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount/*
2984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount * Copyright (C) 2014 The Android Open Source Project
3984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount *
4984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount * Licensed under the Apache License, Version 2.0 (the "License");
5984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount * you may not use this file except in compliance with the License.
6984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount * You may obtain a copy of the License at
7984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount *
8984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount *      http://www.apache.org/licenses/LICENSE-2.0
9984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount *
10984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount * Unless required by applicable law or agreed to in writing, software
11984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount * distributed under the License is distributed on an "AS IS" BASIS,
12984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount * See the License for the specific language governing permissions and
14984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount * limitations under the License.
15984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount */
16984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mountpackage android.animation;
17984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount
18984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mountimport android.graphics.Path;
19984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mountimport android.graphics.PointF;
20984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount
21984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mountimport java.util.ArrayList;
22984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount
23984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount/**
24984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount * PathKeyframes relies on approximating the Path as a series of line segments.
25984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount * The line segments are recursively divided until there is less than 1/2 pixel error
26984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount * between the lines and the curve. Each point of the line segment is converted
27984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount * to a Keyframe and a linear interpolation between Keyframes creates a good approximation
28984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount * of the curve.
29984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount * <p>
30984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount * PathKeyframes is optimized to reduce the number of objects created when there are
31984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount * many keyframes for a curve.
32984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount * </p>
33984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount * <p>
34984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount * Typically, the returned type is a PointF, but the individual components can be extracted
35984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount * as either an IntKeyframes or FloatKeyframes.
36984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount * </p>
37984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount */
38984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mountclass PathKeyframes implements Keyframes {
39984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount    private static final int FRACTION_OFFSET = 0;
40984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount    private static final int X_OFFSET = 1;
41984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount    private static final int Y_OFFSET = 2;
42984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount    private static final int NUM_COMPONENTS = 3;
43984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount    private static final ArrayList<Keyframe> EMPTY_KEYFRAMES = new ArrayList<Keyframe>();
44984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount
45984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount    private PointF mTempPointF = new PointF();
46984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount    private float[] mKeyframeData;
47984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount
48984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount    public PathKeyframes(Path path) {
49984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount        this(path, 0.5f);
50984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount    }
51984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount
52984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount    public PathKeyframes(Path path, float error) {
53984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount        if (path == null || path.isEmpty()) {
54984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount            throw new IllegalArgumentException("The path must not be null or empty");
55984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount        }
56984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount        mKeyframeData = path.approximate(error);
57984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount    }
58984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount
59984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount    @Override
60984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount    public ArrayList<Keyframe> getKeyframes() {
61984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount        return EMPTY_KEYFRAMES;
62984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount    }
63984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount
64984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount    @Override
65984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount    public Object getValue(float fraction) {
66984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount        int numPoints = mKeyframeData.length / 3;
67bc68f5adc222e6438588537fba222ea6a13f46e0George Mount        if (fraction < 0) {
68bc68f5adc222e6438588537fba222ea6a13f46e0George Mount            return interpolateInRange(fraction, 0, 1);
69bc68f5adc222e6438588537fba222ea6a13f46e0George Mount        } else if (fraction > 1) {
70bc68f5adc222e6438588537fba222ea6a13f46e0George Mount            return interpolateInRange(fraction, numPoints - 2, numPoints - 1);
71bc68f5adc222e6438588537fba222ea6a13f46e0George Mount        } else if (fraction == 0) {
72984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount            return pointForIndex(0);
73984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount        } else if (fraction == 1) {
74984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount            return pointForIndex(numPoints - 1);
75984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount        } else {
76984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount            // Binary search for the correct section
77984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount            int low = 0;
78984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount            int high = numPoints - 1;
79984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount
80984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount            while (low <= high) {
81984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount                int mid = (low + high) / 2;
82984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount                float midFraction = mKeyframeData[(mid * NUM_COMPONENTS) + FRACTION_OFFSET];
83984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount
84984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount                if (fraction < midFraction) {
85984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount                    high = mid - 1;
86984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount                } else if (fraction > midFraction) {
87984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount                    low = mid + 1;
88984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount                } else {
89984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount                    return pointForIndex(mid);
90984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount                }
91984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount            }
92984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount
93984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount            // now high is below the fraction and low is above the fraction
94bc68f5adc222e6438588537fba222ea6a13f46e0George Mount            return interpolateInRange(fraction, high, low);
95bc68f5adc222e6438588537fba222ea6a13f46e0George Mount        }
96bc68f5adc222e6438588537fba222ea6a13f46e0George Mount    }
97984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount
98bc68f5adc222e6438588537fba222ea6a13f46e0George Mount    private PointF interpolateInRange(float fraction, int startIndex, int endIndex) {
99bc68f5adc222e6438588537fba222ea6a13f46e0George Mount        int startBase = (startIndex * NUM_COMPONENTS);
100bc68f5adc222e6438588537fba222ea6a13f46e0George Mount        int endBase = (endIndex * NUM_COMPONENTS);
101984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount
102bc68f5adc222e6438588537fba222ea6a13f46e0George Mount        float startFraction = mKeyframeData[startBase + FRACTION_OFFSET];
103bc68f5adc222e6438588537fba222ea6a13f46e0George Mount        float endFraction = mKeyframeData[endBase + FRACTION_OFFSET];
104984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount
105bc68f5adc222e6438588537fba222ea6a13f46e0George Mount        float intervalFraction = (fraction - startFraction)/(endFraction - startFraction);
106984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount
107bc68f5adc222e6438588537fba222ea6a13f46e0George Mount        float startX = mKeyframeData[startBase + X_OFFSET];
108bc68f5adc222e6438588537fba222ea6a13f46e0George Mount        float endX = mKeyframeData[endBase + X_OFFSET];
109bc68f5adc222e6438588537fba222ea6a13f46e0George Mount        float startY = mKeyframeData[startBase + Y_OFFSET];
110bc68f5adc222e6438588537fba222ea6a13f46e0George Mount        float endY = mKeyframeData[endBase + Y_OFFSET];
111984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount
112bc68f5adc222e6438588537fba222ea6a13f46e0George Mount        float x = interpolate(intervalFraction, startX, endX);
113bc68f5adc222e6438588537fba222ea6a13f46e0George Mount        float y = interpolate(intervalFraction, startY, endY);
114bc68f5adc222e6438588537fba222ea6a13f46e0George Mount
115bc68f5adc222e6438588537fba222ea6a13f46e0George Mount        mTempPointF.set(x, y);
116bc68f5adc222e6438588537fba222ea6a13f46e0George Mount        return mTempPointF;
117984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount    }
118984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount
119984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount    @Override
120984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount    public void invalidateCache() {
121984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount    }
122984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount
123984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount    @Override
124984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount    public void setEvaluator(TypeEvaluator evaluator) {
125984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount    }
126984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount
127984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount    @Override
128984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount    public Class getType() {
129984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount        return PointF.class;
130984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount    }
131984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount
132984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount    @Override
133984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount    public Keyframes clone() {
134984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount        Keyframes clone = null;
135984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount        try {
136984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount            clone = (Keyframes) super.clone();
137984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount        } catch (CloneNotSupportedException e) {}
138984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount        return clone;
139984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount    }
140984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount
141984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount    private PointF pointForIndex(int index) {
142984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount        int base = (index * NUM_COMPONENTS);
143984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount        int xOffset = base + X_OFFSET;
144984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount        int yOffset = base + Y_OFFSET;
145984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount        mTempPointF.set(mKeyframeData[xOffset], mKeyframeData[yOffset]);
146984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount        return mTempPointF;
147984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount    }
148984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount
149984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount    private static float interpolate(float fraction, float startValue, float endValue) {
150984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount        float diff = endValue - startValue;
151984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount        return startValue + (diff * fraction);
152984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount    }
153984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount
154984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount    /**
155984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount     * Returns a FloatKeyframes for the X component of the Path.
156984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount     * @return a FloatKeyframes for the X component of the Path.
157984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount     */
158984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount    public FloatKeyframes createXFloatKeyframes() {
159984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount        return new FloatKeyframesBase() {
160984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount            @Override
161984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount            public float getFloatValue(float fraction) {
162984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount                PointF pointF = (PointF) PathKeyframes.this.getValue(fraction);
163984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount                return pointF.x;
164984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount            }
165984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount        };
166984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount    }
167984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount
168984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount    /**
169984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount     * Returns a FloatKeyframes for the Y component of the Path.
170984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount     * @return a FloatKeyframes for the Y component of the Path.
171984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount     */
172984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount    public FloatKeyframes createYFloatKeyframes() {
173984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount        return new FloatKeyframesBase() {
174984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount            @Override
175984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount            public float getFloatValue(float fraction) {
176984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount                PointF pointF = (PointF) PathKeyframes.this.getValue(fraction);
177984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount                return pointF.y;
178984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount            }
179984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount        };
180984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount    }
181984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount
182984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount    /**
183984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount     * Returns an IntKeyframes for the X component of the Path.
184984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount     * @return an IntKeyframes for the X component of the Path.
185984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount     */
186984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount    public IntKeyframes createXIntKeyframes() {
187984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount        return new IntKeyframesBase() {
188984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount            @Override
189984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount            public int getIntValue(float fraction) {
190984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount                PointF pointF = (PointF) PathKeyframes.this.getValue(fraction);
191984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount                return Math.round(pointF.x);
192984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount            }
193984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount        };
194984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount    }
195984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount
196984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount    /**
197984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount     * Returns an IntKeyframeSet for the Y component of the Path.
198984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount     * @return an IntKeyframeSet for the Y component of the Path.
199984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount     */
200984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount    public IntKeyframes createYIntKeyframes() {
201984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount        return new IntKeyframesBase() {
202984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount            @Override
203984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount            public int getIntValue(float fraction) {
204984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount                PointF pointF = (PointF) PathKeyframes.this.getValue(fraction);
205984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount                return Math.round(pointF.y);
206984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount            }
207984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount        };
208984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount    }
209984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount
210984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount    private abstract static class SimpleKeyframes implements Keyframes {
211984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount        @Override
212984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount        public void setEvaluator(TypeEvaluator evaluator) {
213984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount        }
214984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount
215984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount        @Override
216984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount        public void invalidateCache() {
217984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount        }
218984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount
219984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount        @Override
220984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount        public ArrayList<Keyframe> getKeyframes() {
221984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount            return EMPTY_KEYFRAMES;
222984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount        }
223984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount
224984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount        @Override
225984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount        public Keyframes clone() {
226984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount            Keyframes clone = null;
227984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount            try {
228984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount                clone = (Keyframes) super.clone();
229984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount            } catch (CloneNotSupportedException e) {}
230984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount            return clone;
231984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount        }
232984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount    }
233984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount
234984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount    private abstract static class IntKeyframesBase extends SimpleKeyframes implements IntKeyframes {
235984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount        @Override
236984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount        public Class getType() {
237984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount            return Integer.class;
238984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount        }
239984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount
240984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount        @Override
241984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount        public Object getValue(float fraction) {
242984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount            return getIntValue(fraction);
243984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount        }
244984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount    }
245984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount
246984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount    private abstract static class FloatKeyframesBase extends SimpleKeyframes
247984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount            implements FloatKeyframes {
248984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount        @Override
249984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount        public Class getType() {
250984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount            return Float.class;
251984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount        }
252984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount
253984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount        @Override
254984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount        public Object getValue(float fraction) {
255984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount            return getFloatValue(fraction);
256984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount        }
257984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount    }
258984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount}
259