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

Last change on this file since 15912 was 15912, checked in by simon04, 4 years ago

see #18749 - Use efficient/unmodifiable maps for svgSalamander

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