source: josm/trunk/src/com/kitfox/svg/SVGElement.java@ 14334

Last change on this file since 14334 was 14328, checked in by Don-vip, 6 years ago

see #14319, see #16838 - update to svgSalamander 1.1.2

  • Property svn:eol-style set to native
File size: 25.4 KB
Line 
1/*
2 * SVG Salamander
3 * Copyright (c) 2004, Mark McKay
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or
7 * without modification, are permitted provided that the following
8 * conditions are met:
9 *
10 * - Redistributions of source code must retain the above
11 * copyright notice, this list of conditions and the following
12 * disclaimer.
13 * - Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials
16 * provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
23 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
29 * OF THE POSSIBILITY OF SUCH DAMAGE.
30 *
31 * Mark McKay can be contacted at mark@kitfox.com. Salamander and other
32 * projects can be found at http://www.kitfox.com
33 *
34 * Created on January 26, 2004, 1:59 AM
35 */
36package com.kitfox.svg;
37
38import java.awt.geom.AffineTransform;
39import java.awt.geom.GeneralPath;
40import java.io.Serializable;
41import java.net.URI;
42import java.util.ArrayList;
43import java.util.Collections;
44import java.util.HashMap;
45import java.util.HashSet;
46import java.util.Iterator;
47import java.util.LinkedList;
48import java.util.List;
49import java.util.Set;
50import java.util.regex.Matcher;
51import java.util.regex.Pattern;
52
53import org.xml.sax.Attributes;
54import org.xml.sax.SAXException;
55
56import com.kitfox.svg.pathcmd.Arc;
57import com.kitfox.svg.pathcmd.BuildHistory;
58import com.kitfox.svg.pathcmd.Cubic;
59import com.kitfox.svg.pathcmd.CubicSmooth;
60import com.kitfox.svg.pathcmd.Horizontal;
61import com.kitfox.svg.pathcmd.LineTo;
62import com.kitfox.svg.pathcmd.MoveTo;
63import com.kitfox.svg.pathcmd.PathCommand;
64import com.kitfox.svg.pathcmd.Quadratic;
65import com.kitfox.svg.pathcmd.QuadraticSmooth;
66import com.kitfox.svg.pathcmd.Terminal;
67import com.kitfox.svg.pathcmd.Vertical;
68import com.kitfox.svg.xml.StyleAttribute;
69import com.kitfox.svg.xml.StyleSheet;
70import com.kitfox.svg.xml.XMLParseUtil;
71
72/**
73 * @author Mark McKay
74 * @author <a href="mailto:mark@kitfox.com">Mark McKay</a>
75 */
76abstract public class SVGElement implements Serializable
77{
78
79 public static final long serialVersionUID = 0;
80 public static final String SVG_NS = "http://www.w3.org/2000/svg";
81 protected SVGElement parent = null;
82 protected final ArrayList<SVGElement> children = new ArrayList<>();
83 protected String id = null;
84 /**
85 * CSS class. Used for applying style sheet information.
86 */
87 protected String cssClass = null;
88 /**
89 * Styles defined for this elemnt via the <b>style</b> attribute.
90 */
91 protected final HashMap<String, StyleAttribute> inlineStyles = new HashMap<>();
92 /**
93 * Presentation attributes set for this element. Ie, any attribute other
94 * than the <b>style</b> attribute.
95 */
96 protected final HashMap<String, StyleAttribute> presAttribs = new HashMap<>();
97 /**
98 * A list of presentation attributes to not include in the presentation
99 * attribute set.
100 */
101 protected static final Set<String> ignorePresAttrib;
102
103 static
104 {
105 HashSet<String> set = new HashSet<>();
106// set.add("id");
107// set.add("class");
108// set.add("style");
109// set.add("xml:base");
110
111 ignorePresAttrib = Collections.unmodifiableSet(set);
112 }
113 /**
114 * This element may override the URI we resolve against with an xml:base
115 * attribute. If so, a copy is placed here. Otherwise, we defer to our
116 * parent for the reolution base
117 */
118 protected URI xmlBase = null;
119 /**
120 * The diagram this element belongs to
121 */
122 protected SVGDiagram diagram;
123 boolean dirty = true;
124
125 /**
126 * Creates a new instance of SVGElement
127 */
128 public SVGElement()
129 {
130 this(null, null, null);
131 }
132
133 public SVGElement(String id, SVGElement parent)
134 {
135 this(id, null, parent);
136 }
137
138 public SVGElement(String id, String cssClass, SVGElement parent)
139 {
140 this.id = id;
141 this.cssClass = cssClass;
142 this.parent = parent;
143 }
144
145 abstract public String getTagName();
146
147 public SVGElement getParent()
148 {
149 return parent;
150 }
151
152 void setParent(SVGElement parent)
153 {
154 this.parent = parent;
155 }
156
157 /**
158 * @param retVec
159 * @return an ordered list of nodes from the root of the tree to this node
160 */
161 public List<SVGElement> getPath(List<SVGElement> retVec)
162 {
163 if (retVec == null)
164 {
165 retVec = new ArrayList<>();
166 }
167
168 if (parent != null)
169 {
170 parent.getPath(retVec);
171 }
172 retVec.add(this);
173
174 return retVec;
175 }
176
177 /**
178 * @param retVec - A list to add all children to. If null, a new list is
179 * created and children of this group are added.
180 *
181 * @return The list containing the children of this group
182 */
183 public List<SVGElement> getChildren(List<SVGElement> retVec)
184 {
185 if (retVec == null)
186 {
187 retVec = new ArrayList<>();
188 }
189
190 retVec.addAll(children);
191
192 return retVec;
193 }
194
195 /**
196 * @param id - Id of svg element to return
197 * @return the child of the given id, or null if no such child exists.
198 */
199 public SVGElement getChild(String id)
200 {
201 for (SVGElement ele : children) {
202 String eleId = ele.getId();
203 if (eleId != null && eleId.equals(id))
204 {
205 return ele;
206 }
207 }
208
209 return null;
210 }
211
212 /**
213 * Searches children for given element. If found, returns index of child.
214 * Otherwise returns -1.
215 * @param child
216 * @return index of child
217 */
218 public int indexOfChild(SVGElement child)
219 {
220 return children.indexOf(child);
221 }
222
223 /**
224 * Swaps 2 elements in children.
225 *
226 * @param i index of first child
227 * @param j index of second child
228 * @throws com.kitfox.svg.SVGException
229 */
230 public void swapChildren(int i, int j) throws SVGException
231 {
232 if ((children == null) || (i < 0) || (i >= children.size()) || (j < 0) || (j >= children.size()))
233 {
234 return;
235 }
236
237 SVGElement temp = children.get(i);
238 children.set(i, children.get(j));
239 children.set(j, temp);
240 build();
241 }
242
243 /**
244 * Called during SAX load process to notify that this tag has begun the
245 * process of being loaded
246 *
247 * @param attrs - Attributes of this tag
248 * @param helper - An object passed to all SVG elements involved in this
249 * build process to aid in sharing information.
250 * @param parent
251 * @throws org.xml.sax.SAXException
252 */
253 public void loaderStartElement(SVGLoaderHelper helper, Attributes attrs, SVGElement parent) throws SAXException
254 {
255 //Set identification info
256 this.parent = parent;
257 this.diagram = helper.diagram;
258
259 this.id = attrs.getValue("id");
260 if (this.id != null && !this.id.equals(""))
261 {
262 diagram.setElement(this.id, this);
263 }
264
265 String className = attrs.getValue("class");
266 this.cssClass = (className == null || className.equals("")) ? null : className;
267 //docRoot = helper.docRoot;
268 //universe = helper.universe;
269
270 //Parse style string, if any
271 String style = attrs.getValue("style");
272 if (style != null)
273 {
274 HashMap<?, ?> map = XMLParseUtil.parseStyle(style, inlineStyles);
275 }
276
277 String base = attrs.getValue("xml:base");
278 if (base != null && !base.equals(""))
279 {
280 try
281 {
282 xmlBase = new URI(base);
283 } catch (Exception e)
284 {
285 throw new SAXException(e);
286 }
287 }
288
289 //Place all other attributes into the presentation attribute list
290 int numAttrs = attrs.getLength();
291 for (int i = 0; i < numAttrs; i++)
292 {
293 String name = attrs.getQName(i);
294 if (ignorePresAttrib.contains(name))
295 {
296 continue;
297 }
298 String value = attrs.getValue(i);
299
300 presAttribs.put(name, new StyleAttribute(name, value));
301 }
302 }
303
304 /**
305 * @return a set of Strings that corespond to CSS attributes on this element
306 */
307 public Set<String> getInlineAttributes()
308 {
309 return inlineStyles.keySet();
310 }
311
312 /**
313 * @return a set of Strings that corespond to XML attributes on this element
314 */
315 public Set<String> getPresentationAttributes()
316 {
317 return presAttribs.keySet();
318 }
319
320 /**
321 * Called after the start element but before the end element to indicate
322 * each child tag that has been processed
323 * @param helper
324 * @param child
325 * @throws com.kitfox.svg.SVGElementException
326 */
327 public void loaderAddChild(SVGLoaderHelper helper, SVGElement child) throws SVGElementException
328 {
329 children.add(child);
330 child.parent = this;
331 child.setDiagram(diagram);
332 }
333
334 protected void setDiagram(SVGDiagram diagram)
335 {
336 this.diagram = diagram;
337 diagram.setElement(id, this);
338 for (SVGElement ele : children) {
339 ele.setDiagram(diagram);
340 }
341 }
342
343 public void removeChild(SVGElement child) throws SVGElementException
344 {
345 if (!children.contains(child))
346 {
347 throw new SVGElementException(this, "Element does not contain child " + child);
348 }
349
350 children.remove(child);
351 }
352
353 /**
354 * Called during load process to add text scanned within a tag
355 * @param helper
356 * @param text
357 */
358 public void loaderAddText(SVGLoaderHelper helper, String text)
359 {
360 }
361
362 /**
363 * Called to indicate that this tag and the tags it contains have been
364 * completely processed, and that it should finish any load processes.
365 * @param helper
366 * @throws com.kitfox.svg.SVGParseException
367 */
368 public void loaderEndElement(SVGLoaderHelper helper) throws SVGParseException
369 {
370// try
371// {
372// build();
373// }
374// catch (SVGException se)
375// {
376// throw new SVGParseException(se);
377// }
378 }
379
380 /**
381 * Called by internal processes to rebuild the geometry of this node from
382 * it's presentation attributes, style attributes and animated tracks.
383 * @throws com.kitfox.svg.SVGException
384 */
385 protected void build() throws SVGException
386 {
387 StyleAttribute sty = new StyleAttribute();
388
389 if (getPres(sty.setName("id")))
390 {
391 String newId = sty.getStringValue();
392 if (!newId.equals(id))
393 {
394 diagram.removeElement(id);
395 id = newId;
396 diagram.setElement(this.id, this);
397 }
398 }
399 if (getPres(sty.setName("class")))
400 {
401 cssClass = sty.getStringValue();
402 }
403 if (getPres(sty.setName("xml:base")))
404 {
405 xmlBase = sty.getURIValue();
406 }
407
408 //Build children
409 for (int i = 0; i < children.size(); ++i)
410 {
411 SVGElement ele = children.get(i);
412 ele.build();
413 }
414 }
415
416 public URI getXMLBase()
417 {
418 return xmlBase != null ? xmlBase
419 : (parent != null ? parent.getXMLBase() : diagram.getXMLBase());
420 }
421
422 /**
423 * @return the id assigned to this node. Null if no id explicitly set.
424 */
425 public String getId()
426 {
427 return id;
428 }
429 LinkedList<SVGElement> contexts = new LinkedList<>();
430
431 /**
432 * Hack to allow nodes to temporarily change their parents. The Use tag will
433 * need this so it can alter the attributes that a particular node uses.
434 * @param context
435 */
436 protected void pushParentContext(SVGElement context)
437 {
438 contexts.addLast(context);
439 }
440
441 protected SVGElement popParentContext()
442 {
443 return contexts.removeLast();
444 }
445
446 protected SVGElement getParentContext()
447 {
448 return contexts.isEmpty() ? null : (SVGElement) contexts.getLast();
449 }
450
451 public SVGRoot getRoot()
452 {
453 return parent == null ? null : parent.getRoot();
454 }
455
456 /*
457 * Returns the named style attribute. Checks for inline styles first, then
458 * internal and extranal style sheets, and finally checks for presentation
459 * attributes.
460 * @param styleName - Name of attribute to return
461 * @param recursive - If true and this object does not contain the
462 * named style attribute, checks attributes of parents abck to root until
463 * one found.
464 */
465 public boolean getStyle(StyleAttribute attrib) throws SVGException
466 {
467 return getStyle(attrib, true);
468 }
469
470 /**
471 * Copies the current style into the passed style attribute. Checks for
472 * inline styles first, then internal and extranal style sheets, and finally
473 * checks for presentation attributes. Recursively checks parents.
474 *
475 * @param attrib - Attribute to write style data to. Must have it's name set
476 * to the name of the style being queried.
477 * @param recursive - If true and this object does not contain the named
478 * style attribute, checks attributes of parents back to root until one
479 * found.
480 * @return
481 */
482 public boolean getStyle(StyleAttribute attrib, boolean recursive) throws SVGException
483 {
484 String styName = attrib.getName();
485
486 //Check for local inline styles
487 StyleAttribute styAttr = inlineStyles.get(styName);
488
489 attrib.setStringValue(styAttr == null ? "" : styAttr.getStringValue());
490
491 //Return if we've found a non animated style
492 if (styAttr != null)
493 {
494 return true;
495 }
496
497
498 //Check for presentation attribute
499 StyleAttribute presAttr = presAttribs.get(styName);
500
501 attrib.setStringValue(presAttr == null ? "" : presAttr.getStringValue());
502
503 //Return if we've found a presentation attribute instead
504 if (presAttr != null)
505 {
506 return true;
507 }
508
509 //Check for style sheet
510 SVGRoot root = getRoot();
511 if (root != null)
512 {
513 StyleSheet ss = root.getStyleSheet();
514 if (ss != null)
515 {
516 return ss.getStyle(attrib, getTagName(), cssClass);
517 }
518 }
519
520 //If we're recursive, check parents
521 if (recursive)
522 {
523 SVGElement parentContext = getParentContext();
524 if (parentContext != null)
525 {
526 return parentContext.getStyle(attrib, true);
527 }
528 if (parent != null)
529 {
530 return parent.getStyle(attrib, true);
531 }
532 }
533
534 //Unsuccessful reading style attribute
535 return false;
536 }
537
538 /**
539 * @param styName
540 * @return the raw style value of this attribute. Does not take the
541 * presentation value or animation into consideration. Used by animations to
542 * determine the base to animate from.
543 */
544 public StyleAttribute getStyleAbsolute(String styName)
545 {
546 //Check for local inline styles
547 return inlineStyles.get(styName);
548 }
549
550 /**
551 * Copies the presentation attribute into the passed one.
552 *
553 * @param attrib
554 * @return - True if attribute was read successfully
555 * @throws com.kitfox.svg.SVGException
556 */
557 public boolean getPres(StyleAttribute attrib) throws SVGException
558 {
559 String presName = attrib.getName();
560
561 //Make sure we have a coresponding presentation attribute
562 StyleAttribute presAttr = presAttribs.get(presName);
563
564 //Copy presentation value directly
565 attrib.setStringValue(presAttr == null ? "" : presAttr.getStringValue());
566
567 //Return if we found presentation attribute
568 if (presAttr != null)
569 {
570 return true;
571 }
572
573 return false;
574 }
575
576 /**
577 * @param styName
578 * @return the raw presentation value of this attribute. Ignores any
579 * modifications applied by style attributes or animation. Used by
580 * animations to determine the starting point to animate from
581 */
582 public StyleAttribute getPresAbsolute(String styName)
583 {
584 //Check for local inline styles
585 return presAttribs.get(styName);
586 }
587
588 static protected AffineTransform parseTransform(String val) throws SVGException
589 {
590 final Matcher matchExpression = Pattern.compile("\\w+\\([^)]*\\)").matcher("");
591
592 AffineTransform retXform = new AffineTransform();
593
594 matchExpression.reset(val);
595 while (matchExpression.find())
596 {
597 retXform.concatenate(parseSingleTransform(matchExpression.group()));
598 }
599
600 return retXform;
601 }
602
603 static public AffineTransform parseSingleTransform(String val) throws SVGException
604 {
605 final Matcher matchWord = Pattern.compile("([a-zA-Z]+|-?\\d+(\\.\\d+)?(e-?\\d+)?|-?\\.\\d+(e-?\\d+)?)").matcher("");
606
607 AffineTransform retXform = new AffineTransform();
608
609 matchWord.reset(val);
610 if (!matchWord.find())
611 {
612 //Return identity transformation if no data present (eg, empty string)
613 return retXform;
614 }
615
616 String function = matchWord.group().toLowerCase();
617
618 LinkedList<String> termList = new LinkedList<>();
619 while (matchWord.find())
620 {
621 termList.add(matchWord.group());
622 }
623
624
625 double[] terms = new double[termList.size()];
626 Iterator<String> it = termList.iterator();
627 int count = 0;
628 while (it.hasNext())
629 {
630 terms[count++] = XMLParseUtil.parseDouble(it.next());
631 }
632
633 //Calculate transformation
634 if (function.equals("matrix"))
635 {
636 retXform.setTransform(terms[0], terms[1], terms[2], terms[3], terms[4], terms[5]);
637 } else if (function.equals("translate"))
638 {
639 if (terms.length == 1)
640 {
641 retXform.setToTranslation(terms[0], 0);
642 } else
643 {
644 retXform.setToTranslation(terms[0], terms[1]);
645 }
646 } else if (function.equals("scale"))
647 {
648 if (terms.length > 1)
649 {
650 retXform.setToScale(terms[0], terms[1]);
651 } else
652 {
653 retXform.setToScale(terms[0], terms[0]);
654 }
655 } else if (function.equals("rotate"))
656 {
657 if (terms.length > 2)
658 {
659 retXform.setToRotation(Math.toRadians(terms[0]), terms[1], terms[2]);
660 } else
661 {
662 retXform.setToRotation(Math.toRadians(terms[0]));
663 }
664 } else if (function.equals("skewx"))
665 {
666 retXform.setToShear(Math.toRadians(terms[0]), 0.0);
667 } else if (function.equals("skewy"))
668 {
669 retXform.setToShear(0.0, Math.toRadians(terms[0]));
670 } else
671 {
672 throw new SVGException("Unknown transform type");
673 }
674
675 return retXform;
676 }
677
678 static protected float nextFloat(LinkedList<String> l)
679 {
680 String s = l.removeFirst();
681 return Float.parseFloat(s);
682 }
683
684 static protected PathCommand[] parsePathList(String list)
685 {
686 final Matcher matchPathCmd = Pattern.compile("([MmLlHhVvAaQqTtCcSsZz])|([-+]?((\\d*\\.\\d+)|(\\d+))([eE][-+]?\\d+)?)").matcher(list);
687
688 //Tokenize
689 LinkedList<String> tokens = new LinkedList<>();
690 while (matchPathCmd.find())
691 {
692 tokens.addLast(matchPathCmd.group());
693 }
694
695
696 boolean defaultRelative = false;
697 LinkedList<PathCommand> cmdList = new LinkedList<>();
698 char curCmd = 'Z';
699 while (tokens.size() != 0)
700 {
701 String curToken = tokens.removeFirst();
702 char initChar = curToken.charAt(0);
703 if ((initChar >= 'A' && initChar <= 'Z') || (initChar >= 'a' && initChar <= 'z'))
704 {
705 curCmd = initChar;
706 } else
707 {
708 tokens.addFirst(curToken);
709 }
710
711 PathCommand cmd = null;
712
713 switch (curCmd)
714 {
715 case 'M':
716 cmd = new MoveTo(false, nextFloat(tokens), nextFloat(tokens));
717 curCmd = 'L';
718 break;
719 case 'm':
720 cmd = new MoveTo(true, nextFloat(tokens), nextFloat(tokens));
721 curCmd = 'l';
722 break;
723 case 'L':
724 cmd = new LineTo(false, nextFloat(tokens), nextFloat(tokens));
725 break;
726 case 'l':
727 cmd = new LineTo(true, nextFloat(tokens), nextFloat(tokens));
728 break;
729 case 'H':
730 cmd = new Horizontal(false, nextFloat(tokens));
731 break;
732 case 'h':
733 cmd = new Horizontal(true, nextFloat(tokens));
734 break;
735 case 'V':
736 cmd = new Vertical(false, nextFloat(tokens));
737 break;
738 case 'v':
739 cmd = new Vertical(true, nextFloat(tokens));
740 break;
741 case 'A':
742 cmd = new Arc(false, nextFloat(tokens), nextFloat(tokens),
743 nextFloat(tokens),
744 nextFloat(tokens) == 1f, nextFloat(tokens) == 1f,
745 nextFloat(tokens), nextFloat(tokens));
746 break;
747 case 'a':
748 cmd = new Arc(true, nextFloat(tokens), nextFloat(tokens),
749 nextFloat(tokens),
750 nextFloat(tokens) == 1f, nextFloat(tokens) == 1f,
751 nextFloat(tokens), nextFloat(tokens));
752 break;
753 case 'Q':
754 cmd = new Quadratic(false, nextFloat(tokens), nextFloat(tokens),
755 nextFloat(tokens), nextFloat(tokens));
756 break;
757 case 'q':
758 cmd = new Quadratic(true, nextFloat(tokens), nextFloat(tokens),
759 nextFloat(tokens), nextFloat(tokens));
760 break;
761 case 'T':
762 cmd = new QuadraticSmooth(false, nextFloat(tokens), nextFloat(tokens));
763 break;
764 case 't':
765 cmd = new QuadraticSmooth(true, nextFloat(tokens), nextFloat(tokens));
766 break;
767 case 'C':
768 cmd = new Cubic(false, nextFloat(tokens), nextFloat(tokens),
769 nextFloat(tokens), nextFloat(tokens),
770 nextFloat(tokens), nextFloat(tokens));
771 break;
772 case 'c':
773 cmd = new Cubic(true, nextFloat(tokens), nextFloat(tokens),
774 nextFloat(tokens), nextFloat(tokens),
775 nextFloat(tokens), nextFloat(tokens));
776 break;
777 case 'S':
778 cmd = new CubicSmooth(false, nextFloat(tokens), nextFloat(tokens),
779 nextFloat(tokens), nextFloat(tokens));
780 break;
781 case 's':
782 cmd = new CubicSmooth(true, nextFloat(tokens), nextFloat(tokens),
783 nextFloat(tokens), nextFloat(tokens));
784 break;
785 case 'Z':
786 case 'z':
787 cmd = new Terminal();
788 break;
789 default:
790 throw new RuntimeException("Invalid path element");
791 }
792
793 cmdList.add(cmd);
794 defaultRelative = cmd.isRelative;
795 }
796
797 PathCommand[] retArr = new PathCommand[cmdList.size()];
798 cmdList.toArray(retArr);
799 return retArr;
800 }
801
802 static protected GeneralPath buildPath(String text, int windingRule)
803 {
804 PathCommand[] commands = parsePathList(text);
805
806 int numKnots = 2;
807 for (int i = 0; i < commands.length; i++)
808 {
809 numKnots += commands[i].getNumKnotsAdded();
810 }
811
812
813 GeneralPath path = new GeneralPath(windingRule, numKnots);
814
815 BuildHistory hist = new BuildHistory();
816
817 for (int i = 0; i < commands.length; i++)
818 {
819 PathCommand cmd = commands[i];
820 cmd.appendPath(path, hist);
821 }
822
823 return path;
824 }
825
826 /**
827 * Updates all attributes in this diagram associated with a time event. Ie,
828 * all attributes with track information.
829 *
830 * @param curTime
831 * @return - true if this node has changed state as a result of the time
832 * update
833 * @throws com.kitfox.svg.SVGException
834 */
835 abstract public boolean updateTime(double curTime) throws SVGException;
836
837 public int getNumChildren()
838 {
839 return children.size();
840 }
841
842 public SVGElement getChild(int i)
843 {
844 return children.get(i);
845 }
846
847 public double lerp(double t0, double t1, double alpha)
848 {
849 return (1 - alpha) * t0 + alpha * t1;
850 }
851}
Note: See TracBrowser for help on using the repository browser.