/* * Animate.java * * The Salamander Project - 2D and 3D graphics libraries in Java * Copyright (C) 2004 Mark McKay * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Mark McKay can be contacted at mark@kitfox.com. Salamander and other * projects can be found at http://www.kitfox.com * * Created on August 15, 2004, 2:51 AM */ package com.kitfox.svg.animation; import com.kitfox.svg.SVGElement; import com.kitfox.svg.SVGException; import com.kitfox.svg.SVGLoaderHelper; import com.kitfox.svg.animation.parser.AnimTimeParser; import com.kitfox.svg.xml.StyleAttribute; import com.kitfox.svg.xml.XMLParseUtil; import java.awt.geom.AffineTransform; import java.util.regex.Pattern; import org.xml.sax.Attributes; import org.xml.sax.SAXException; /** * @author Mark McKay * @author Mark McKay */ public class AnimateTransform extends AnimateXform { // protected AffineTransform fromValue; // protected AffineTransform toValue; // protected double[] fromValue; //Transform parameters // protected double[] toValue; protected double[][] values; protected double[] keyTimes; public static final int AT_REPLACE = 0; public static final int AT_SUM = 1; protected int additive = AT_REPLACE; public static final int TR_TRANSLATE = 0; public static final int TR_ROTATE = 1; public static final int TR_SCALE = 2; public static final int TR_SKEWY = 3; public static final int TR_SKEWX = 4; public static final int TR_INVALID = 5; protected int xformType = TR_INVALID; /** Creates a new instance of Animate */ public AnimateTransform() { } public void loaderStartElement(SVGLoaderHelper helper, Attributes attrs, SVGElement parent) throws SAXException { //Load style string super.loaderStartElement(helper, attrs, parent); //Type of matrix of transform. Should be one of the known names used to // define matrix transforms // valid types: translate, scale, rotate, skewX, skewY // 'matrix' not valid for animation String type = attrs.getValue("type").toLowerCase(); if (type.equals("translate")) xformType = TR_TRANSLATE; if (type.equals("rotate")) xformType = TR_ROTATE; if (type.equals("scale")) xformType = TR_SCALE; if (type.equals("skewx")) xformType = TR_SKEWX; if (type.equals("skewy")) xformType = TR_SKEWY; String fromStrn = attrs.getValue("from"); String toStrn = attrs.getValue("to"); if (fromStrn != null && toStrn != null) { //fromValue = parseSingleTransform(type + "(" + strn + ")"); double[] fromValue = XMLParseUtil.parseDoubleList(fromStrn); fromValue = validate(fromValue); // toValue = parseSingleTransform(type + "(" + strn + ")"); double[] toValue = XMLParseUtil.parseDoubleList(toStrn); toValue = validate(toValue); values = new double[][]{fromValue, toValue}; keyTimes = new double[]{0, 1}; } String keyTimeStrn = attrs.getValue("keyTimes"); String valuesStrn = attrs.getValue("values"); if (keyTimeStrn != null && valuesStrn != null) { keyTimes = XMLParseUtil.parseDoubleList(keyTimeStrn); String[] valueList = Pattern.compile(";").split(valuesStrn); values = new double[valueList.length][]; for (int i = 0; i < valueList.length; i++) { double[] list = XMLParseUtil.parseDoubleList(valueList[i]); values[i] = validate(list); } } //Check our additive state String additive = attrs.getValue("additive"); if (additive != null) { if (additive.equals("sum")) this.additive = AT_SUM; } } /** * Check list size against current xform type and ensure list * is expanded to a standard list size */ private double[] validate(double[] paramList) { switch (xformType) { case TR_SCALE: { if (paramList == null) { paramList = new double[]{1, 1}; } else if (paramList.length == 1) { paramList = new double[]{paramList[0], paramList[0]}; // double[] tmp = paramList; // paramList = new double[2]; // paramList[0] = paramList[1] = tmp[0]; } } } return paramList; } /** * Evaluates this animation element for the passed interpolation time. Interp * must be on [0..1]. */ public AffineTransform eval(AffineTransform xform, double interp) { int idx = 0; for (; idx < keyTimes.length - 1; idx++) { if (interp >= keyTimes[idx]) { idx--; if (idx < 0) idx = 0; break; } } double spanStartTime = keyTimes[idx]; double spanEndTime = keyTimes[idx + 1]; // double span = spanStartTime - spanEndTime; interp = (interp - spanStartTime) / (spanEndTime - spanStartTime); double[] fromValue = values[idx]; double[] toValue = values[idx + 1]; switch (xformType) { case TR_TRANSLATE: { double x0 = fromValue.length > 1 ? fromValue[0] : 0; double x1 = toValue.length > 1 ? toValue[0] : 0; double y0 = fromValue.length > 2 ? fromValue[1] : 0; double y1 = toValue.length > 2 ? toValue[1] : 0; double x = lerp(x0, x1, interp); double y = lerp(y0, y1, interp); xform.setToTranslation(x, y); break; } case TR_ROTATE: { double x1 = fromValue.length == 3 ? fromValue[1] : 0; double y1 = fromValue.length == 3 ? fromValue[2] : 0; double x2 = toValue.length == 3 ? toValue[1] : 0; double y2 = toValue.length == 3 ? toValue[2] : 0; double theta = lerp(fromValue[0], toValue[0], interp); double x = lerp(x1, x2, interp); double y = lerp(y1, y2, interp); xform.setToRotation(Math.toRadians(theta), x, y); break; } case TR_SCALE: { double x0 = fromValue.length > 1 ? fromValue[0] : 1; double x1 = toValue.length > 1 ? toValue[0] : 1; double y0 = fromValue.length > 2 ? fromValue[1] : 1; double y1 = toValue.length > 2 ? toValue[1] : 1; double x = lerp(x0, x1, interp); double y = lerp(y0, y1, interp); xform.setToScale(x, y); break; } case TR_SKEWX: { double x = lerp(fromValue[0], toValue[0], interp); xform.setToShear(Math.toRadians(x), 0.0); break; } case TR_SKEWY: { double y = lerp(fromValue[0], toValue[0], interp); xform.setToShear(0.0, Math.toRadians(y)); break; } default: xform.setToIdentity(); break; } return xform; } protected void rebuild(AnimTimeParser animTimeParser) throws SVGException { super.rebuild(animTimeParser); StyleAttribute sty = new StyleAttribute(); if (getPres(sty.setName("type"))) { String strn = sty.getStringValue().toLowerCase(); if (strn.equals("translate")) xformType = TR_TRANSLATE; if (strn.equals("rotate")) xformType = TR_ROTATE; if (strn.equals("scale")) xformType = TR_SCALE; if (strn.equals("skewx")) xformType = TR_SKEWX; if (strn.equals("skewy")) xformType = TR_SKEWY; } String fromStrn = null; if (getPres(sty.setName("from"))) { fromStrn = sty.getStringValue(); } String toStrn = null; if (getPres(sty.setName("to"))) { toStrn = sty.getStringValue(); } if (fromStrn != null && toStrn != null) { double[] fromValue = XMLParseUtil.parseDoubleList(fromStrn); fromValue = validate(fromValue); double[] toValue = XMLParseUtil.parseDoubleList(toStrn); toValue = validate(toValue); values = new double[][]{fromValue, toValue}; } String keyTimeStrn = null; if (getPres(sty.setName("keyTimes"))) { keyTimeStrn = sty.getStringValue(); } String valuesStrn = null; if (getPres(sty.setName("values"))) { valuesStrn = sty.getStringValue(); } if (keyTimeStrn != null && valuesStrn != null) { keyTimes = XMLParseUtil.parseDoubleList(keyTimeStrn); String[] valueList = Pattern.compile(";").split(valuesStrn); values = new double[valueList.length][]; for (int i = 0; i < valueList.length; i++) { double[] list = XMLParseUtil.parseDoubleList(valueList[i]); values[i] = validate(list); } } //Check our additive state if (getPres(sty.setName("additive"))) { String strn = sty.getStringValue().toLowerCase(); if (strn.equals("sum")) this.additive = AT_SUM; } } }