001 /*
002 * Animate.java
003 *
004 * The Salamander Project - 2D and 3D graphics libraries in Java
005 * Copyright (C) 2004 Mark McKay
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * You should have received a copy of the GNU Lesser General Public
018 * License along with this library; if not, write to the Free Software
019 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020 *
021 * Mark McKay can be contacted at mark@kitfox.com. Salamander and other
022 * projects can be found at http://www.kitfox.com
023 *
024 * Created on August 15, 2004, 2:51 AM
025 */
026
027 package com.kitfox.svg.animation;
028
029 import com.kitfox.svg.SVGElement;
030 import com.kitfox.svg.SVGException;
031 import com.kitfox.svg.SVGLoaderHelper;
032 import com.kitfox.svg.animation.parser.AnimTimeParser;
033 import com.kitfox.svg.xml.StyleAttribute;
034 import com.kitfox.svg.xml.XMLParseUtil;
035 import java.awt.geom.AffineTransform;
036 import java.util.regex.Pattern;
037 import org.xml.sax.Attributes;
038 import org.xml.sax.SAXException;
039
040
041 /**
042 * @author Mark McKay
043 * @author <a href="mailto:mark@kitfox.com">Mark McKay</a>
044 */
045 public class AnimateTransform extends AnimateXform
046 {
047 // protected AffineTransform fromValue;
048 // protected AffineTransform toValue;
049 // protected double[] fromValue; //Transform parameters
050 // protected double[] toValue;
051 protected double[][] values;
052 protected double[] keyTimes;
053
054 public static final int AT_REPLACE = 0;
055 public static final int AT_SUM = 1;
056
057 protected int additive = AT_REPLACE;
058
059 public static final int TR_TRANSLATE = 0;
060 public static final int TR_ROTATE = 1;
061 public static final int TR_SCALE = 2;
062 public static final int TR_SKEWY = 3;
063 public static final int TR_SKEWX = 4;
064 public static final int TR_INVALID = 5;
065
066 protected int xformType = TR_INVALID;
067
068 /** Creates a new instance of Animate */
069 public AnimateTransform()
070 {
071 }
072
073 public void loaderStartElement(SVGLoaderHelper helper, Attributes attrs, SVGElement parent) throws SAXException
074 {
075 //Load style string
076 super.loaderStartElement(helper, attrs, parent);
077
078 //Type of matrix of transform. Should be one of the known names used to
079 // define matrix transforms
080 // valid types: translate, scale, rotate, skewX, skewY
081 // 'matrix' not valid for animation
082 String type = attrs.getValue("type").toLowerCase();
083 if (type.equals("translate")) xformType = TR_TRANSLATE;
084 if (type.equals("rotate")) xformType = TR_ROTATE;
085 if (type.equals("scale")) xformType = TR_SCALE;
086 if (type.equals("skewx")) xformType = TR_SKEWX;
087 if (type.equals("skewy")) xformType = TR_SKEWY;
088
089 String fromStrn = attrs.getValue("from");
090 String toStrn = attrs.getValue("to");
091 if (fromStrn != null && toStrn != null)
092 {
093 //fromValue = parseSingleTransform(type + "(" + strn + ")");
094 double[] fromValue = XMLParseUtil.parseDoubleList(fromStrn);
095 fromValue = validate(fromValue);
096
097 // toValue = parseSingleTransform(type + "(" + strn + ")");
098 double[] toValue = XMLParseUtil.parseDoubleList(toStrn);
099 toValue = validate(toValue);
100
101 values = new double[][]{fromValue, toValue};
102 keyTimes = new double[]{0, 1};
103 }
104
105 String keyTimeStrn = attrs.getValue("keyTimes");
106 String valuesStrn = attrs.getValue("values");
107 if (keyTimeStrn != null && valuesStrn != null)
108 {
109 keyTimes = XMLParseUtil.parseDoubleList(keyTimeStrn);
110
111 String[] valueList = Pattern.compile(";").split(valuesStrn);
112 values = new double[valueList.length][];
113 for (int i = 0; i < valueList.length; i++)
114 {
115 double[] list = XMLParseUtil.parseDoubleList(valueList[i]);
116 values[i] = validate(list);
117 }
118 }
119
120 //Check our additive state
121 String additive = attrs.getValue("additive");
122 if (additive != null)
123 {
124 if (additive.equals("sum")) this.additive = AT_SUM;
125 }
126 }
127
128 /**
129 * Check list size against current xform type and ensure list
130 * is expanded to a standard list size
131 */
132 private double[] validate(double[] paramList)
133 {
134 switch (xformType)
135 {
136 case TR_SCALE:
137 {
138 if (paramList == null)
139 {
140 paramList = new double[]{1, 1};
141 }
142 else if (paramList.length == 1)
143 {
144 paramList = new double[]{paramList[0], paramList[0]};
145
146 // double[] tmp = paramList;
147 // paramList = new double[2];
148 // paramList[0] = paramList[1] = tmp[0];
149 }
150 }
151 }
152
153 return paramList;
154 }
155
156 /**
157 * Evaluates this animation element for the passed interpolation time. Interp
158 * must be on [0..1].
159 */
160 public AffineTransform eval(AffineTransform xform, double interp)
161 {
162 int idx = 0;
163 for (; idx < keyTimes.length - 1; idx++)
164 {
165 if (interp >= keyTimes[idx])
166 {
167 idx--;
168 if (idx < 0) idx = 0;
169 break;
170 }
171 }
172
173 double spanStartTime = keyTimes[idx];
174 double spanEndTime = keyTimes[idx + 1];
175 // double span = spanStartTime - spanEndTime;
176
177 interp = (interp - spanStartTime) / (spanEndTime - spanStartTime);
178 double[] fromValue = values[idx];
179 double[] toValue = values[idx + 1];
180
181 switch (xformType)
182 {
183 case TR_TRANSLATE:
184 {
185 double x = (1.0 - interp) * fromValue[0] + interp * toValue[0];
186 double y = (1.0 - interp) * fromValue[1] + interp * toValue[1];
187 xform.setToTranslation(x, y);
188 break;
189 }
190 case TR_ROTATE:
191 {
192 double x1 = fromValue.length == 3 ? fromValue[1] : 0;
193 double y1 = fromValue.length == 3 ? fromValue[2] : 0;
194 double x2 = toValue.length == 3 ? toValue[1] : 0;
195 double y2 = toValue.length == 3 ? toValue[2] : 0;
196
197 double theta = (1.0 - interp) * fromValue[0] + interp * toValue[0];
198 double x = (1.0 - interp) * x1 + interp * x2;
199 double y = (1.0 - interp) * y1 + interp * y2;
200 xform.setToRotation(Math.toRadians(theta), x, y);
201 break;
202 }
203 case TR_SCALE:
204 {
205 double x = (1.0 - interp) * fromValue[0] + interp * toValue[0];
206 double y = (1.0 - interp) * fromValue[1] + interp * toValue[1];
207 xform.setToScale(x, y);
208 break;
209 }
210 case TR_SKEWX:
211 {
212 double x = (1.0 - interp) * fromValue[0] + interp * toValue[0];
213 xform.setToShear(Math.toRadians(x), 0.0);
214 break;
215 }
216 case TR_SKEWY:
217 {
218 double y = (1.0 - interp) * fromValue[0] + interp * toValue[0];
219 xform.setToShear(0.0, Math.toRadians(y));
220 break;
221 }
222 default:
223 xform.setToIdentity();
224 break;
225 }
226
227 return xform;
228 }
229
230 protected void rebuild(AnimTimeParser animTimeParser) throws SVGException
231 {
232 super.rebuild(animTimeParser);
233
234 StyleAttribute sty = new StyleAttribute();
235
236 if (getPres(sty.setName("type")))
237 {
238 String strn = sty.getStringValue().toLowerCase();
239 if (strn.equals("translate")) xformType = TR_TRANSLATE;
240 if (strn.equals("rotate")) xformType = TR_ROTATE;
241 if (strn.equals("scale")) xformType = TR_SCALE;
242 if (strn.equals("skewx")) xformType = TR_SKEWX;
243 if (strn.equals("skewy")) xformType = TR_SKEWY;
244 }
245
246 String fromStrn = null;
247 if (getPres(sty.setName("from")))
248 {
249 fromStrn = sty.getStringValue();
250 }
251
252 String toStrn = null;
253 if (getPres(sty.setName("to")))
254 {
255 toStrn = sty.getStringValue();
256 }
257
258 if (fromStrn != null && toStrn != null)
259 {
260 double[] fromValue = XMLParseUtil.parseDoubleList(fromStrn);
261 fromValue = validate(fromValue);
262
263 double[] toValue = XMLParseUtil.parseDoubleList(toStrn);
264 toValue = validate(toValue);
265
266 values = new double[][]{fromValue, toValue};
267 }
268
269 String keyTimeStrn = null;
270 if (getPres(sty.setName("keyTimes")))
271 {
272 keyTimeStrn = sty.getStringValue();
273 }
274
275 String valuesStrn = null;
276 if (getPres(sty.setName("values")))
277 {
278 valuesStrn = sty.getStringValue();
279 }
280
281 if (keyTimeStrn != null && valuesStrn != null)
282 {
283 keyTimes = XMLParseUtil.parseDoubleList(keyTimeStrn);
284
285 String[] valueList = Pattern.compile(";").split(valuesStrn);
286 values = new double[valueList.length][];
287 for (int i = 0; i < valueList.length; i++)
288 {
289 double[] list = XMLParseUtil.parseDoubleList(valueList[i]);
290 values[i] = validate(list);
291 }
292 }
293
294 //Check our additive state
295
296 if (getPres(sty.setName("additive")))
297 {
298 String strn = sty.getStringValue().toLowerCase();
299 if (strn.equals("sum")) this.additive = AT_SUM;
300 }
301 }
302 }