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 @Override 086 public String getTagName() 087 { 088 return TAG_NAME; 089 } 090 091 @Override 092 public void loaderStartElement(SVGLoaderHelper helper, Attributes attrs, SVGElement parent) throws SAXException 093 { 094 //Load style string 095 super.loaderStartElement(helper, attrs, parent); 096 097 //Type of matrix of transform. Should be one of the known names used to 098 // define matrix transforms 099 // valid types: translate, scale, rotate, skewX, skewY 100 // 'matrix' not valid for animation 101 String type = attrs.getValue("type").toLowerCase(); 102 if (type.equals("translate")) xformType = TR_TRANSLATE; 103 if (type.equals("rotate")) xformType = TR_ROTATE; 104 if (type.equals("scale")) xformType = TR_SCALE; 105 if (type.equals("skewx")) xformType = TR_SKEWX; 106 if (type.equals("skewy")) xformType = TR_SKEWY; 107 108 String fromStrn = attrs.getValue("from"); 109 String toStrn = attrs.getValue("to"); 110 if (fromStrn != null && toStrn != null) 111 { 112 //fromValue = parseSingleTransform(type + "(" + strn + ")"); 113 double[] fromValue = XMLParseUtil.parseDoubleList(fromStrn); 114 fromValue = validate(fromValue); 115 116 // toValue = parseSingleTransform(type + "(" + strn + ")"); 117 double[] toValue = XMLParseUtil.parseDoubleList(toStrn); 118 toValue = validate(toValue); 119 120 values = new double[][]{fromValue, toValue}; 121 keyTimes = new double[]{0, 1}; 122 } 123 124 String keyTimeStrn = attrs.getValue("keyTimes"); 125 String valuesStrn = attrs.getValue("values"); 126 if (keyTimeStrn != null && valuesStrn != null) 127 { 128 keyTimes = XMLParseUtil.parseDoubleList(keyTimeStrn); 129 130 String[] valueList = Pattern.compile(";").split(valuesStrn); 131 values = new double[valueList.length][]; 132 for (int i = 0; i < valueList.length; i++) 133 { 134 double[] list = XMLParseUtil.parseDoubleList(valueList[i]); 135 values[i] = validate(list); 136 } 137 } 138 139 //Check our additive state 140 String additive = attrs.getValue("additive"); 141 if (additive != null) 142 { 143 if (additive.equals("sum")) this.additive = AT_SUM; 144 } 145 } 146 147 /** 148 * Check list size against current xform type and ensure list 149 * is expanded to a standard list size 150 */ 151 private double[] validate(double[] paramList) 152 { 153 switch (xformType) 154 { 155 case TR_SCALE: 156 { 157 if (paramList == null) 158 { 159 paramList = new double[]{1, 1}; 160 } 161 else if (paramList.length == 1) 162 { 163 paramList = new double[]{paramList[0], paramList[0]}; 164 165// double[] tmp = paramList; 166// paramList = new double[2]; 167// paramList[0] = paramList[1] = tmp[0]; 168 } 169 } 170 } 171 172 return paramList; 173 } 174 175 /** 176 * Evaluates this animation element for the passed interpolation time. Interp 177 * must be on [0..1]. 178 */ 179 @Override 180 public AffineTransform eval(AffineTransform xform, double interp) 181 { 182 int idx = 0; 183 for (; idx < keyTimes.length - 1; idx++) 184 { 185 if (interp >= keyTimes[idx]) 186 { 187 idx--; 188 if (idx < 0) idx = 0; 189 break; 190 } 191 } 192 193 double spanStartTime = keyTimes[idx]; 194 double spanEndTime = keyTimes[idx + 1]; 195// double span = spanStartTime - spanEndTime; 196 197 interp = (interp - spanStartTime) / (spanEndTime - spanStartTime); 198 double[] fromValue = values[idx]; 199 double[] toValue = values[idx + 1]; 200 201 switch (xformType) 202 { 203 case TR_TRANSLATE: 204 { 205 double x0 = fromValue.length >= 1 ? fromValue[0] : 0; 206 double x1 = toValue.length >= 1 ? toValue[0] : 0; 207 double y0 = fromValue.length >= 2 ? fromValue[1] : 0; 208 double y1 = toValue.length >= 2 ? toValue[1] : 0; 209 210 double x = lerp(x0, x1, interp); 211 double y = lerp(y0, y1, interp); 212 213 xform.setToTranslation(x, y); 214 break; 215 } 216 case TR_ROTATE: 217 { 218 double x1 = fromValue.length == 3 ? fromValue[1] : 0; 219 double y1 = fromValue.length == 3 ? fromValue[2] : 0; 220 double x2 = toValue.length == 3 ? toValue[1] : 0; 221 double y2 = toValue.length == 3 ? toValue[2] : 0; 222 223 double theta = lerp(fromValue[0], toValue[0], interp); 224 double x = lerp(x1, x2, interp); 225 double y = lerp(y1, y2, interp); 226 xform.setToRotation(Math.toRadians(theta), x, y); 227 break; 228 } 229 case TR_SCALE: 230 { 231 double x0 = fromValue.length >= 1 ? fromValue[0] : 1; 232 double x1 = toValue.length >= 1 ? toValue[0] : 1; 233 double y0 = fromValue.length >= 2 ? fromValue[1] : 1; 234 double y1 = toValue.length >= 2 ? toValue[1] : 1; 235 236 double x = lerp(x0, x1, interp); 237 double y = lerp(y0, y1, interp); 238 xform.setToScale(x, y); 239 break; 240 } 241 case TR_SKEWX: 242 { 243 double x = lerp(fromValue[0], toValue[0], interp); 244 xform.setToShear(Math.toRadians(x), 0.0); 245 break; 246 } 247 case TR_SKEWY: 248 { 249 double y = lerp(fromValue[0], toValue[0], interp); 250 xform.setToShear(0.0, Math.toRadians(y)); 251 break; 252 } 253 default: 254 xform.setToIdentity(); 255 break; 256 } 257 258 return xform; 259 } 260 261 @Override 262 protected void rebuild(AnimTimeParser animTimeParser) throws SVGException 263 { 264 super.rebuild(animTimeParser); 265 266 StyleAttribute sty = new StyleAttribute(); 267 268 if (getPres(sty.setName("type"))) 269 { 270 String strn = sty.getStringValue().toLowerCase(); 271 if (strn.equals("translate")) xformType = TR_TRANSLATE; 272 if (strn.equals("rotate")) xformType = TR_ROTATE; 273 if (strn.equals("scale")) xformType = TR_SCALE; 274 if (strn.equals("skewx")) xformType = TR_SKEWX; 275 if (strn.equals("skewy")) xformType = TR_SKEWY; 276 } 277 278 String fromStrn = null; 279 if (getPres(sty.setName("from"))) 280 { 281 fromStrn = sty.getStringValue(); 282 } 283 284 String toStrn = null; 285 if (getPres(sty.setName("to"))) 286 { 287 toStrn = sty.getStringValue(); 288 } 289 290 if (fromStrn != null && toStrn != null) 291 { 292 double[] fromValue = XMLParseUtil.parseDoubleList(fromStrn); 293 fromValue = validate(fromValue); 294 295 double[] toValue = XMLParseUtil.parseDoubleList(toStrn); 296 toValue = validate(toValue); 297 298 values = new double[][]{fromValue, toValue}; 299 } 300 301 String keyTimeStrn = null; 302 if (getPres(sty.setName("keyTimes"))) 303 { 304 keyTimeStrn = sty.getStringValue(); 305 } 306 307 String valuesStrn = null; 308 if (getPres(sty.setName("values"))) 309 { 310 valuesStrn = sty.getStringValue(); 311 } 312 313 if (keyTimeStrn != null && valuesStrn != null) 314 { 315 keyTimes = XMLParseUtil.parseDoubleList(keyTimeStrn); 316 317 String[] valueList = Pattern.compile(";").split(valuesStrn); 318 values = new double[valueList.length][]; 319 for (int i = 0; i < valueList.length; i++) 320 { 321 double[] list = XMLParseUtil.parseDoubleList(valueList[i]); 322 values[i] = validate(list); 323 } 324 } 325 326 //Check our additive state 327 328 if (getPres(sty.setName("additive"))) 329 { 330 String strn = sty.getStringValue().toLowerCase(); 331 if (strn.equals("sum")) this.additive = AT_SUM; 332 } 333 } 334 335 /** 336 * @return the values 337 */ 338 public double[][] getValues() 339 { 340 return values; 341 } 342 343 /** 344 * @param values the values to set 345 */ 346 public void setValues(double[][] values) 347 { 348 this.values = values; 349 } 350 351 /** 352 * @return the keyTimes 353 */ 354 public double[] getKeyTimes() 355 { 356 return keyTimes; 357 } 358 359 /** 360 * @param keyTimes the keyTimes to set 361 */ 362 public void setKeyTimes(double[] keyTimes) 363 { 364 this.keyTimes = keyTimes; 365 } 366 367 /** 368 * @return the additive 369 */ 370 public int getAdditive() 371 { 372 return additive; 373 } 374 375 /** 376 * @param additive the additive to set 377 */ 378 public void setAdditive(int additive) 379 { 380 this.additive = additive; 381 } 382 383 /** 384 * @return the xformType 385 */ 386 public int getXformType() 387 { 388 return xformType; 389 } 390 391 /** 392 * @param xformType the xformType to set 393 */ 394 public void setXformType(int xformType) 395 { 396 this.xformType = xformType; 397 } 398}