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

Last change on this file since 14643 was 14643, checked in by Don-vip, 5 years ago

optimize parsing by creating static regex patterns (patch by dpolivaev)

See https://github.com/blackears/svgSalamander/pull/38

  • Property svn:eol-style set to native
File size: 25.6 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 private static final Pattern TRANSFORM_PATTERN = Pattern.compile("\\w+\\([^)]*\\)");
589 static protected AffineTransform parseTransform(String val) throws SVGException
590 {
591 final Matcher matchExpression = TRANSFORM_PATTERN.matcher("");
592
593 AffineTransform retXform = new AffineTransform();
594
595 matchExpression.reset(val);
596 while (matchExpression.find())
597 {
598 retXform.concatenate(parseSingleTransform(matchExpression.group()));
599 }
600
601 return retXform;
602 }
603
604 private static final Pattern WORD_PATTERN = Pattern.compile("([a-zA-Z]+|-?\\d+(\\.\\d+)?(e-?\\d+)?|-?\\.\\d+(e-?\\d+)?)");
605 static public AffineTransform parseSingleTransform(String val) throws SVGException
606 {
607 final Matcher matchWord = WORD_PATTERN.matcher("");
608
609 AffineTransform retXform = new AffineTransform();
610
611 matchWord.reset(val);
612 if (!matchWord.find())
613 {
614 //Return identity transformation if no data present (eg, empty string)
615 return retXform;
616 }
617
618 String function = matchWord.group().toLowerCase();
619
620 LinkedList<String> termList = new LinkedList<>();
621 while (matchWord.find())
622 {
623 termList.add(matchWord.group());
624 }
625
626
627 double[] terms = new double[termList.size()];
628 Iterator<String> it = termList.iterator();
629 int count = 0;
630 while (it.hasNext())
631 {
632 terms[count++] = XMLParseUtil.parseDouble(it.next());
633 }
634
635 //Calculate transformation
636 if (function.equals("matrix"))
637 {
638 retXform.setTransform(terms[0], terms[1], terms[2], terms[3], terms[4], terms[5]);
639 } else if (function.equals("translate"))
640 {
641 if (terms.length == 1)
642 {
643 retXform.setToTranslation(terms[0], 0);
644 } else
645 {
646 retXform.setToTranslation(terms[0], terms[1]);
647 }
648 } else if (function.equals("scale"))
649 {
650 if (terms.length > 1)
651 {
652 retXform.setToScale(terms[0], terms[1]);
653 } else
654 {
655 retXform.setToScale(terms[0], terms[0]);
656 }
657 } else if (function.equals("rotate"))
658 {
659 if (terms.length > 2)
660 {
661 retXform.setToRotation(Math.toRadians(terms[0]), terms[1], terms[2]);
662 } else
663 {
664 retXform.setToRotation(Math.toRadians(terms[0]));
665 }
666 } else if (function.equals("skewx"))
667 {
668 retXform.setToShear(Math.toRadians(terms[0]), 0.0);
669 } else if (function.equals("skewy"))
670 {
671 retXform.setToShear(0.0, Math.toRadians(terms[0]));
672 } else
673 {
674 throw new SVGException("Unknown transform type");
675 }
676
677 return retXform;
678 }
679
680 static protected float nextFloat(LinkedList<String> l)
681 {
682 String s = l.removeFirst();
683 return Float.parseFloat(s);
684 }
685
686 private static final Pattern COMMAND_PATTERN = Pattern.compile("([MmLlHhVvAaQqTtCcSsZz])|([-+]?((\\d*\\.\\d+)|(\\d+))([eE][-+]?\\d+)?)");
687 static protected PathCommand[] parsePathList(String list)
688 {
689 final Matcher matchPathCmd = COMMAND_PATTERN.matcher(list);
690
691 //Tokenize
692 LinkedList<String> tokens = new LinkedList<>();
693 while (matchPathCmd.find())
694 {
695 tokens.addLast(matchPathCmd.group());
696 }
697
698
699 boolean defaultRelative = false;
700 LinkedList<PathCommand> cmdList = new LinkedList<>();
701 char curCmd = 'Z';
702 while (tokens.size() != 0)
703 {
704 String curToken = tokens.removeFirst();
705 char initChar = curToken.charAt(0);
706 if ((initChar >= 'A' && initChar <= 'Z') || (initChar >= 'a' && initChar <= 'z'))
707 {
708 curCmd = initChar;
709 } else
710 {
711 tokens.addFirst(curToken);
712 }
713
714 PathCommand cmd = null;
715
716 switch (curCmd)
717 {
718 case 'M':
719 cmd = new MoveTo(false, nextFloat(tokens), nextFloat(tokens));
720 curCmd = 'L';
721 break;
722 case 'm':
723 cmd = new MoveTo(true, nextFloat(tokens), nextFloat(tokens));
724 curCmd = 'l';
725 break;
726 case 'L':
727 cmd = new LineTo(false, nextFloat(tokens), nextFloat(tokens));
728 break;
729 case 'l':
730 cmd = new LineTo(true, nextFloat(tokens), nextFloat(tokens));
731 break;
732 case 'H':
733 cmd = new Horizontal(false, nextFloat(tokens));
734 break;
735 case 'h':
736 cmd = new Horizontal(true, nextFloat(tokens));
737 break;
738 case 'V':
739 cmd = new Vertical(false, nextFloat(tokens));
740 break;
741 case 'v':
742 cmd = new Vertical(true, nextFloat(tokens));
743 break;
744 case 'A':
745 cmd = new Arc(false, nextFloat(tokens), nextFloat(tokens),
746 nextFloat(tokens),
747 nextFloat(tokens) == 1f, nextFloat(tokens) == 1f,
748 nextFloat(tokens), nextFloat(tokens));
749 break;
750 case 'a':
751 cmd = new Arc(true, nextFloat(tokens), nextFloat(tokens),
752 nextFloat(tokens),
753 nextFloat(tokens) == 1f, nextFloat(tokens) == 1f,
754 nextFloat(tokens), nextFloat(tokens));
755 break;
756 case 'Q':
757 cmd = new Quadratic(false, nextFloat(tokens), nextFloat(tokens),
758 nextFloat(tokens), nextFloat(tokens));
759 break;
760 case 'q':
761 cmd = new Quadratic(true, nextFloat(tokens), nextFloat(tokens),
762 nextFloat(tokens), nextFloat(tokens));
763 break;
764 case 'T':
765 cmd = new QuadraticSmooth(false, nextFloat(tokens), nextFloat(tokens));
766 break;
767 case 't':
768 cmd = new QuadraticSmooth(true, nextFloat(tokens), nextFloat(tokens));
769 break;
770 case 'C':
771 cmd = new Cubic(false, nextFloat(tokens), nextFloat(tokens),
772 nextFloat(tokens), nextFloat(tokens),
773 nextFloat(tokens), nextFloat(tokens));
774 break;
775 case 'c':
776 cmd = new Cubic(true, nextFloat(tokens), nextFloat(tokens),
777 nextFloat(tokens), nextFloat(tokens),
778 nextFloat(tokens), nextFloat(tokens));
779 break;
780 case 'S':
781 cmd = new CubicSmooth(false, nextFloat(tokens), nextFloat(tokens),
782 nextFloat(tokens), nextFloat(tokens));
783 break;
784 case 's':
785 cmd = new CubicSmooth(true, nextFloat(tokens), nextFloat(tokens),
786 nextFloat(tokens), nextFloat(tokens));
787 break;
788 case 'Z':
789 case 'z':
790 cmd = new Terminal();
791 break;
792 default:
793 throw new RuntimeException("Invalid path element");
794 }
795
796 cmdList.add(cmd);
797 defaultRelative = cmd.isRelative;
798 }
799
800 PathCommand[] retArr = new PathCommand[cmdList.size()];
801 cmdList.toArray(retArr);
802 return retArr;
803 }
804
805 static protected GeneralPath buildPath(String text, int windingRule)
806 {
807 PathCommand[] commands = parsePathList(text);
808
809 int numKnots = 2;
810 for (int i = 0; i < commands.length; i++)
811 {
812 numKnots += commands[i].getNumKnotsAdded();
813 }
814
815
816 GeneralPath path = new GeneralPath(windingRule, numKnots);
817
818 BuildHistory hist = new BuildHistory();
819
820 for (int i = 0; i < commands.length; i++)
821 {
822 PathCommand cmd = commands[i];
823 cmd.appendPath(path, hist);
824 }
825
826 return path;
827 }
828
829 /**
830 * Updates all attributes in this diagram associated with a time event. Ie,
831 * all attributes with track information.
832 *
833 * @param curTime
834 * @return - true if this node has changed state as a result of the time
835 * update
836 * @throws com.kitfox.svg.SVGException
837 */
838 abstract public boolean updateTime(double curTime) throws SVGException;
839
840 public int getNumChildren()
841 {
842 return children.size();
843 }
844
845 public SVGElement getChild(int i)
846 {
847 return children.get(i);
848 }
849
850 public double lerp(double t0, double t1, double alpha)
851 {
852 return (1 - alpha) * t0 + alpha * t1;
853 }
854}
Note: See TracBrowser for help on using the repository browser.