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.ColorTable; 044import com.kitfox.svg.xml.StyleAttribute; 045import com.kitfox.svg.xml.XMLParseUtil; 046import java.awt.Color; 047import java.awt.geom.AffineTransform; 048import java.awt.geom.GeneralPath; 049import java.awt.geom.PathIterator; 050import org.xml.sax.Attributes; 051import org.xml.sax.SAXException; 052 053 054/** 055 * Animate is a really annoying morphic tag that could represent a real value, 056 * a color or a path 057 * 058 * @author Mark McKay 059 * @author <a href="mailto:mark@kitfox.com">Mark McKay</a> 060 */ 061public class Animate extends AnimateBase implements AnimateColorIface 062{ 063 public static final String TAG_NAME = "animate"; 064 065// StyleAttribute retAttrib = new StyleAttribute 066 public static final int DT_REAL = 0; 067 public static final int DT_COLOR = 1; 068 public static final int DT_PATH = 2; 069 int dataType = DT_REAL; 070 071 protected double fromValue = Double.NaN; 072 protected double toValue = Double.NaN; 073 protected double byValue = Double.NaN; 074 protected double[] valuesValue; 075 076 protected Color fromColor = null; 077 protected Color toColor = null; 078 079 protected GeneralPath fromPath = null; 080 protected GeneralPath toPath = null; 081 082 /** Creates a new instance of Animate */ 083 public Animate() 084 { 085 } 086 087 public String getTagName() 088 { 089 return TAG_NAME; 090 } 091 092 public int getDataType() 093 { 094 return dataType; 095 } 096 097 public void loaderStartElement(SVGLoaderHelper helper, Attributes attrs, SVGElement parent) throws SAXException 098 { 099 //Load style string 100 super.loaderStartElement(helper, attrs, parent); 101 102 String strn = attrs.getValue("from"); 103 if (strn != null) 104 { 105 if (XMLParseUtil.isDouble(strn)) 106 { 107 fromValue = XMLParseUtil.parseDouble(strn); 108 } 109// else if (attrs.getValue("attributeName").equals("d")) 110// { 111// fromPath = this.buildPath(strn, GeneralPath.WIND_EVEN_ODD); 112// dataType = DT_PATH; 113// } 114 else 115 { 116 fromColor = ColorTable.parseColor(strn); 117 if (fromColor == null) 118 { 119 //Try path 120 fromPath = this.buildPath(strn, GeneralPath.WIND_EVEN_ODD); 121 dataType = DT_PATH; 122 } 123 else dataType = DT_COLOR; 124 } 125 } 126 127 strn = attrs.getValue("to"); 128 if (strn != null) 129 { 130 if (XMLParseUtil.isDouble(strn)) 131 { 132 toValue = XMLParseUtil.parseDouble(strn); 133 } 134 else 135 { 136 toColor = ColorTable.parseColor(strn); 137 if (toColor == null) 138 { 139 //Try path 140 toPath = this.buildPath(strn, GeneralPath.WIND_EVEN_ODD); 141 dataType = DT_PATH; 142 } 143 else dataType = DT_COLOR; 144 } 145 } 146 147 strn = attrs.getValue("by"); 148 try 149 { 150 if (strn != null) byValue = XMLParseUtil.parseDouble(strn); 151 } catch (Exception e) {} 152 153 strn = attrs.getValue("values"); 154 try 155 { 156 if (strn != null) valuesValue = XMLParseUtil.parseDoubleList(strn); 157 } catch (Exception e) {} 158 } 159 160 /** 161 * Evaluates this animation element for the passed interpolation time. Interp 162 * must be on [0..1]. 163 */ 164 public double eval(double interp) 165 { 166 boolean fromExists = !Double.isNaN(fromValue); 167 boolean toExists = !Double.isNaN(toValue); 168 boolean byExists = !Double.isNaN(byValue); 169 boolean valuesExists = valuesValue != null; 170 171 if (valuesExists) 172 { 173 double sp = interp * valuesValue.length; 174 int ip = (int)sp; 175 double fp = sp - ip; 176 177 int i0 = ip; 178 int i1 = ip + 1; 179 180 if (i0 < 0) return valuesValue[0]; 181 if (i1 >= valuesValue.length) return valuesValue[valuesValue.length - 1]; 182 return valuesValue[i0] * (1 - fp) + valuesValue[i1] * fp; 183 } 184 else if (fromExists && toExists) 185 { 186 return toValue * interp + fromValue * (1.0 - interp); 187 } 188 else if (fromExists && byExists) 189 { 190 return fromValue + byValue * interp; 191 } 192 else if (toExists && byExists) 193 { 194 return toValue - byValue * (1.0 - interp); 195 } 196 else if (byExists) 197 { 198 return byValue * interp; 199 } 200 201 //Should not reach this line 202 throw new RuntimeException("Animate tag could not be evalutated - insufficient arguements"); 203 } 204 205 public Color evalColor(double interp) 206 { 207 if (fromColor == null && toColor != null) 208 { 209 float[] toCol = new float[3]; 210 toColor.getColorComponents(toCol); 211 return new Color(toCol[0] * (float)interp, 212 toCol[1] * (float)interp, 213 toCol[2] * (float)interp); 214 } 215 else if (fromColor != null && toColor != null) 216 { 217 float nInterp = 1 - (float)interp; 218 219 float[] fromCol = new float[3]; 220 float[] toCol = new float[3]; 221 fromColor.getColorComponents(fromCol); 222 toColor.getColorComponents(toCol); 223 return new Color(fromCol[0] * nInterp + toCol[0] * (float)interp, 224 fromCol[1] * nInterp + toCol[1] * (float)interp, 225 fromCol[2] * nInterp + toCol[2] * (float)interp); 226 } 227 228 throw new RuntimeException("Animate tag could not be evalutated - insufficient arguements"); 229 } 230 231 public GeneralPath evalPath(double interp) 232 { 233 if (fromPath == null && toPath != null) 234 { 235 PathIterator itTo = toPath.getPathIterator(new AffineTransform()); 236 237 GeneralPath midPath = new GeneralPath(); 238 float[] coordsTo = new float[6]; 239 240 for (; !itTo.isDone(); itTo.next()) 241 { 242 int segTo = itTo.currentSegment(coordsTo); 243 244 switch (segTo) 245 { 246 case PathIterator.SEG_CLOSE: 247 midPath.closePath(); 248 break; 249 case PathIterator.SEG_CUBICTO: 250 midPath.curveTo( 251 (float)(coordsTo[0] * interp), 252 (float)(coordsTo[1] * interp), 253 (float)(coordsTo[2] * interp), 254 (float)(coordsTo[3] * interp), 255 (float)(coordsTo[4] * interp), 256 (float)(coordsTo[5] * interp) 257 ); 258 break; 259 case PathIterator.SEG_LINETO: 260 midPath.lineTo( 261 (float)(coordsTo[0] * interp), 262 (float)(coordsTo[1] * interp) 263 ); 264 break; 265 case PathIterator.SEG_MOVETO: 266 midPath.moveTo( 267 (float)(coordsTo[0] * interp), 268 (float)(coordsTo[1] * interp) 269 ); 270 break; 271 case PathIterator.SEG_QUADTO: 272 midPath.quadTo( 273 (float)(coordsTo[0] * interp), 274 (float)(coordsTo[1] * interp), 275 (float)(coordsTo[2] * interp), 276 (float)(coordsTo[3] * interp) 277 ); 278 break; 279 } 280 } 281 282 return midPath; 283 } 284 else if (toPath != null) 285 { 286 PathIterator itFrom = fromPath.getPathIterator(new AffineTransform()); 287 PathIterator itTo = toPath.getPathIterator(new AffineTransform()); 288 289 GeneralPath midPath = new GeneralPath(); 290 float[] coordsFrom = new float[6]; 291 float[] coordsTo = new float[6]; 292 293 for (; !itFrom.isDone(); itFrom.next()) 294 { 295 int segFrom = itFrom.currentSegment(coordsFrom); 296 int segTo = itTo.currentSegment(coordsTo); 297 298 if (segFrom != segTo) 299 { 300 throw new RuntimeException("Path shape mismatch"); 301 } 302 303 switch (segFrom) 304 { 305 case PathIterator.SEG_CLOSE: 306 midPath.closePath(); 307 break; 308 case PathIterator.SEG_CUBICTO: 309 midPath.curveTo( 310 (float)(coordsFrom[0] * (1 - interp) + coordsTo[0] * interp), 311 (float)(coordsFrom[1] * (1 - interp) + coordsTo[1] * interp), 312 (float)(coordsFrom[2] * (1 - interp) + coordsTo[2] * interp), 313 (float)(coordsFrom[3] * (1 - interp) + coordsTo[3] * interp), 314 (float)(coordsFrom[4] * (1 - interp) + coordsTo[4] * interp), 315 (float)(coordsFrom[5] * (1 - interp) + coordsTo[5] * interp) 316 ); 317 break; 318 case PathIterator.SEG_LINETO: 319 midPath.lineTo( 320 (float)(coordsFrom[0] * (1 - interp) + coordsTo[0] * interp), 321 (float)(coordsFrom[1] * (1 - interp) + coordsTo[1] * interp) 322 ); 323 break; 324 case PathIterator.SEG_MOVETO: 325 midPath.moveTo( 326 (float)(coordsFrom[0] * (1 - interp) + coordsTo[0] * interp), 327 (float)(coordsFrom[1] * (1 - interp) + coordsTo[1] * interp) 328 ); 329 break; 330 case PathIterator.SEG_QUADTO: 331 midPath.quadTo( 332 (float)(coordsFrom[0] * (1 - interp) + coordsTo[0] * interp), 333 (float)(coordsFrom[1] * (1 - interp) + coordsTo[1] * interp), 334 (float)(coordsFrom[2] * (1 - interp) + coordsTo[2] * interp), 335 (float)(coordsFrom[3] * (1 - interp) + coordsTo[3] * interp) 336 ); 337 break; 338 } 339 } 340 341 return midPath; 342 } 343 344 throw new RuntimeException("Animate tag could not be evalutated - insufficient arguements"); 345 } 346 347 /** 348 * If this element is being accumulated, detemine the delta to accumulate by 349 */ 350 public double repeatSkipSize(int reps) 351 { 352 boolean fromExists = !Double.isNaN(fromValue); 353 boolean toExists = !Double.isNaN(toValue); 354 boolean byExists = !Double.isNaN(byValue); 355 356 if (fromExists && toExists) 357 { 358 return (toValue - fromValue) * reps; 359 } 360 else if (fromExists && byExists) 361 { 362 return (fromValue + byValue) * reps; 363 } 364 else if (toExists && byExists) 365 { 366 return toValue * reps; 367 } 368 else if (byExists) 369 { 370 return byValue * reps; 371 } 372 373 //Should not reach this line 374 return 0; 375 } 376 377 protected void rebuild(AnimTimeParser animTimeParser) throws SVGException 378 { 379 super.rebuild(animTimeParser); 380 381 StyleAttribute sty = new StyleAttribute(); 382 383 if (getPres(sty.setName("from"))) 384 { 385 String strn = sty.getStringValue(); 386 if (XMLParseUtil.isDouble(strn)) 387 { 388 fromValue = XMLParseUtil.parseDouble(strn); 389 } 390 else 391 { 392 fromColor = ColorTable.parseColor(strn); 393 if (fromColor == null) 394 { 395 //Try path 396 fromPath = this.buildPath(strn, GeneralPath.WIND_EVEN_ODD); 397 dataType = DT_PATH; 398 } 399 else dataType = DT_COLOR; 400 } 401 } 402 403 if (getPres(sty.setName("to"))) 404 { 405 String strn = sty.getStringValue(); 406 if (XMLParseUtil.isDouble(strn)) 407 { 408 toValue = XMLParseUtil.parseDouble(strn); 409 } 410 else 411 { 412 toColor = ColorTable.parseColor(strn); 413 if (toColor == null) 414 { 415 //Try path 416 toPath = this.buildPath(strn, GeneralPath.WIND_EVEN_ODD); 417 dataType = DT_PATH; 418 } 419 else dataType = DT_COLOR; 420 } 421 } 422 423 if (getPres(sty.setName("by"))) 424 { 425 String strn = sty.getStringValue(); 426 if (strn != null) byValue = XMLParseUtil.parseDouble(strn); 427 } 428 429 if (getPres(sty.setName("values"))) 430 { 431 String strn = sty.getStringValue(); 432 if (strn != null) valuesValue = XMLParseUtil.parseDoubleList(strn); 433 } 434 } 435 436}