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.values;
020:
021: import java.util.Iterator;
022: import java.util.Vector;
023: import java.util.List;
024:
025: import org.apache.batik.dom.anim.AnimationTarget;
026: import org.apache.batik.dom.svg.AbstractSVGTransform;
027: import org.apache.batik.dom.svg.SVGOMTransform;
028:
029: import org.w3c.dom.svg.SVGMatrix;
030: import org.w3c.dom.svg.SVGTransform;
031:
032: /**
033: * An SVG transform list value in the animation system.
034: *
035: * @author <a href="mailto:cam%40mcc%2eid%2eau">Cameron McCormack</a>
036: * @version $Id: AnimatableTransformListValue.java 515307 2007-03-06 21:15:58Z cam $
037: */
038: public class AnimatableTransformListValue extends AnimatableValue {
039:
040: /**
041: * Identity transform value of type 'skewX'.
042: */
043: protected static SVGOMTransform IDENTITY_SKEWX = new SVGOMTransform();
044:
045: /**
046: * Identity transform value of type 'skewY'.
047: */
048: protected static SVGOMTransform IDENTITY_SKEWY = new SVGOMTransform();
049:
050: /**
051: * Identity transform value of type 'scale'.
052: */
053: protected static SVGOMTransform IDENTITY_SCALE = new SVGOMTransform();
054:
055: /**
056: * Identity transform value of type 'rotate'.
057: */
058: protected static SVGOMTransform IDENTITY_ROTATE = new SVGOMTransform();
059:
060: /**
061: * Identity transform value of type 'translate'.
062: */
063: protected static SVGOMTransform IDENTITY_TRANSLATE = new SVGOMTransform();
064:
065: static {
066: IDENTITY_SKEWX.setSkewX(0f);
067: IDENTITY_SKEWY.setSkewY(0f);
068: IDENTITY_SCALE.setScale(0f, 0f);
069: IDENTITY_ROTATE.setRotate(0f, 0f, 0f);
070: IDENTITY_TRANSLATE.setTranslate(0f, 0f);
071: }
072:
073: /**
074: * List of transforms.
075: */
076: protected Vector transforms;
077:
078: /**
079: * Creates a new, uninitialized AnimatableTransformListValue.
080: */
081: protected AnimatableTransformListValue(AnimationTarget target) {
082: super (target);
083: }
084:
085: /**
086: * Creates a new AnimatableTransformListValue with a single transform.
087: */
088: public AnimatableTransformListValue(AnimationTarget target,
089: AbstractSVGTransform t) {
090: super (target);
091: this .transforms = new Vector();
092: this .transforms.add(t);
093: }
094:
095: /**
096: * Creates a new AnimatableTransformListValue with a transform list.
097: */
098: public AnimatableTransformListValue(AnimationTarget target,
099: List transforms) {
100: super (target);
101:
102: this .transforms = new Vector(transforms);
103:
104: }
105:
106: /**
107: * Performs interpolation to the given value.
108: */
109: public AnimatableValue interpolate(AnimatableValue result,
110: AnimatableValue to, float interpolation,
111: AnimatableValue accumulation, int multiplier) {
112:
113: AnimatableTransformListValue toTransformList = (AnimatableTransformListValue) to;
114: AnimatableTransformListValue accTransformList = (AnimatableTransformListValue) accumulation;
115:
116: int accSize = accumulation == null ? 0
117: : accTransformList.transforms.size();
118: int newSize = transforms.size() + accSize * multiplier;
119:
120: AnimatableTransformListValue res;
121: if (result == null) {
122: res = new AnimatableTransformListValue(target);
123: res.transforms = new Vector(newSize);
124: res.transforms.setSize(newSize);
125: } else {
126: res = (AnimatableTransformListValue) result;
127: if (res.transforms == null) {
128: res.transforms = new Vector(newSize);
129: res.transforms.setSize(newSize);
130: } else if (res.transforms.size() != newSize) {
131: res.transforms.setSize(newSize);
132: }
133: }
134:
135: int index = 0;
136: for (int j = 0; j < multiplier; j++) {
137: for (int i = 0; i < accSize; i++, index++) {
138: res.transforms.setElementAt(accTransformList.transforms
139: .elementAt(i), index);
140: }
141: }
142: for (int i = 0; i < transforms.size() - 1; i++, index++) {
143: res.transforms.setElementAt(transforms.elementAt(i), index);
144: }
145:
146: if (to != null) {
147: AbstractSVGTransform tt = (AbstractSVGTransform) toTransformList.transforms
148: .lastElement();
149: AbstractSVGTransform ft = null;
150: int type;
151: if (transforms.isEmpty()) {
152: // For the case of an additive animation with an underlying
153: // transform list of zero elements.
154: type = tt.getType();
155: switch (type) {
156: case SVGTransform.SVG_TRANSFORM_SKEWX:
157: ft = IDENTITY_SKEWX;
158: break;
159: case SVGTransform.SVG_TRANSFORM_SKEWY:
160: ft = IDENTITY_SKEWY;
161: break;
162: case SVGTransform.SVG_TRANSFORM_SCALE:
163: ft = IDENTITY_SCALE;
164: break;
165: case SVGTransform.SVG_TRANSFORM_ROTATE:
166: ft = IDENTITY_ROTATE;
167: break;
168: case SVGTransform.SVG_TRANSFORM_TRANSLATE:
169: ft = IDENTITY_TRANSLATE;
170: break;
171: }
172: } else {
173: ft = (AbstractSVGTransform) transforms.lastElement();
174: type = ft.getType();
175: }
176: if (type == tt.getType()) {
177: AbstractSVGTransform t;
178: if (res.transforms.isEmpty()) {
179: t = new SVGOMTransform();
180: res.transforms.add(t);
181: } else {
182: t = (AbstractSVGTransform) res.transforms
183: .elementAt(index);
184: if (t == null) {
185: t = new SVGOMTransform();
186: res.transforms.setElementAt(t, index);
187: }
188: }
189: float x, y, r = 0;
190: switch (type) {
191: case SVGTransform.SVG_TRANSFORM_SKEWX:
192: case SVGTransform.SVG_TRANSFORM_SKEWY:
193: r = ft.getAngle();
194: r += interpolation * (tt.getAngle() - r);
195: if (type == SVGTransform.SVG_TRANSFORM_SKEWX) {
196: t.setSkewX(r);
197: } else if (type == SVGTransform.SVG_TRANSFORM_SKEWY) {
198: t.setSkewY(r);
199: }
200: break;
201: case SVGTransform.SVG_TRANSFORM_SCALE: {
202: SVGMatrix fm = ft.getMatrix();
203: SVGMatrix tm = tt.getMatrix();
204: x = fm.getA();
205: y = fm.getD();
206: x += interpolation * (tm.getA() - x);
207: y += interpolation * (tm.getD() - y);
208: t.setScale(x, y);
209: break;
210: }
211: case SVGTransform.SVG_TRANSFORM_ROTATE: {
212: x = ft.getX();
213: y = ft.getY();
214: x += interpolation * (tt.getX() - x);
215: y += interpolation * (tt.getY() - y);
216: r = ft.getAngle();
217: r += interpolation * (tt.getAngle() - r);
218: t.setRotate(r, x, y);
219: break;
220: }
221: case SVGTransform.SVG_TRANSFORM_TRANSLATE: {
222: SVGMatrix fm = ft.getMatrix();
223: SVGMatrix tm = tt.getMatrix();
224: x = fm.getE();
225: y = fm.getF();
226: x += interpolation * (tm.getE() - x);
227: y += interpolation * (tm.getF() - y);
228: t.setTranslate(x, y);
229: break;
230: }
231: }
232: }
233: } else {
234: AbstractSVGTransform ft = (AbstractSVGTransform) transforms
235: .lastElement();
236: AbstractSVGTransform t = (AbstractSVGTransform) res.transforms
237: .elementAt(index);
238: if (t == null) {
239: t = new SVGOMTransform();
240: res.transforms.setElementAt(t, index);
241: }
242: t.assign(ft);
243: }
244:
245: // XXX Do better checking for changes.
246: res.hasChanged = true;
247:
248: return res;
249: }
250:
251: /**
252: * Performs a two-way interpolation between the specified values.
253: * value[12] and to[12] must all be of the same type, either scale or
254: * translation transforms, or all null.
255: */
256: public static AnimatableTransformListValue interpolate(
257: AnimatableTransformListValue res,
258: AnimatableTransformListValue value1,
259: AnimatableTransformListValue value2,
260: AnimatableTransformListValue to1,
261: AnimatableTransformListValue to2, float interpolation1,
262: float interpolation2,
263: AnimatableTransformListValue accumulation, int multiplier) {
264:
265: int accSize = accumulation == null ? 0
266: : accumulation.transforms.size();
267: int newSize = accSize * multiplier + 1;
268:
269: if (res == null) {
270: res = new AnimatableTransformListValue(to1.target);
271: res.transforms = new Vector(newSize);
272: res.transforms.setSize(newSize);
273: } else {
274: if (res.transforms == null) {
275: res.transforms = new Vector(newSize);
276: res.transforms.setSize(newSize);
277: } else if (res.transforms.size() != newSize) {
278: res.transforms.setSize(newSize);
279: }
280: }
281:
282: int index = 0;
283: for (int j = 0; j < multiplier; j++) {
284: for (int i = 0; i < accSize; i++, index++) {
285: res.transforms.setElementAt(accumulation.transforms
286: .elementAt(i), index);
287: }
288: }
289:
290: AbstractSVGTransform ft1 = (AbstractSVGTransform) value1.transforms
291: .lastElement();
292: AbstractSVGTransform ft2 = (AbstractSVGTransform) value2.transforms
293: .lastElement();
294:
295: AbstractSVGTransform t = (AbstractSVGTransform) res.transforms
296: .elementAt(index);
297: if (t == null) {
298: t = new SVGOMTransform();
299: res.transforms.setElementAt(t, index);
300: }
301:
302: int type = ft1.getType();
303:
304: float x, y;
305: if (type == SVGTransform.SVG_TRANSFORM_SCALE) {
306: x = ft1.getMatrix().getA();
307: y = ft2.getMatrix().getD();
308: } else {
309: x = ft1.getMatrix().getE();
310: y = ft2.getMatrix().getF();
311: }
312:
313: if (to1 != null) {
314: AbstractSVGTransform tt1 = (AbstractSVGTransform) to1.transforms
315: .lastElement();
316: AbstractSVGTransform tt2 = (AbstractSVGTransform) to2.transforms
317: .lastElement();
318:
319: if (type == SVGTransform.SVG_TRANSFORM_SCALE) {
320: x += interpolation1 * (tt1.getMatrix().getA() - x);
321: y += interpolation2 * (tt2.getMatrix().getD() - y);
322: } else {
323: x += interpolation1 * (tt1.getMatrix().getE() - x);
324: y += interpolation2 * (tt2.getMatrix().getF() - y);
325: }
326: }
327:
328: if (type == SVGTransform.SVG_TRANSFORM_SCALE) {
329: t.setScale(x, y);
330: } else {
331: t.setTranslate(x, y);
332: }
333:
334: // XXX Do better checking for changes.
335: res.hasChanged = true;
336:
337: return res;
338: }
339:
340: /**
341: * Performs a three-way interpolation between the specified values.
342: * value[123] and to[123] must all be single rotation transforms,
343: * or all null.
344: */
345: public static AnimatableTransformListValue interpolate(
346: AnimatableTransformListValue res,
347: AnimatableTransformListValue value1,
348: AnimatableTransformListValue value2,
349: AnimatableTransformListValue value3,
350: AnimatableTransformListValue to1,
351: AnimatableTransformListValue to2,
352: AnimatableTransformListValue to3, float interpolation1,
353: float interpolation2, float interpolation3,
354: AnimatableTransformListValue accumulation, int multiplier) {
355:
356: int accSize = accumulation == null ? 0
357: : accumulation.transforms.size();
358: int newSize = accSize * multiplier + 1;
359:
360: if (res == null) {
361: res = new AnimatableTransformListValue(to1.target);
362: res.transforms = new Vector(newSize);
363: res.transforms.setSize(newSize);
364: } else {
365: if (res.transforms == null) {
366: res.transforms = new Vector(newSize);
367: res.transforms.setSize(newSize);
368: } else if (res.transforms.size() != newSize) {
369: res.transforms.setSize(newSize);
370: }
371: }
372:
373: int index = 0;
374: for (int j = 0; j < multiplier; j++) {
375: for (int i = 0; i < accSize; i++, index++) {
376: res.transforms.setElementAt(accumulation.transforms
377: .elementAt(i), index);
378: }
379: }
380:
381: AbstractSVGTransform ft1 = (AbstractSVGTransform) value1.transforms
382: .lastElement();
383: AbstractSVGTransform ft2 = (AbstractSVGTransform) value2.transforms
384: .lastElement();
385: AbstractSVGTransform ft3 = (AbstractSVGTransform) value3.transforms
386: .lastElement();
387:
388: AbstractSVGTransform t = (AbstractSVGTransform) res.transforms
389: .elementAt(index);
390: if (t == null) {
391: t = new SVGOMTransform();
392: res.transforms.setElementAt(t, index);
393: }
394:
395: float x, y, r;
396: r = ft1.getAngle();
397: x = ft2.getX();
398: y = ft3.getY();
399:
400: if (to1 != null) {
401: AbstractSVGTransform tt1 = (AbstractSVGTransform) to1.transforms
402: .lastElement();
403: AbstractSVGTransform tt2 = (AbstractSVGTransform) to2.transforms
404: .lastElement();
405: AbstractSVGTransform tt3 = (AbstractSVGTransform) to3.transforms
406: .lastElement();
407:
408: r += interpolation1 * (tt1.getAngle() - r);
409: x += interpolation2 * (tt2.getX() - x);
410: y += interpolation3 * (tt3.getY() - y);
411: }
412: t.setRotate(r, x, y);
413:
414: // XXX Do better checking for changes.
415: res.hasChanged = true;
416:
417: return res;
418: }
419:
420: /**
421: * Gets the transforms.
422: */
423: public Iterator getTransforms() {
424: return transforms.iterator();
425: }
426:
427: /**
428: * Returns whether two values of this type can have their distance
429: * computed, as needed by paced animation.
430: */
431: public boolean canPace() {
432: return true;
433: }
434:
435: /**
436: * Returns the absolute distance between this value and the specified other
437: * value.
438: */
439: public float distanceTo(AnimatableValue other) {
440: AnimatableTransformListValue o = (AnimatableTransformListValue) other;
441: if (transforms.isEmpty() || o.transforms.isEmpty()) {
442: return 0f;
443: }
444: AbstractSVGTransform t1 = (AbstractSVGTransform) transforms
445: .lastElement();
446: AbstractSVGTransform t2 = (AbstractSVGTransform) o.transforms
447: .lastElement();
448: short type1 = t1.getType();
449: if (type1 != t2.getType()) {
450: return 0f;
451: }
452: SVGMatrix m1 = t1.getMatrix();
453: SVGMatrix m2 = t2.getMatrix();
454: switch (type1) {
455: case SVGTransform.SVG_TRANSFORM_TRANSLATE:
456: return Math.abs(m1.getE() - m2.getE())
457: + Math.abs(m1.getF() - m2.getF());
458: case SVGTransform.SVG_TRANSFORM_SCALE:
459: return Math.abs(m1.getA() - m2.getA())
460: + Math.abs(m1.getD() - m2.getD());
461: case SVGTransform.SVG_TRANSFORM_ROTATE:
462: case SVGTransform.SVG_TRANSFORM_SKEWX:
463: case SVGTransform.SVG_TRANSFORM_SKEWY:
464: return Math.abs(t1.getAngle() - t2.getAngle());
465: }
466: return 0f;
467: }
468:
469: /**
470: * Returns the distance between this value's first component and the
471: * specified other value's first component.
472: */
473: public float distanceTo1(AnimatableValue other) {
474: AnimatableTransformListValue o = (AnimatableTransformListValue) other;
475: if (transforms.isEmpty() || o.transforms.isEmpty()) {
476: return 0f;
477: }
478: AbstractSVGTransform t1 = (AbstractSVGTransform) transforms
479: .lastElement();
480: AbstractSVGTransform t2 = (AbstractSVGTransform) o.transforms
481: .lastElement();
482: short type1 = t1.getType();
483: if (type1 != t2.getType()) {
484: return 0f;
485: }
486: SVGMatrix m1 = t1.getMatrix();
487: SVGMatrix m2 = t2.getMatrix();
488: switch (type1) {
489: case SVGTransform.SVG_TRANSFORM_TRANSLATE:
490: return Math.abs(m1.getE() - m2.getE());
491: case SVGTransform.SVG_TRANSFORM_SCALE:
492: return Math.abs(m1.getA() - m2.getA());
493: case SVGTransform.SVG_TRANSFORM_ROTATE:
494: case SVGTransform.SVG_TRANSFORM_SKEWX:
495: case SVGTransform.SVG_TRANSFORM_SKEWY:
496: return Math.abs(t1.getAngle() - t2.getAngle());
497: }
498: return 0f;
499: }
500:
501: /**
502: * Returns the distance between this value's second component and the
503: * specified other value's second component.
504: */
505: public float distanceTo2(AnimatableValue other) {
506: AnimatableTransformListValue o = (AnimatableTransformListValue) other;
507: if (transforms.isEmpty() || o.transforms.isEmpty()) {
508: return 0f;
509: }
510: AbstractSVGTransform t1 = (AbstractSVGTransform) transforms
511: .lastElement();
512: AbstractSVGTransform t2 = (AbstractSVGTransform) o.transforms
513: .lastElement();
514: short type1 = t1.getType();
515: if (type1 != t2.getType()) {
516: return 0f;
517: }
518: SVGMatrix m1 = t1.getMatrix();
519: SVGMatrix m2 = t2.getMatrix();
520: switch (type1) {
521: case SVGTransform.SVG_TRANSFORM_TRANSLATE:
522: return Math.abs(m1.getF() - m2.getF());
523: case SVGTransform.SVG_TRANSFORM_SCALE:
524: return Math.abs(m1.getD() - m2.getD());
525: case SVGTransform.SVG_TRANSFORM_ROTATE:
526: return Math.abs(t1.getX() - t2.getX());
527: }
528: return 0f;
529: }
530:
531: /**
532: * Returns the distance between this value's third component and the
533: * specified other value's third component.
534: */
535: public float distanceTo3(AnimatableValue other) {
536: AnimatableTransformListValue o = (AnimatableTransformListValue) other;
537: if (transforms.isEmpty() || o.transforms.isEmpty()) {
538: return 0f;
539: }
540: AbstractSVGTransform t1 = (AbstractSVGTransform) transforms
541: .lastElement();
542: AbstractSVGTransform t2 = (AbstractSVGTransform) o.transforms
543: .lastElement();
544: short type1 = t1.getType();
545: if (type1 != t2.getType()) {
546: return 0f;
547: }
548: if (type1 == SVGTransform.SVG_TRANSFORM_ROTATE) {
549: return Math.abs(t1.getY() - t2.getY());
550: }
551: return 0f;
552: }
553:
554: /**
555: * Returns a zero value of this AnimatableValue's type. This returns an
556: * empty transform list.
557: */
558: public AnimatableValue getZeroValue() {
559: return new AnimatableTransformListValue(target, new Vector(5));
560: }
561:
562: /**
563: * Returns the CSS text representation of the value.
564: */
565: public String toStringRep() {
566: StringBuffer sb = new StringBuffer();
567: Iterator i = transforms.iterator();
568: while (i.hasNext()) {
569: AbstractSVGTransform t = (AbstractSVGTransform) i.next();
570: if (t == null) {
571: sb.append("null");
572: } else {
573: SVGMatrix m = t.getMatrix();
574: switch (t.getType()) {
575: case SVGTransform.SVG_TRANSFORM_TRANSLATE:
576: sb.append("translate(");
577: sb.append(m.getE());
578: sb.append(',');
579: sb.append(m.getF());
580: sb.append(')');
581: break;
582: case SVGTransform.SVG_TRANSFORM_SCALE:
583: sb.append("scale(");
584: sb.append(m.getA());
585: sb.append(',');
586: sb.append(m.getD());
587: sb.append(')');
588: break;
589: case SVGTransform.SVG_TRANSFORM_SKEWX:
590: sb.append("skewX(");
591: sb.append(t.getAngle());
592: sb.append(')');
593: break;
594: case SVGTransform.SVG_TRANSFORM_SKEWY:
595: sb.append("skewY(");
596: sb.append(t.getAngle());
597: sb.append(')');
598: break;
599: case SVGTransform.SVG_TRANSFORM_ROTATE:
600: sb.append("rotate(");
601: sb.append(t.getAngle());
602: sb.append(',');
603: sb.append(t.getX());
604: sb.append(',');
605: sb.append(t.getY());
606: sb.append(')');
607: break;
608: }
609: }
610: if (i.hasNext()) {
611: sb.append(' ');
612: }
613: }
614: return sb.toString();
615: }
616: }
|