001: /*
002:
003: Licensed to the Apache Software Foundation (ASF) under one or more
004: contributor license agreements. See the NOTICE file distributed with
005: this work for additional information regarding copyright ownership.
006: The ASF licenses this file to You under the Apache License, Version 2.0
007: (the "License"); you may not use this file except in compliance with
008: the License. You may obtain a copy of the License at
009:
010: http://www.apache.org/licenses/LICENSE-2.0
011:
012: Unless required by applicable law or agreed to in writing, software
013: distributed under the License is distributed on an "AS IS" BASIS,
014: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: See the License for the specific language governing permissions and
016: limitations under the License.
017:
018: */
019: package org.apache.batik.anim;
020:
021: import org.apache.batik.anim.timing.TimedElement;
022: import org.apache.batik.anim.values.AnimatableValue;
023: import org.apache.batik.anim.values.AnimatableTransformListValue;
024: import org.apache.batik.dom.anim.AnimatableElement;
025:
026: import org.w3c.dom.svg.SVGTransform;
027:
028: /**
029: * An animation class for 'animateTransform' animations.
030: *
031: * @author <a href="mailto:cam%40mcc%2eid%2eau">Cameron McCormack</a>
032: * @version $Id: TransformAnimation.java 492528 2007-01-04 11:45:47Z cam $
033: */
034: public class TransformAnimation extends SimpleAnimation {
035:
036: /**
037: * The transform type. This should take one of the constants defined
038: * in {@link org.w3c.dom.svg.SVGTransform}.
039: */
040: protected short type;
041:
042: /**
043: * Time values to control the pacing of the second component of the
044: * animation.
045: */
046: protected float[] keyTimes2;
047:
048: /**
049: * Time values to control the pacing of the third component of the
050: * animation.
051: */
052: protected float[] keyTimes3;
053:
054: /**
055: * Creates a new TransformAnimation.
056: */
057: public TransformAnimation(TimedElement timedElement,
058: AnimatableElement animatableElement, int calcMode,
059: float[] keyTimes, float[] keySplines, boolean additive,
060: boolean cumulative, AnimatableValue[] values,
061: AnimatableValue from, AnimatableValue to,
062: AnimatableValue by, short type) {
063: // pretend we didn't get a calcMode="paced", since we need specialised
064: // behaviour in sampledAtUnitTime.
065: super (timedElement, animatableElement,
066: calcMode == CALC_MODE_PACED ? CALC_MODE_LINEAR
067: : calcMode, calcMode == CALC_MODE_PACED ? null
068: : keyTimes, keySplines, additive, cumulative,
069: values, from, to, by);
070: this .calcMode = calcMode;
071: this .type = type;
072:
073: if (calcMode != CALC_MODE_PACED) {
074: return;
075: }
076:
077: // Determine the equivalent keyTimes for the individual components
078: // of the transforms for CALC_MODE_PACED.
079: int count = this .values.length;
080: float[] cumulativeDistances1;
081: float[] cumulativeDistances2 = null;
082: float[] cumulativeDistances3 = null;
083: switch (type) {
084: case SVGTransform.SVG_TRANSFORM_ROTATE:
085: cumulativeDistances3 = new float[count];
086: cumulativeDistances3[0] = 0f;
087: // fall through
088: case SVGTransform.SVG_TRANSFORM_SCALE:
089: case SVGTransform.SVG_TRANSFORM_TRANSLATE:
090: cumulativeDistances2 = new float[count];
091: cumulativeDistances2[0] = 0f;
092: // fall through
093: default:
094: cumulativeDistances1 = new float[count];
095: cumulativeDistances1[0] = 0f;
096: }
097:
098: for (int i = 1; i < this .values.length; i++) {
099: switch (type) {
100: case SVGTransform.SVG_TRANSFORM_ROTATE:
101: cumulativeDistances3[i] = cumulativeDistances3[i - 1]
102: + ((AnimatableTransformListValue) this .values[i - 1])
103: .distanceTo3(this .values[i]);
104: // fall through
105: case SVGTransform.SVG_TRANSFORM_SCALE:
106: case SVGTransform.SVG_TRANSFORM_TRANSLATE:
107: cumulativeDistances2[i] = cumulativeDistances2[i - 1]
108: + ((AnimatableTransformListValue) this .values[i - 1])
109: .distanceTo2(this .values[i]);
110: // fall through
111: default:
112: cumulativeDistances1[i] = cumulativeDistances1[i - 1]
113: + ((AnimatableTransformListValue) this .values[i - 1])
114: .distanceTo1(this .values[i]);
115: }
116: }
117:
118: switch (type) {
119: case SVGTransform.SVG_TRANSFORM_ROTATE:
120: float totalLength = cumulativeDistances3[count - 1];
121: keyTimes3 = new float[count];
122: keyTimes3[0] = 0f;
123: for (int i = 1; i < count - 1; i++) {
124: keyTimes3[i] = cumulativeDistances3[i] / totalLength;
125: }
126: keyTimes3[count - 1] = 1f;
127: // fall through
128: case SVGTransform.SVG_TRANSFORM_SCALE:
129: case SVGTransform.SVG_TRANSFORM_TRANSLATE:
130: totalLength = cumulativeDistances2[count - 1];
131: keyTimes2 = new float[count];
132: keyTimes2[0] = 0f;
133: for (int i = 1; i < count - 1; i++) {
134: keyTimes2[i] = cumulativeDistances2[i] / totalLength;
135: }
136: keyTimes2[count - 1] = 1f;
137: // fall through
138: default:
139: totalLength = cumulativeDistances1[count - 1];
140: this .keyTimes = new float[count];
141: this .keyTimes[0] = 0f;
142: for (int i = 1; i < count - 1; i++) {
143: this .keyTimes[i] = cumulativeDistances1[i]
144: / totalLength;
145: }
146: this .keyTimes[count - 1] = 1f;
147: }
148: }
149:
150: /**
151: * Called when the element is sampled at the given unit time. This updates
152: * the {@link #value} of the animation if active.
153: */
154: protected void sampledAtUnitTime(float unitTime, int repeatIteration) {
155: // Note that skews are handled by SimpleAnimation and not here, since
156: // they need just the one component of interpolation.
157: if (calcMode != CALC_MODE_PACED
158: || type == SVGTransform.SVG_TRANSFORM_SKEWX
159: || type == SVGTransform.SVG_TRANSFORM_SKEWY) {
160: super .sampledAtUnitTime(unitTime, repeatIteration);
161: return;
162: }
163:
164: AnimatableTransformListValue value1, value2, value3 = null, nextValue1, nextValue2, nextValue3 = null, accumulation;
165: float interpolation1 = 0f, interpolation2 = 0f, interpolation3 = 0f;
166: if (unitTime != 1) {
167: switch (type) {
168: case SVGTransform.SVG_TRANSFORM_ROTATE:
169: int keyTimeIndex = 0;
170: while (keyTimeIndex < keyTimes3.length - 1
171: && unitTime >= keyTimes3[keyTimeIndex + 1]) {
172: keyTimeIndex++;
173: }
174: value3 = (AnimatableTransformListValue) this .values[keyTimeIndex];
175: nextValue3 = (AnimatableTransformListValue) this .values[keyTimeIndex + 1];
176: interpolation3 = (unitTime - keyTimes3[keyTimeIndex])
177: / (keyTimes3[keyTimeIndex + 1] - keyTimes3[keyTimeIndex]);
178: // fall through
179: default:
180: keyTimeIndex = 0;
181: while (keyTimeIndex < keyTimes2.length - 1
182: && unitTime >= keyTimes2[keyTimeIndex + 1]) {
183: keyTimeIndex++;
184: }
185: value2 = (AnimatableTransformListValue) this .values[keyTimeIndex];
186: nextValue2 = (AnimatableTransformListValue) this .values[keyTimeIndex + 1];
187: interpolation2 = (unitTime - keyTimes2[keyTimeIndex])
188: / (keyTimes2[keyTimeIndex + 1] - keyTimes2[keyTimeIndex]);
189:
190: keyTimeIndex = 0;
191: while (keyTimeIndex < keyTimes.length - 1
192: && unitTime >= keyTimes[keyTimeIndex + 1]) {
193: keyTimeIndex++;
194: }
195: value1 = (AnimatableTransformListValue) this .values[keyTimeIndex];
196: nextValue1 = (AnimatableTransformListValue) this .values[keyTimeIndex + 1];
197: interpolation1 = (unitTime - keyTimes[keyTimeIndex])
198: / (keyTimes[keyTimeIndex + 1] - keyTimes[keyTimeIndex]);
199: }
200: } else {
201: value1 = value2 = value3 = (AnimatableTransformListValue) this .values[this .values.length - 1];
202: nextValue1 = nextValue2 = nextValue3 = null;
203: interpolation1 = interpolation2 = interpolation3 = 1f;
204: }
205: if (cumulative) {
206: accumulation = (AnimatableTransformListValue) this .values[this .values.length - 1];
207: } else {
208: accumulation = null;
209: }
210:
211: switch (type) {
212: case SVGTransform.SVG_TRANSFORM_ROTATE:
213: this .value = AnimatableTransformListValue.interpolate(
214: (AnimatableTransformListValue) this .value, value1,
215: value2, value3, nextValue1, nextValue2, nextValue3,
216: interpolation1, interpolation2, interpolation3,
217: accumulation, repeatIteration);
218: break;
219: default:
220: this .value = AnimatableTransformListValue.interpolate(
221: (AnimatableTransformListValue) this.value, value1,
222: value2, nextValue1, nextValue2, interpolation1,
223: interpolation2, accumulation, repeatIteration);
224: break;
225: }
226:
227: if (this.value.hasChanged()) {
228: markDirty();
229: }
230: }
231: }
|