001/* 002 * SVG Salamander 003 * Copyright (c) 2004, Mark McKay 004 * All rights reserved. 005 * 006 * Redistribution and use in source and binary forms, with or 007 * without modification, are permitted provided that the following 008 * conditions are met: 009 * 010 * - Redistributions of source code must retain the above 011 * copyright notice, this list of conditions and the following 012 * disclaimer. 013 * - Redistributions in binary form must reproduce the above 014 * copyright notice, this list of conditions and the following 015 * disclaimer in the documentation and/or other materials 016 * provided with the distribution. 017 * 018 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 019 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 020 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 021 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 022 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 023 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 025 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 026 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 027 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 028 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 029 * OF THE POSSIBILITY OF SUCH DAMAGE. 030 * 031 * Mark McKay can be contacted at mark@kitfox.com. Salamander and other 032 * projects can be found at http://www.kitfox.com 033 * 034 * Created on August 15, 2004, 2:51 AM 035 */ 036 037package com.kitfox.svg.animation; 038 039import com.kitfox.svg.SVGElement; 040import com.kitfox.svg.SVGException; 041import com.kitfox.svg.SVGLoaderHelper; 042import com.kitfox.svg.animation.parser.AnimTimeParser; 043import com.kitfox.svg.xml.StyleAttribute; 044import com.kitfox.svg.xml.XMLParseUtil; 045import java.awt.geom.AffineTransform; 046import java.util.regex.Pattern; 047import org.xml.sax.Attributes; 048import org.xml.sax.SAXException; 049 050 051/** 052 * @author Mark McKay 053 * @author <a href="mailto:mark@kitfox.com">Mark McKay</a> 054 */ 055public class AnimateTransform extends AnimateXform 056{ 057 public static final String TAG_NAME = "animateTransform"; 058 059// protected AffineTransform fromValue; 060// protected AffineTransform toValue; 061// protected double[] fromValue; //Transform parameters 062// protected double[] toValue; 063 private double[][] values; 064 private double[] keyTimes; 065 066 public static final int AT_REPLACE = 0; 067 public static final int AT_SUM = 1; 068 069 private int additive = AT_REPLACE; 070 071 public static final int TR_TRANSLATE = 0; 072 public static final int TR_ROTATE = 1; 073 public static final int TR_SCALE = 2; 074 public static final int TR_SKEWY = 3; 075 public static final int TR_SKEWX = 4; 076 public static final int TR_INVALID = 5; 077 078 private int xformType = TR_INVALID; 079 080 /** Creates a new instance of Animate */ 081 public AnimateTransform() 082 { 083 } 084 085 public String getTagName() 086 { 087 return TAG_NAME; 088 } 089 090 public void loaderStartElement(SVGLoaderHelper helper, Attributes attrs, SVGElement parent) throws SAXException 091 { 092 //Load style string 093 super.loaderStartElement(helper, attrs, parent); 094 095 //Type of matrix of transform. Should be one of the known names used to 096 // define matrix transforms 097 // valid types: translate, scale, rotate, skewX, skewY 098 // 'matrix' not valid for animation 099 String type = attrs.getValue("type").toLowerCase(); 100 if (type.equals("translate")) xformType = TR_TRANSLATE; 101 if (type.equals("rotate")) xformType = TR_ROTATE; 102 if (type.equals("scale")) xformType = TR_SCALE; 103 if (type.equals("skewx")) xformType = TR_SKEWX; 104 if (type.equals("skewy")) xformType = TR_SKEWY; 105 106 String fromStrn = attrs.getValue("from"); 107 String toStrn = attrs.getValue("to"); 108 if (fromStrn != null && toStrn != null) 109 { 110 //fromValue = parseSingleTransform(type + "(" + strn + ")"); 111 double[] fromValue = XMLParseUtil.parseDoubleList(fromStrn); 112 fromValue = validate(fromValue); 113 114 // toValue = parseSingleTransform(type + "(" + strn + ")"); 115 double[] toValue = XMLParseUtil.parseDoubleList(toStrn); 116 toValue = validate(toValue); 117 118 values = new double[][]{fromValue, toValue}; 119 keyTimes = new double[]{0, 1}; 120 } 121 122 String keyTimeStrn = attrs.getValue("keyTimes"); 123 String valuesStrn = attrs.getValue("values"); 124 if (keyTimeStrn != null && valuesStrn != null) 125 { 126 keyTimes = XMLParseUtil.parseDoubleList(keyTimeStrn); 127 128 String[] valueList = Pattern.compile(";").split(valuesStrn); 129 values = new double[valueList.length][]; 130 for (int i = 0; i < valueList.length; i++) 131 { 132 double[] list = XMLParseUtil.parseDoubleList(valueList[i]); 133 values[i] = validate(list); 134 } 135 } 136 137 //Check our additive state 138 String additive = attrs.getValue("additive"); 139 if (additive != null) 140 { 141 if (additive.equals("sum")) this.additive = AT_SUM; 142 } 143 } 144 145 /** 146 * Check list size against current xform type and ensure list 147 * is expanded to a standard list size 148 */ 149 private double[] validate(double[] paramList) 150 { 151 switch (xformType) 152 { 153 case TR_SCALE: 154 { 155 if (paramList == null) 156 { 157 paramList = new double[]{1, 1}; 158 } 159 else if (paramList.length == 1) 160 { 161 paramList = new double[]{paramList[0], paramList[0]}; 162 163// double[] tmp = paramList; 164// paramList = new double[2]; 165// paramList[0] = paramList[1] = tmp[0]; 166 } 167 } 168 } 169 170 return paramList; 171 } 172 173 /** 174 * Evaluates this animation element for the passed interpolation time. Interp 175 * must be on [0..1]. 176 */ 177 public AffineTransform eval(AffineTransform xform, double interp) 178 { 179 int idx = 0; 180 for (; idx < keyTimes.length - 1; idx++) 181 { 182 if (interp >= keyTimes[idx]) 183 { 184 idx--; 185 if (idx < 0) idx = 0; 186 break; 187 } 188 } 189 190 double spanStartTime = keyTimes[idx]; 191 double spanEndTime = keyTimes[idx + 1]; 192// double span = spanStartTime - spanEndTime; 193 194 interp = (interp - spanStartTime) / (spanEndTime - spanStartTime); 195 double[] fromValue = values[idx]; 196 double[] toValue = values[idx + 1]; 197 198 switch (xformType) 199 { 200 case TR_TRANSLATE: 201 { 202 double x0 = fromValue.length >= 1 ? fromValue[0] : 0; 203 double x1 = toValue.length >= 1 ? toValue[0] : 0; 204 double y0 = fromValue.length >= 2 ? fromValue[1] : 0; 205 double y1 = toValue.length >= 2 ? toValue[1] : 0; 206 207 double x = lerp(x0, x1, interp); 208 double y = lerp(y0, y1, interp); 209 210 xform.setToTranslation(x, y); 211 break; 212 } 213 case TR_ROTATE: 214 { 215 double x1 = fromValue.length == 3 ? fromValue[1] : 0; 216 double y1 = fromValue.length == 3 ? fromValue[2] : 0; 217 double x2 = toValue.length == 3 ? toValue[1] : 0; 218 double y2 = toValue.length == 3 ? toValue[2] : 0; 219 220 double theta = lerp(fromValue[0], toValue[0], interp); 221 double x = lerp(x1, x2, interp); 222 double y = lerp(y1, y2, interp); 223 xform.setToRotation(Math.toRadians(theta), x, y); 224 break; 225 } 226 case TR_SCALE: 227 { 228 double x0 = fromValue.length >= 1 ? fromValue[0] : 1; 229 double x1 = toValue.length >= 1 ? toValue[0] : 1; 230 double y0 = fromValue.length >= 2 ? fromValue[1] : 1; 231 double y1 = toValue.length >= 2 ? toValue[1] : 1; 232 233 double x = lerp(x0, x1, interp); 234 double y = lerp(y0, y1, interp); 235 xform.setToScale(x, y); 236 break; 237 } 238 case TR_SKEWX: 239 { 240 double x = lerp(fromValue[0], toValue[0], interp); 241 xform.setToShear(Math.toRadians(x), 0.0); 242 break; 243 } 244 case TR_SKEWY: 245 { 246 double y = lerp(fromValue[0], toValue[0], interp); 247 xform.setToShear(0.0, Math.toRadians(y)); 248 break; 249 } 250 default: 251 xform.setToIdentity(); 252 break; 253 } 254 255 return xform; 256 } 257 258 protected void rebuild(AnimTimeParser animTimeParser) throws SVGException 259 { 260 super.rebuild(animTimeParser); 261 262 StyleAttribute sty = new StyleAttribute(); 263 264 if (getPres(sty.setName("type"))) 265 { 266 String strn = sty.getStringValue().toLowerCase(); 267 if (strn.equals("translate")) xformType = TR_TRANSLATE; 268 if (strn.equals("rotate")) xformType = TR_ROTATE; 269 if (strn.equals("scale")) xformType = TR_SCALE; 270 if (strn.equals("skewx")) xformType = TR_SKEWX; 271 if (strn.equals("skewy")) xformType = TR_SKEWY; 272 } 273 274 String fromStrn = null; 275 if (getPres(sty.setName("from"))) 276 { 277 fromStrn = sty.getStringValue(); 278 } 279 280 String toStrn = null; 281 if (getPres(sty.setName("to"))) 282 { 283 toStrn = sty.getStringValue(); 284 } 285 286 if (fromStrn != null && toStrn != null) 287 { 288 double[] fromValue = XMLParseUtil.parseDoubleList(fromStrn); 289 fromValue = validate(fromValue); 290 291 double[] toValue = XMLParseUtil.parseDoubleList(toStrn); 292 toValue = validate(toValue); 293 294 values = new double[][]{fromValue, toValue}; 295 } 296 297 String keyTimeStrn = null; 298 if (getPres(sty.setName("keyTimes"))) 299 { 300 keyTimeStrn = sty.getStringValue(); 301 } 302 303 String valuesStrn = null; 304 if (getPres(sty.setName("values"))) 305 { 306 valuesStrn = sty.getStringValue(); 307 } 308 309 if (keyTimeStrn != null && valuesStrn != null) 310 { 311 keyTimes = XMLParseUtil.parseDoubleList(keyTimeStrn); 312 313 String[] valueList = Pattern.compile(";").split(valuesStrn); 314 values = new double[valueList.length][]; 315 for (int i = 0; i < valueList.length; i++) 316 { 317 double[] list = XMLParseUtil.parseDoubleList(valueList[i]); 318 values[i] = validate(list); 319 } 320 } 321 322 //Check our additive state 323 324 if (getPres(sty.setName("additive"))) 325 { 326 String strn = sty.getStringValue().toLowerCase(); 327 if (strn.equals("sum")) this.additive = AT_SUM; 328 } 329 } 330 331 /** 332 * @return the values 333 */ 334 public double[][] getValues() 335 { 336 return values; 337 } 338 339 /** 340 * @param values the values to set 341 */ 342 public void setValues(double[][] values) 343 { 344 this.values = values; 345 } 346 347 /** 348 * @return the keyTimes 349 */ 350 public double[] getKeyTimes() 351 { 352 return keyTimes; 353 } 354 355 /** 356 * @param keyTimes the keyTimes to set 357 */ 358 public void setKeyTimes(double[] keyTimes) 359 { 360 this.keyTimes = keyTimes; 361 } 362 363 /** 364 * @return the additive 365 */ 366 public int getAdditive() 367 { 368 return additive; 369 } 370 371 /** 372 * @param additive the additive to set 373 */ 374 public void setAdditive(int additive) 375 { 376 this.additive = additive; 377 } 378 379 /** 380 * @return the xformType 381 */ 382 public int getXformType() 383 { 384 return xformType; 385 } 386 387 /** 388 * @param xformType the xformType to set 389 */ 390 public void setXformType(int xformType) 391 { 392 this.xformType = xformType; 393 } 394}