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