propane 3.1.0.pre-java → 3.2.0-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. checksums.yaml +4 -4
  2. data/.mvn/wrapper/maven-wrapper.properties +1 -0
  3. data/CHANGELOG.md +1 -5
  4. data/README.md +23 -12
  5. data/Rakefile +23 -12
  6. data/lib/propane/helpers/version_error.rb +6 -0
  7. data/lib/propane/runner.rb +12 -0
  8. data/lib/propane/version.rb +1 -1
  9. data/library/slider/slider.rb +1 -1
  10. data/mvnw +234 -0
  11. data/mvnw.cmd +145 -0
  12. data/pom.xml +28 -27
  13. data/propane.gemspec +2 -2
  14. data/src/main/java/japplemenubar/JAppleMenuBar.java +41 -47
  15. data/src/main/java/monkstone/ColorUtil.java +1 -1
  16. data/src/main/java/monkstone/MathToolModule.java +12 -11
  17. data/src/main/java/monkstone/PropaneLibrary.java +9 -10
  18. data/src/main/java/monkstone/core/LibraryProxy.java +124 -113
  19. data/src/main/java/monkstone/fastmath/Deglut.java +86 -89
  20. data/src/main/java/monkstone/filechooser/Chooser.java +7 -13
  21. data/src/main/java/monkstone/noise/SimplexNoise.java +0 -1
  22. data/src/main/java/monkstone/slider/CustomHorizontalSlider.java +4 -4
  23. data/src/main/java/monkstone/slider/CustomVerticalSlider.java +1 -1
  24. data/src/main/java/monkstone/slider/SimpleHorizontalSlider.java +9 -9
  25. data/src/main/java/monkstone/slider/SimpleSlider.java +0 -9
  26. data/src/main/java/monkstone/slider/SimpleVerticalSlider.java +11 -13
  27. data/src/main/java/monkstone/slider/Slider.java +1 -1
  28. data/src/main/java/monkstone/slider/SliderBar.java +1 -1
  29. data/src/main/java/monkstone/slider/SliderGroup.java +1 -1
  30. data/src/main/java/monkstone/slider/WheelHandler.java +8 -9
  31. data/src/main/java/monkstone/vecmath/AppRender.java +2 -2
  32. data/src/main/java/monkstone/vecmath/ShapeRender.java +2 -2
  33. data/src/main/java/monkstone/vecmath/package-info.java +2 -2
  34. data/src/main/java/monkstone/vecmath/vec2/Vec2.java +2 -2
  35. data/src/main/java/monkstone/vecmath/vec3/Vec3.java +1 -1
  36. data/src/main/java/monkstone/videoevent/VideoInterface.java +11 -5
  37. data/src/main/java/monkstone/videoevent/package-info.java +2 -2
  38. data/src/main/java/processing/awt/PGraphicsJava2D.java +1742 -2243
  39. data/src/main/java/processing/awt/PShapeJava2D.java +268 -270
  40. data/src/main/java/processing/awt/PSurfaceAWT.java +821 -920
  41. data/src/main/java/processing/core/DesktopHandler.java +94 -0
  42. data/src/main/java/processing/core/PApplet.java +14170 -14082
  43. data/src/main/java/processing/core/PConstants.java +447 -473
  44. data/src/main/java/processing/core/PFont.java +867 -873
  45. data/src/main/java/processing/core/PGraphics.java +7193 -7428
  46. data/src/main/java/processing/core/PImage.java +3051 -3117
  47. data/src/main/java/processing/core/PMatrix.java +159 -172
  48. data/src/main/java/processing/core/PMatrix2D.java +403 -444
  49. data/src/main/java/processing/core/PMatrix3D.java +735 -749
  50. data/src/main/java/processing/core/PShape.java +2651 -2793
  51. data/src/main/java/processing/core/PShapeOBJ.java +415 -422
  52. data/src/main/java/processing/core/PShapeSVG.java +1466 -1475
  53. data/src/main/java/processing/core/PStyle.java +37 -40
  54. data/src/main/java/processing/core/PSurface.java +98 -103
  55. data/src/main/java/processing/core/PSurfaceNone.java +208 -236
  56. data/src/main/java/processing/core/PVector.java +961 -990
  57. data/src/main/java/processing/data/DoubleDict.java +709 -753
  58. data/src/main/java/processing/data/DoubleList.java +695 -748
  59. data/src/main/java/processing/data/FloatDict.java +702 -746
  60. data/src/main/java/processing/data/FloatList.java +697 -751
  61. data/src/main/java/processing/data/IntDict.java +673 -718
  62. data/src/main/java/processing/data/IntList.java +633 -699
  63. data/src/main/java/processing/data/JSONArray.java +873 -931
  64. data/src/main/java/processing/data/JSONObject.java +1165 -1262
  65. data/src/main/java/processing/data/JSONTokener.java +341 -351
  66. data/src/main/java/processing/data/LongDict.java +662 -707
  67. data/src/main/java/processing/data/LongList.java +634 -700
  68. data/src/main/java/processing/data/Sort.java +41 -37
  69. data/src/main/java/processing/data/StringDict.java +486 -522
  70. data/src/main/java/processing/data/StringList.java +580 -624
  71. data/src/main/java/processing/data/Table.java +3508 -3686
  72. data/src/main/java/processing/data/TableRow.java +183 -182
  73. data/src/main/java/processing/data/XML.java +883 -957
  74. data/src/main/java/processing/event/Event.java +66 -87
  75. data/src/main/java/processing/event/KeyEvent.java +41 -48
  76. data/src/main/java/processing/event/MouseEvent.java +93 -103
  77. data/src/main/java/processing/event/TouchEvent.java +6 -10
  78. data/src/main/java/processing/javafx/PGraphicsFX2D.java +5 -69
  79. data/src/main/java/processing/javafx/PSurfaceFX.java +2 -7
  80. data/src/main/java/processing/opengl/FontTexture.java +270 -290
  81. data/src/main/java/processing/opengl/FrameBuffer.java +363 -375
  82. data/src/main/java/processing/opengl/LinePath.java +500 -543
  83. data/src/main/java/processing/opengl/LineStroker.java +582 -593
  84. data/src/main/java/processing/opengl/PGL.java +2881 -2904
  85. data/src/main/java/processing/opengl/PGraphics2D.java +315 -408
  86. data/src/main/java/processing/opengl/PGraphics3D.java +72 -107
  87. data/src/main/java/processing/opengl/PGraphicsOpenGL.java +12043 -12230
  88. data/src/main/java/processing/opengl/PJOGL.java +1681 -1745
  89. data/src/main/java/processing/opengl/PShader.java +1257 -1260
  90. data/src/main/java/processing/opengl/PShapeOpenGL.java +4599 -4662
  91. data/src/main/java/processing/opengl/PSurfaceJOGL.java +1030 -1047
  92. data/src/main/java/processing/opengl/Texture.java +1397 -1462
  93. data/src/main/java/processing/opengl/VertexBuffer.java +55 -57
  94. data/src/main/resources/icon/icon-1024.png +0 -0
  95. data/src/main/resources/icon/icon-128.png +0 -0
  96. data/src/main/resources/icon/icon-16.png +0 -0
  97. data/src/main/resources/icon/icon-256.png +0 -0
  98. data/src/main/resources/icon/icon-32.png +0 -0
  99. data/src/main/resources/icon/icon-48.png +0 -0
  100. data/src/main/resources/icon/icon-512.png +0 -0
  101. data/src/main/resources/icon/icon-64.png +0 -0
  102. data/vendors/Rakefile +1 -1
  103. metadata +12 -8
  104. data/src/main/java/processing/core/ThinkDifferent.java +0 -70
@@ -1,5 +1,3 @@
1
- /* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
2
-
3
1
  /*
4
2
  Part of the Processing project - http://processing.org
5
3
 
@@ -20,8 +18,7 @@
20
18
  Public License along with this library; if not, write to the
21
19
  Free Software Foundation, Inc., 59 Temple Place, Suite 330,
22
20
  Boston, MA 02111-1307 USA
23
- */
24
-
21
+ */
25
22
  package processing.core;
26
23
 
27
24
  import processing.data.*;
@@ -35,21 +32,20 @@ import java.util.HashMap;
35
32
  import java.util.regex.Matcher;
36
33
  import java.util.regex.Pattern;
37
34
 
38
-
39
35
  /**
40
- * This class is not part of the Processing API and should not be used
41
- * directly. Instead, use loadShape() and methods like it, which will make
42
- * use of this class. Using this class directly will cause your code to break
43
- * when combined with future versions of Processing.
36
+ * This class is not part of the Processing API and should not be used directly.
37
+ * Instead, use loadShape() and methods like it, which will make use of this
38
+ * class. Using this class directly will cause your code to break when combined
39
+ * with future versions of Processing.
44
40
  * <p>
45
- * SVG stands for Scalable Vector Graphics, a portable graphics format.
46
- * It is a vector format so it allows for "infinite" resolution and relatively
47
- * small file sizes. Most modern media software can view SVG files, including
48
- * Adobe products, Firefox, etc. Illustrator and Inkscape can edit SVG files.
49
- * View the SVG specification <A HREF="http://www.w3.org/TR/SVG">here</A>.
41
+ * SVG stands for Scalable Vector Graphics, a portable graphics format. It is a
42
+ * vector format so it allows for "infinite" resolution and relatively small
43
+ * file sizes. Most modern media software can view SVG files, including Adobe
44
+ * products, Firefox, etc. Illustrator and Inkscape can edit SVG files. View the
45
+ * SVG specification <A HREF="http://www.w3.org/TR/SVG">here</A>.
50
46
  * <p>
51
- * We have no intention of turning this into a full-featured SVG library.
52
- * The goal of this project is a basic shape importer that originally was small
47
+ * We have no intention of turning this into a full-featured SVG library. The
48
+ * goal of this project is a basic shape importer that originally was small
53
49
  * enough to be included with applets, meaning that its download size should be
54
50
  * in the neighborhood of 25-30 Kb. Though we're far less limited nowadays on
55
51
  * size constraints, we remain extremely limited in terms of time, and do not
@@ -59,20 +55,22 @@ import java.util.regex.Pattern;
59
55
  * <A HREF="http://xmlgraphics.apache.org/batik/">Batik</A>
60
56
  * library from the Apache Software Foundation.
61
57
  * <p>
62
- * Batik is used in the SVG Export library in Processing 3, however using it
63
- * for full SVG import is still a considerable amount of work. Wiring it to
64
- * Java2D wouldn't be too bad, but using it with OpenGL, JavaFX, and features
65
- * like begin/endRecord() and begin/endRaw() would be considerable effort.
58
+ * Batik is used in the SVG Export library in Processing 3, however using it for
59
+ * full SVG import is still a considerable amount of work. Wiring it to Java2D
60
+ * wouldn't be too bad, but using it with OpenGL, JavaFX, and features like
61
+ * begin/endRecord() and begin/endRaw() would be considerable effort.
66
62
  * <p>
67
- * Future improvements to this library may focus on this properly supporting
68
- * a specific subset of SVG, for instance the simpler SVG profiles known as
69
- * <A HREF="http://www.w3.org/TR/SVGMobile/">SVG Tiny or Basic</A>,
70
- * although we still would not support the interactivity options.
63
+ * Future improvements to this library may focus on this properly supporting a
64
+ * specific subset of SVG, for instance the simpler SVG profiles known as
65
+ * <A HREF="http://www.w3.org/TR/SVGMobile/">SVG Tiny or Basic</A>, although we
66
+ * still would not support the interactivity options.
71
67
  *
72
- * <p> <hr noshade> <p>
68
+ * <p>
69
+ * <hr noshade>
70
+ * <p>
73
71
  *
74
- * A minimal example program using SVG:
75
- * (assuming a working moo.svg is in your data folder)
72
+ * A minimal example program using SVG: (assuming a working moo.svg is in your
73
+ * data folder)
76
74
  *
77
75
  * <PRE>
78
76
  * PShape moo;
@@ -88,385 +86,392 @@ import java.util.regex.Pattern;
88
86
  * </PRE>
89
87
  */
90
88
  public class PShapeSVG extends PShape {
91
- XML element;
92
-
93
- /// Values between 0 and 1.
94
- protected float opacity;
95
- float strokeOpacity;
96
- float fillOpacity;
97
-
98
- /** Width of containing SVG (used for percentages). */
99
- protected float svgWidth;
100
-
101
- /** Height of containing SVG (used for percentages). */
102
- protected float svgHeight;
103
89
 
104
- /** √((w² + h²)/2) of containing SVG (used for percentages). */
105
- protected float svgSizeXY;
90
+ XML element;
91
+
92
+ /// Values between 0 and 1.
93
+ protected float opacity;
94
+ float strokeOpacity;
95
+ float fillOpacity;
96
+
97
+ /**
98
+ * Width of containing SVG (used for percentages).
99
+ */
100
+ protected float svgWidth;
101
+
102
+ /**
103
+ * Height of containing SVG (used for percentages).
104
+ */
105
+ protected float svgHeight;
106
+
107
+ /**
108
+ * √((w² + h²)/2) of containing SVG (used for percentages).
109
+ */
110
+ protected float svgSizeXY;
111
+
112
+ protected Gradient strokeGradient;
113
+ String strokeName; // id of another object, gradients only?
114
+
115
+ protected Gradient fillGradient;
116
+ String fillName; // id of another object
117
+
118
+ /**
119
+ * Initializes a new SVG object from the given XML object.
120
+ */
121
+ public PShapeSVG(XML svg) {
122
+ this(null, svg, true);
123
+
124
+ if (!svg.getName().equals("svg")) {
125
+ if (svg.getName().toLowerCase().equals("html")) {
126
+ // Common case is that files aren't downloaded properly
127
+ throw new RuntimeException("This appears to be a web page, not an SVG file.");
128
+ } else {
129
+ throw new RuntimeException("The root node is not <svg>, it's <" + svg.getName() + ">");
130
+ }
131
+ }
132
+ }
106
133
 
107
- protected Gradient strokeGradient;
108
- String strokeName; // id of another object, gradients only?
134
+ protected PShapeSVG(PShapeSVG parent, XML properties, boolean parseKids) {
135
+ setParent(parent);
109
136
 
110
- protected Gradient fillGradient;
111
- String fillName; // id of another object
137
+ // Need to get width/height in early.
138
+ if (properties.getName().equals("svg")) {
139
+ String unitWidth = properties.getString("width");
140
+ String unitHeight = properties.getString("height");
112
141
 
142
+ // Can't handle width/height as percentages easily. I'm just going
143
+ // to put in 100 as a dummy value, beacuse this means that it will
144
+ // come out as a reasonable value.
145
+ if (unitWidth != null) {
146
+ width = parseUnitSize(unitWidth, 100);
147
+ }
148
+ if (unitHeight != null) {
149
+ height = parseUnitSize(unitHeight, 100);
150
+ }
113
151
 
114
- /**
115
- * Initializes a new SVG object from the given XML object.
116
- */
117
- public PShapeSVG(XML svg) {
118
- this(null, svg, true);
152
+ String viewBoxStr = properties.getString("viewBox");
153
+ if (viewBoxStr != null) {
154
+ float[] viewBox = PApplet.parseFloat(PApplet.splitTokens(viewBoxStr));
155
+ if (unitWidth == null || unitHeight == null) {
156
+ // Not proper parsing of the viewBox, but will cover us for cases where
157
+ // the width and height of the object is not specified.
158
+ width = viewBox[2];
159
+ height = viewBox[3];
160
+ } else {
161
+ // http://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute
162
+ // TODO: preserveAspectRatio.
163
+ if (matrix == null) {
164
+ matrix = new PMatrix2D();
165
+ }
166
+ matrix.scale(width / viewBox[2], height / viewBox[3]);
167
+ matrix.translate(-viewBox[0], -viewBox[1]);
168
+ }
169
+ }
119
170
 
120
- if (!svg.getName().equals("svg")) {
121
- if (svg.getName().toLowerCase().equals("html")) {
122
- // Common case is that files aren't downloaded properly
123
- throw new RuntimeException("This appears to be a web page, not an SVG file.");
124
- } else {
125
- throw new RuntimeException("The root node is not <svg>, it's <" + svg.getName() + ">");
126
- }
127
- }
128
- }
171
+ // Negative size is illegal.
172
+ if (width < 0 || height < 0) {
173
+ throw new RuntimeException("<svg>: width (" + width
174
+ + ") and height (" + height + ") must not be negative.");
175
+ }
129
176
 
177
+ // It's technically valid to have width or height == 0. Not specified at
178
+ // all is what to test for.
179
+ if ((unitWidth == null || unitHeight == null) && viewBoxStr == null) {
180
+ //throw new RuntimeException("width/height not specified");
181
+ PGraphics.showWarning("The width and/or height is not "
182
+ + "readable in the <svg> tag of this file.");
183
+ // For the spec, the default is 100% and 100%. For purposes
184
+ // here, insert a dummy value because this is prolly just a
185
+ // font or something for which the w/h doesn't matter.
186
+ width = 1;
187
+ height = 1;
188
+ }
130
189
 
131
- protected PShapeSVG(PShapeSVG parent, XML properties, boolean parseKids) {
132
- setParent(parent);
133
-
134
- // Need to get width/height in early.
135
- if (properties.getName().equals("svg")) {
136
- String unitWidth = properties.getString("width");
137
- String unitHeight = properties.getString("height");
138
-
139
- // Can't handle width/height as percentages easily. I'm just going
140
- // to put in 100 as a dummy value, beacuse this means that it will
141
- // come out as a reasonable value.
142
- if (unitWidth != null) width = parseUnitSize(unitWidth, 100);
143
- if (unitHeight != null) height = parseUnitSize(unitHeight, 100);
144
-
145
- String viewBoxStr = properties.getString("viewBox");
146
- if (viewBoxStr != null) {
147
- float[] viewBox = PApplet.parseFloat(PApplet.splitTokens(viewBoxStr));
148
- if (unitWidth == null || unitHeight == null) {
149
- // Not proper parsing of the viewBox, but will cover us for cases where
150
- // the width and height of the object is not specified.
151
- width = viewBox[2];
152
- height = viewBox[3];
153
- } else {
154
- // http://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute
155
- // TODO: preserveAspectRatio.
156
- if (matrix == null) matrix = new PMatrix2D();
157
- matrix.scale(width/viewBox[2], height/viewBox[3]);
158
- matrix.translate(-viewBox[0], -viewBox[1]);
190
+ svgWidth = width;
191
+ svgHeight = height;
192
+ svgSizeXY = PApplet.sqrt((svgWidth * svgWidth + svgHeight * svgHeight) / 2.0f);
159
193
  }
160
- }
161
-
162
- // Negative size is illegal.
163
- if (width < 0 || height < 0)
164
- throw new RuntimeException("<svg>: width (" + width +
165
- ") and height (" + height + ") must not be negative.");
166
-
167
- // It's technically valid to have width or height == 0. Not specified at
168
- // all is what to test for.
169
- if ((unitWidth == null || unitHeight == null) && viewBoxStr == null) {
170
- //throw new RuntimeException("width/height not specified");
171
- PGraphics.showWarning("The width and/or height is not " +
172
- "readable in the <svg> tag of this file.");
173
- // For the spec, the default is 100% and 100%. For purposes
174
- // here, insert a dummy value because this is prolly just a
175
- // font or something for which the w/h doesn't matter.
176
- width = 1;
177
- height = 1;
178
- }
179
-
180
- svgWidth = width;
181
- svgHeight = height;
182
- svgSizeXY = PApplet.sqrt((svgWidth*svgWidth + svgHeight*svgHeight)/2.0f);
183
- }
184
194
 
185
- element = properties;
186
- name = properties.getString("id");
187
- // @#$(* adobe illustrator mangles names of objects when re-saving
188
- if (name != null) {
189
- while (true) {
190
- String[] m = PApplet.match(name, "_x([A-Za-z0-9]{2})_");
191
- if (m == null) break;
192
- char repair = (char) PApplet.unhex(m[1]);
193
- name = name.replace(m[0], "" + repair);
194
- }
195
- }
195
+ element = properties;
196
+ name = properties.getString("id");
197
+ // @#$(* adobe illustrator mangles names of objects when re-saving
198
+ if (name != null) {
199
+ while (true) {
200
+ String[] m = PApplet.match(name, "_x([A-Za-z0-9]{2})_");
201
+ if (m == null) {
202
+ break;
203
+ }
204
+ char repair = (char) PApplet.unhex(m[1]);
205
+ name = name.replace(m[0], "" + repair);
206
+ }
207
+ }
196
208
 
197
- String displayStr = properties.getString("display", "inline");
198
- visible = !displayStr.equals("none");
209
+ String displayStr = properties.getString("display", "inline");
210
+ visible = !displayStr.equals("none");
199
211
 
200
- String transformStr = properties.getString("transform");
201
- if (transformStr != null) {
202
- if (matrix == null) {
203
- matrix = parseTransform(transformStr);
204
- } else {
205
- matrix.preApply(parseTransform(transformStr));
206
- }
207
- }
212
+ String transformStr = properties.getString("transform");
213
+ if (transformStr != null) {
214
+ if (matrix == null) {
215
+ matrix = parseTransform(transformStr);
216
+ } else {
217
+ matrix.preApply(parseTransform(transformStr));
218
+ }
219
+ }
208
220
 
209
- if (parseKids) {
210
- parseColors(properties);
211
- parseChildren(properties);
221
+ if (parseKids) {
222
+ parseColors(properties);
223
+ parseChildren(properties);
224
+ }
212
225
  }
213
- }
214
-
215
226
 
216
- // Broken out so that subclasses can copy any additional variables
217
- // (i.e. fillGradientPaint and strokeGradientPaint)
218
- protected void setParent(PShapeSVG parent) {
219
- // Need to set this so that findChild() works.
220
- // Otherwise 'parent' is null until addChild() is called later.
221
- this.parent = parent;
222
-
223
- if (parent == null) {
224
- // set values to their defaults according to the SVG spec
225
- stroke = false;
226
- strokeColor = 0xff000000;
227
- strokeWeight = 1;
228
- strokeCap = PConstants.SQUARE; // equivalent to BUTT in svg spec
229
- strokeJoin = PConstants.MITER;
230
- strokeGradient = null;
227
+ // Broken out so that subclasses can copy any additional variables
228
+ // (i.e. fillGradientPaint and strokeGradientPaint)
229
+ protected void setParent(PShapeSVG parent) {
230
+ // Need to set this so that findChild() works.
231
+ // Otherwise 'parent' is null until addChild() is called later.
232
+ this.parent = parent;
233
+
234
+ if (parent == null) {
235
+ // set values to their defaults according to the SVG spec
236
+ stroke = false;
237
+ strokeColor = 0xff000000;
238
+ strokeWeight = 1;
239
+ strokeCap = PConstants.SQUARE; // equivalent to BUTT in svg spec
240
+ strokeJoin = PConstants.MITER;
241
+ strokeGradient = null;
231
242
  // strokeGradientPaint = null;
232
- strokeName = null;
243
+ strokeName = null;
233
244
 
234
- fill = true;
235
- fillColor = 0xff000000;
236
- fillGradient = null;
245
+ fill = true;
246
+ fillColor = 0xff000000;
247
+ fillGradient = null;
237
248
  // fillGradientPaint = null;
238
- fillName = null;
249
+ fillName = null;
239
250
 
240
- //hasTransform = false;
241
- //transformation = null; //new float[] { 1, 0, 0, 1, 0, 0 };
251
+ //hasTransform = false;
252
+ //transformation = null; //new float[] { 1, 0, 0, 1, 0, 0 };
253
+ // svgWidth, svgHeight, and svgXYSize done below.
254
+ strokeOpacity = 1;
255
+ fillOpacity = 1;
256
+ opacity = 1;
242
257
 
243
- // svgWidth, svgHeight, and svgXYSize done below.
244
-
245
- strokeOpacity = 1;
246
- fillOpacity = 1;
247
- opacity = 1;
248
-
249
- } else {
250
- stroke = parent.stroke;
251
- strokeColor = parent.strokeColor;
252
- strokeWeight = parent.strokeWeight;
253
- strokeCap = parent.strokeCap;
254
- strokeJoin = parent.strokeJoin;
255
- strokeGradient = parent.strokeGradient;
258
+ } else {
259
+ stroke = parent.stroke;
260
+ strokeColor = parent.strokeColor;
261
+ strokeWeight = parent.strokeWeight;
262
+ strokeCap = parent.strokeCap;
263
+ strokeJoin = parent.strokeJoin;
264
+ strokeGradient = parent.strokeGradient;
256
265
  // strokeGradientPaint = parent.strokeGradientPaint;
257
- strokeName = parent.strokeName;
266
+ strokeName = parent.strokeName;
258
267
 
259
- fill = parent.fill;
260
- fillColor = parent.fillColor;
261
- fillGradient = parent.fillGradient;
268
+ fill = parent.fill;
269
+ fillColor = parent.fillColor;
270
+ fillGradient = parent.fillGradient;
262
271
  // fillGradientPaint = parent.fillGradientPaint;
263
- fillName = parent.fillName;
264
-
265
- svgWidth = parent.svgWidth;
266
- svgHeight = parent.svgHeight;
267
- svgSizeXY = parent.svgSizeXY;
272
+ fillName = parent.fillName;
268
273
 
269
- opacity = parent.opacity;
270
- }
271
-
272
- // The rect and ellipse modes are set to CORNER since it is the expected
273
- // mode for svg shapes.
274
- rectMode = CORNER;
275
- ellipseMode = CORNER;
276
- }
274
+ svgWidth = parent.svgWidth;
275
+ svgHeight = parent.svgHeight;
276
+ svgSizeXY = parent.svgSizeXY;
277
277
 
278
+ opacity = parent.opacity;
279
+ }
278
280
 
279
- /** Factory method for subclasses. */
280
- protected PShapeSVG createShape(PShapeSVG parent, XML properties, boolean parseKids) {
281
- return new PShapeSVG(parent, properties, parseKids);
282
- }
281
+ // The rect and ellipse modes are set to CORNER since it is the expected
282
+ // mode for svg shapes.
283
+ rectMode = CORNER;
284
+ ellipseMode = CORNER;
285
+ }
283
286
 
287
+ /**
288
+ * Factory method for subclasses.
289
+ */
290
+ protected PShapeSVG createShape(PShapeSVG parent, XML properties, boolean parseKids) {
291
+ return new PShapeSVG(parent, properties, parseKids);
292
+ }
284
293
 
285
- protected void parseChildren(XML graphics) {
286
- XML[] elements = graphics.getChildren();
287
- children = new PShape[elements.length];
288
- childCount = 0;
294
+ protected void parseChildren(XML graphics) {
295
+ XML[] elements = graphics.getChildren();
296
+ children = new PShape[elements.length];
297
+ childCount = 0;
289
298
 
290
- for (XML elem : elements) {
291
- PShape kid = parseChild(elem);
292
- if (kid != null) addChild(kid);
299
+ for (XML elem : elements) {
300
+ PShape kid = parseChild(elem);
301
+ if (kid != null) {
302
+ addChild(kid);
303
+ }
304
+ }
305
+ children = (PShape[]) PApplet.subset(children, 0, childCount);
293
306
  }
294
- children = (PShape[]) PApplet.subset(children, 0, childCount);
295
- }
296
-
297
307
 
298
- /**
299
- * Parse a child XML element.
300
- * Override this method to add parsing for more SVG elements.
301
- */
302
- protected PShape parseChild(XML elem) {
308
+ /**
309
+ * Parse a child XML element. Override this method to add parsing for more
310
+ * SVG elements.
311
+ */
312
+ protected PShape parseChild(XML elem) {
303
313
  // System.err.println("parsing child in pshape " + elem.getName());
304
- String name = elem.getName();
305
- PShapeSVG shape = null;
306
-
314
+ String name = elem.getName();
315
+ PShapeSVG shape = null;
307
316
 
308
- if (name == null) {
309
- // just some whitespace that can be ignored (hopefully)
317
+ if (name == null) {
318
+ // just some whitespace that can be ignored (hopefully)
310
319
 
311
- } else if (name.equals("g")) {
312
- shape = createShape(this, elem, true);
320
+ } else if (name.equals("g")) {
321
+ shape = createShape(this, elem, true);
313
322
 
314
- } else if (name.equals("defs")) {
315
- // generally this will contain gradient info, so may
316
- // as well just throw it into a group element for parsing
317
- shape = createShape(this, elem, true);
323
+ } else if (name.equals("defs")) {
324
+ // generally this will contain gradient info, so may
325
+ // as well just throw it into a group element for parsing
326
+ shape = createShape(this, elem, true);
318
327
 
319
- } else if (name.equals("line")) {
320
- shape = createShape(this, elem, true);
321
- shape.parseLine();
328
+ } else if (name.equals("line")) {
329
+ shape = createShape(this, elem, true);
330
+ shape.parseLine();
322
331
 
323
- } else if (name.equals("circle")) {
324
- shape = createShape(this, elem, true);
325
- shape.parseEllipse(true);
332
+ } else if (name.equals("circle")) {
333
+ shape = createShape(this, elem, true);
334
+ shape.parseEllipse(true);
326
335
 
327
- } else if (name.equals("ellipse")) {
328
- shape = createShape(this, elem, true);
329
- shape.parseEllipse(false);
336
+ } else if (name.equals("ellipse")) {
337
+ shape = createShape(this, elem, true);
338
+ shape.parseEllipse(false);
330
339
 
331
- } else if (name.equals("rect")) {
332
- shape = createShape(this, elem, true);
333
- shape.parseRect();
340
+ } else if (name.equals("rect")) {
341
+ shape = createShape(this, elem, true);
342
+ shape.parseRect();
334
343
 
335
- } else if (name.equals("polygon")) {
336
- shape = createShape(this, elem, true);
337
- shape.parsePoly(true);
344
+ } else if (name.equals("polygon")) {
345
+ shape = createShape(this, elem, true);
346
+ shape.parsePoly(true);
338
347
 
339
- } else if (name.equals("polyline")) {
340
- shape = createShape(this, elem, true);
341
- shape.parsePoly(false);
348
+ } else if (name.equals("polyline")) {
349
+ shape = createShape(this, elem, true);
350
+ shape.parsePoly(false);
342
351
 
343
- } else if (name.equals("path")) {
344
- shape = createShape(this, elem, true);
345
- shape.parsePath();
352
+ } else if (name.equals("path")) {
353
+ shape = createShape(this, elem, true);
354
+ shape.parsePath();
346
355
 
347
- } else if (name.equals("radialGradient")) {
348
- return new RadialGradient(this, elem);
356
+ } else if (name.equals("radialGradient")) {
357
+ return new RadialGradient(this, elem);
349
358
 
350
- } else if (name.equals("linearGradient")) {
351
- return new LinearGradient(this, elem);
359
+ } else if (name.equals("linearGradient")) {
360
+ return new LinearGradient(this, elem);
352
361
 
353
- } else if (name.equals("font")) {
354
- return new Font(this, elem);
362
+ } else if (name.equals("font")) {
363
+ return new Font(this, elem);
355
364
 
356
365
  // } else if (name.equals("font-face")) {
357
366
  // return new FontFace(this, elem);
358
-
359
367
  // } else if (name.equals("glyph") || name.equals("missing-glyph")) {
360
368
  // return new FontGlyph(this, elem);
369
+ } else if (name.equals("text")) { // || name.equals("font")) {
370
+ PGraphics.showWarning("Text and fonts in SVG files are "
371
+ + "not currently supported, convert text to outlines instead.");
361
372
 
362
- } else if (name.equals("text")) { // || name.equals("font")) {
363
- PGraphics.showWarning("Text and fonts in SVG files are " +
364
- "not currently supported, convert text to outlines instead.");
373
+ } else if (name.equals("filter")) {
374
+ PGraphics.showWarning("Filters are not supported.");
365
375
 
366
- } else if (name.equals("filter")) {
367
- PGraphics.showWarning("Filters are not supported.");
376
+ } else if (name.equals("mask")) {
377
+ PGraphics.showWarning("Masks are not supported.");
368
378
 
369
- } else if (name.equals("mask")) {
370
- PGraphics.showWarning("Masks are not supported.");
379
+ } else if (name.equals("pattern")) {
380
+ PGraphics.showWarning("Patterns are not supported.");
371
381
 
372
- } else if (name.equals("pattern")) {
373
- PGraphics.showWarning("Patterns are not supported.");
382
+ } else if (name.equals("stop")) {
383
+ // stop tag is handled by gradient parser, so don't warn about it
374
384
 
375
- } else if (name.equals("stop")) {
376
- // stop tag is handled by gradient parser, so don't warn about it
385
+ } else if (name.equals("sodipodi:namedview")) {
386
+ // these are always in Inkscape files, the warnings get tedious
377
387
 
378
- } else if (name.equals("sodipodi:namedview")) {
379
- // these are always in Inkscape files, the warnings get tedious
388
+ } else if (name.equals("metadata")
389
+ || name.equals("title") || name.equals("desc")) {
390
+ // fontforge just stuffs <metadata> in as a comment.
391
+ // All harmless stuff, irrelevant to rendering.
392
+ return null;
380
393
 
381
- } else if (name.equals("metadata")
382
- || name.equals("title") || name.equals("desc")) {
383
- // fontforge just stuffs <metadata> in as a comment.
384
- // All harmless stuff, irrelevant to rendering.
385
- return null;
386
-
387
- } else if (!name.startsWith("#")) {
388
- PGraphics.showWarning("Ignoring <" + name + "> tag.");
394
+ } else if (!name.startsWith("#")) {
395
+ PGraphics.showWarning("Ignoring <" + name + "> tag.");
389
396
  // new Exception().printStackTrace();
397
+ }
398
+ return shape;
390
399
  }
391
- return shape;
392
- }
393
-
394
-
395
- protected void parseLine() {
396
- kind = LINE;
397
- family = PRIMITIVE;
398
- params = new float[] {
399
- getFloatWithUnit(element, "x1", svgWidth),
400
- getFloatWithUnit(element, "y1", svgHeight),
401
- getFloatWithUnit(element, "x2", svgWidth),
402
- getFloatWithUnit(element, "y2", svgHeight)
403
- };
404
- }
405
-
406
400
 
407
- /**
408
- * Handles parsing ellipse and circle tags.
409
- * @param circle true if this is a circle and not an ellipse
410
- */
411
- protected void parseEllipse(boolean circle) {
412
- kind = ELLIPSE;
413
- family = PRIMITIVE;
414
- params = new float[4];
415
-
416
- params[0] = getFloatWithUnit(element, "cx", svgWidth);
417
- params[1] = getFloatWithUnit(element, "cy", svgHeight);
418
-
419
- float rx, ry;
420
- if (circle) {
421
- rx = ry = getFloatWithUnit(element, "r", svgSizeXY);
422
- } else {
423
- rx = getFloatWithUnit(element, "rx", svgWidth);
424
- ry = getFloatWithUnit(element, "ry", svgHeight);
401
+ protected void parseLine() {
402
+ kind = LINE;
403
+ family = PRIMITIVE;
404
+ params = new float[]{
405
+ getFloatWithUnit(element, "x1", svgWidth),
406
+ getFloatWithUnit(element, "y1", svgHeight),
407
+ getFloatWithUnit(element, "x2", svgWidth),
408
+ getFloatWithUnit(element, "y2", svgHeight)
409
+ };
425
410
  }
426
- params[0] -= rx;
427
- params[1] -= ry;
428
-
429
- params[2] = rx*2;
430
- params[3] = ry*2;
431
- }
432
411
 
412
+ /**
413
+ * Handles parsing ellipse and circle tags.
414
+ *
415
+ * @param circle true if this is a circle and not an ellipse
416
+ */
417
+ protected void parseEllipse(boolean circle) {
418
+ kind = ELLIPSE;
419
+ family = PRIMITIVE;
420
+ params = new float[4];
421
+
422
+ params[0] = getFloatWithUnit(element, "cx", svgWidth);
423
+ params[1] = getFloatWithUnit(element, "cy", svgHeight);
424
+
425
+ float rx, ry;
426
+ if (circle) {
427
+ rx = ry = getFloatWithUnit(element, "r", svgSizeXY);
428
+ } else {
429
+ rx = getFloatWithUnit(element, "rx", svgWidth);
430
+ ry = getFloatWithUnit(element, "ry", svgHeight);
431
+ }
432
+ params[0] -= rx;
433
+ params[1] -= ry;
433
434
 
434
- protected void parseRect() {
435
- kind = RECT;
436
- family = PRIMITIVE;
437
- params = new float[] {
438
- getFloatWithUnit(element, "x", svgWidth),
439
- getFloatWithUnit(element, "y", svgHeight),
440
- getFloatWithUnit(element, "width", svgWidth),
441
- getFloatWithUnit(element, "height", svgHeight)
442
- };
443
- }
435
+ params[2] = rx * 2;
436
+ params[3] = ry * 2;
437
+ }
444
438
 
439
+ protected void parseRect() {
440
+ kind = RECT;
441
+ family = PRIMITIVE;
442
+ params = new float[]{
443
+ getFloatWithUnit(element, "x", svgWidth),
444
+ getFloatWithUnit(element, "y", svgHeight),
445
+ getFloatWithUnit(element, "width", svgWidth),
446
+ getFloatWithUnit(element, "height", svgHeight)
447
+ };
448
+ }
445
449
 
446
- /**
447
- * Parse a polyline or polygon from an SVG file.
448
- * Syntax defined at http://www.w3.org/TR/SVG/shapes.html#PointsBNF
449
- * @param close true if shape is closed (polygon), false if not (polyline)
450
- */
451
- protected void parsePoly(boolean close) {
452
- family = PATH;
453
- this.close = close;
454
-
455
- String pointsAttr = element.getString("points");
456
- if (pointsAttr != null) {
457
- Pattern pattern = Pattern.compile("([+-]?[\\d]+(\\.[\\d]+)?([eE][+-][\\d]+)?)(,?\\s*)([+-]?[\\d]+(\\.[\\d]+)?([eE][+-][\\d]+)?)");
458
- Matcher matcher = pattern.matcher(pointsAttr);
459
- vertexCount = 0;
460
- while (matcher.find()) {
461
- vertexCount++;
462
- }
463
- matcher.reset();
464
- vertices = new float[vertexCount][2];
465
- for (int i = 0; i < vertexCount; i++) {
466
- matcher.find();
467
- vertices[i][X] = Float.parseFloat(matcher.group(1));
468
- vertices[i][Y] = Float.parseFloat(matcher.group(5));
469
- }
450
+ /**
451
+ * Parse a polyline or polygon from an SVG file. Syntax defined at
452
+ * http://www.w3.org/TR/SVG/shapes.html#PointsBNF
453
+ *
454
+ * @param close true if shape is closed (polygon), false if not (polyline)
455
+ */
456
+ protected void parsePoly(boolean close) {
457
+ family = PATH;
458
+ this.close = close;
459
+
460
+ String pointsAttr = element.getString("points");
461
+ if (pointsAttr != null) {
462
+ Pattern pattern = Pattern.compile("([+-]?[\\d]+(\\.[\\d]+)?([eE][+-][\\d]+)?)(,?\\s*)([+-]?[\\d]+(\\.[\\d]+)?([eE][+-][\\d]+)?)");
463
+ Matcher matcher = pattern.matcher(pointsAttr);
464
+ vertexCount = 0;
465
+ while (matcher.find()) {
466
+ vertexCount++;
467
+ }
468
+ matcher.reset();
469
+ vertices = new float[vertexCount][2];
470
+ for (int i = 0; i < vertexCount; i++) {
471
+ matcher.find();
472
+ vertices[i][X] = Float.parseFloat(matcher.group(1));
473
+ vertices[i][Y] = Float.parseFloat(matcher.group(5));
474
+ }
470
475
  // String[] pointsBuffer = PApplet.splitTokens(pointsAttr);
471
476
  // vertexCount = pointsBuffer.length;
472
477
  // vertices = new float[vertexCount][2];
@@ -475,391 +480,393 @@ public class PShapeSVG extends PShape {
475
480
  // vertices[i][X] = Float.parseFloat(pb[0]);
476
481
  // vertices[i][Y] = Float.parseFloat(pb[1]);
477
482
  // }
483
+ }
478
484
  }
479
- }
480
485
 
486
+ protected void parsePath() {
487
+ family = PATH;
488
+ kind = 0;
481
489
 
482
- protected void parsePath() {
483
- family = PATH;
484
- kind = 0;
485
-
486
- String pathData = element.getString("d");
487
- if (pathData == null || PApplet.trim(pathData).length() == 0) {
488
- return;
489
- }
490
- char[] pathDataChars = pathData.toCharArray();
491
-
492
- StringBuilder pathBuffer = new StringBuilder();
493
- boolean lastSeparate = false;
494
-
495
- for (int i = 0; i < pathDataChars.length; i++) {
496
- char c = pathDataChars[i];
497
- boolean separate = false;
498
-
499
- if (c == 'M' || c == 'm' ||
500
- c == 'L' || c == 'l' ||
501
- c == 'H' || c == 'h' ||
502
- c == 'V' || c == 'v' ||
503
- c == 'C' || c == 'c' || // beziers
504
- c == 'S' || c == 's' ||
505
- c == 'Q' || c == 'q' || // quadratic beziers
506
- c == 'T' || c == 't' ||
507
- c == 'A' || c == 'a' || // elliptical arc
508
- c == 'Z' || c == 'z' || // closepath
509
- c == ',') {
510
- separate = true;
511
- if (i != 0) {
512
- pathBuffer.append("|");
490
+ String pathData = element.getString("d");
491
+ if (pathData == null || PApplet.trim(pathData).length() == 0) {
492
+ return;
513
493
  }
514
- }
515
- if (c == 'Z' || c == 'z') {
516
- separate = false;
517
- }
518
- if (c == '-' && !lastSeparate) {
519
- // allow for 'e' notation in numbers, e.g. 2.10e-9
520
- // http://dev.processing.org/bugs/show_bug.cgi?id=1408
521
- if (i == 0 || pathDataChars[i-1] != 'e') {
522
- pathBuffer.append("|");
494
+ char[] pathDataChars = pathData.toCharArray();
495
+
496
+ StringBuilder pathBuffer = new StringBuilder();
497
+ boolean lastSeparate = false;
498
+
499
+ for (int i = 0; i < pathDataChars.length; i++) {
500
+ char c = pathDataChars[i];
501
+ boolean separate = false;
502
+
503
+ if (c == 'M' || c == 'm'
504
+ || c == 'L' || c == 'l'
505
+ || c == 'H' || c == 'h'
506
+ || c == 'V' || c == 'v'
507
+ || c == 'C' || c == 'c'
508
+ || // beziers
509
+ c == 'S' || c == 's'
510
+ || c == 'Q' || c == 'q'
511
+ || // quadratic beziers
512
+ c == 'T' || c == 't'
513
+ || c == 'A' || c == 'a'
514
+ || // elliptical arc
515
+ c == 'Z' || c == 'z'
516
+ || // closepath
517
+ c == ',') {
518
+ separate = true;
519
+ if (i != 0) {
520
+ pathBuffer.append("|");
521
+ }
522
+ }
523
+ if (c == 'Z' || c == 'z') {
524
+ separate = false;
525
+ }
526
+ if (c == '-' && !lastSeparate) {
527
+ // allow for 'e' notation in numbers, e.g. 2.10e-9
528
+ // http://dev.processing.org/bugs/show_bug.cgi?id=1408
529
+ if (i == 0 || pathDataChars[i - 1] != 'e') {
530
+ pathBuffer.append("|");
531
+ }
532
+ }
533
+ if (c != ',') {
534
+ pathBuffer.append(c); //"" + pathDataBuffer.charAt(i));
535
+ }
536
+ if (separate && c != ',' && c != '-') {
537
+ pathBuffer.append("|");
538
+ }
539
+ lastSeparate = separate;
523
540
  }
524
- }
525
- if (c != ',') {
526
- pathBuffer.append(c); //"" + pathDataBuffer.charAt(i));
527
- }
528
- if (separate && c != ',' && c != '-') {
529
- pathBuffer.append("|");
530
- }
531
- lastSeparate = separate;
532
- }
533
541
 
534
- // use whitespace constant to get rid of extra spaces and CR or LF
535
- String[] pathTokens =
536
- PApplet.splitTokens(pathBuffer.toString(), "|" + WHITESPACE);
537
- vertices = new float[pathTokens.length][2];
538
- vertexCodes = new int[pathTokens.length];
542
+ // use whitespace constant to get rid of extra spaces and CR or LF
543
+ String[] pathTokens
544
+ = PApplet.splitTokens(pathBuffer.toString(), "|" + WHITESPACE);
545
+ vertices = new float[pathTokens.length][2];
546
+ vertexCodes = new int[pathTokens.length];
539
547
 
540
- float cx = 0;
541
- float cy = 0;
542
- int i = 0;
548
+ float cx = 0;
549
+ float cy = 0;
550
+ int i = 0;
543
551
 
544
- char implicitCommand = '\0';
552
+ char implicitCommand = '\0';
545
553
  // char prevCommand = '\0';
546
- boolean prevCurve = false;
547
- float ctrlX, ctrlY;
548
- // store values for closepath so that relative coords work properly
549
- float movetoX = 0;
550
- float movetoY = 0;
551
-
552
- while (i < pathTokens.length) {
553
- char c = pathTokens[i].charAt(0);
554
- if (((c >= '0' && c <= '9') || (c == '-')) && implicitCommand != '\0') {
555
- c = implicitCommand;
556
- i--;
557
- } else {
558
- implicitCommand = c;
559
- }
560
- switch (c) {
561
-
562
- case 'M': // M - move to (absolute)
563
- cx = PApplet.parseFloat(pathTokens[i + 1]);
564
- cy = PApplet.parseFloat(pathTokens[i + 2]);
565
- movetoX = cx;
566
- movetoY = cy;
567
- parsePathMoveto(cx, cy);
568
- implicitCommand = 'L';
569
- i += 3;
570
- break;
571
-
572
- case 'm': // m - move to (relative)
573
- cx = cx + PApplet.parseFloat(pathTokens[i + 1]);
574
- cy = cy + PApplet.parseFloat(pathTokens[i + 2]);
575
- movetoX = cx;
576
- movetoY = cy;
577
- parsePathMoveto(cx, cy);
578
- implicitCommand = 'l';
579
- i += 3;
580
- break;
581
-
582
- case 'L':
583
- cx = PApplet.parseFloat(pathTokens[i + 1]);
584
- cy = PApplet.parseFloat(pathTokens[i + 2]);
585
- parsePathLineto(cx, cy);
586
- i += 3;
587
- break;
588
-
589
- case 'l':
590
- cx = cx + PApplet.parseFloat(pathTokens[i + 1]);
591
- cy = cy + PApplet.parseFloat(pathTokens[i + 2]);
592
- parsePathLineto(cx, cy);
593
- i += 3;
594
- break;
595
-
596
- // horizontal lineto absolute
597
- case 'H':
598
- cx = PApplet.parseFloat(pathTokens[i + 1]);
599
- parsePathLineto(cx, cy);
600
- i += 2;
601
- break;
602
-
603
- // horizontal lineto relative
604
- case 'h':
605
- cx = cx + PApplet.parseFloat(pathTokens[i + 1]);
606
- parsePathLineto(cx, cy);
607
- i += 2;
608
- break;
609
-
610
- case 'V':
611
- cy = PApplet.parseFloat(pathTokens[i + 1]);
612
- parsePathLineto(cx, cy);
613
- i += 2;
614
- break;
615
-
616
- case 'v':
617
- cy = cy + PApplet.parseFloat(pathTokens[i + 1]);
618
- parsePathLineto(cx, cy);
619
- i += 2;
620
- break;
621
-
622
- // C - curve to (absolute)
623
- case 'C': {
624
- float ctrlX1 = PApplet.parseFloat(pathTokens[i + 1]);
625
- float ctrlY1 = PApplet.parseFloat(pathTokens[i + 2]);
626
- float ctrlX2 = PApplet.parseFloat(pathTokens[i + 3]);
627
- float ctrlY2 = PApplet.parseFloat(pathTokens[i + 4]);
628
- float endX = PApplet.parseFloat(pathTokens[i + 5]);
629
- float endY = PApplet.parseFloat(pathTokens[i + 6]);
630
- parsePathCurveto(ctrlX1, ctrlY1, ctrlX2, ctrlY2, endX, endY);
631
- cx = endX;
632
- cy = endY;
633
- i += 7;
634
- prevCurve = true;
635
- }
636
- break;
637
-
638
- // c - curve to (relative)
639
- case 'c': {
640
- float ctrlX1 = cx + PApplet.parseFloat(pathTokens[i + 1]);
641
- float ctrlY1 = cy + PApplet.parseFloat(pathTokens[i + 2]);
642
- float ctrlX2 = cx + PApplet.parseFloat(pathTokens[i + 3]);
643
- float ctrlY2 = cy + PApplet.parseFloat(pathTokens[i + 4]);
644
- float endX = cx + PApplet.parseFloat(pathTokens[i + 5]);
645
- float endY = cy + PApplet.parseFloat(pathTokens[i + 6]);
646
- parsePathCurveto(ctrlX1, ctrlY1, ctrlX2, ctrlY2, endX, endY);
647
- cx = endX;
648
- cy = endY;
649
- i += 7;
650
- prevCurve = true;
651
- }
652
- break;
653
-
654
- // S - curve to shorthand (absolute)
655
- // Draws a cubic Bézier curve from the current point to (x,y). The first
656
- // control point is assumed to be the reflection of the second control
657
- // point on the previous command relative to the current point.
658
- // (x2,y2) is the second control point (i.e., the control point
659
- // at the end of the curve). S (uppercase) indicates that absolute
660
- // coordinates will follow; s (lowercase) indicates that relative
661
- // coordinates will follow. Multiple sets of coordinates may be specified
662
- // to draw a polybézier. At the end of the command, the new current point
663
- // becomes the final (x,y) coordinate pair used in the polybézier.
664
- case 'S': {
665
- // (If there is no previous command or if the previous command was not
666
- // an C, c, S or s, assume the first control point is coincident with
667
- // the current point.)
668
- if (!prevCurve) {
669
- ctrlX = cx;
670
- ctrlY = cy;
671
- } else {
672
- float ppx = vertices[vertexCount-2][X];
673
- float ppy = vertices[vertexCount-2][Y];
674
- float px = vertices[vertexCount-1][X];
675
- float py = vertices[vertexCount-1][Y];
676
- ctrlX = px + (px - ppx);
677
- ctrlY = py + (py - ppy);
678
- }
679
- float ctrlX2 = PApplet.parseFloat(pathTokens[i + 1]);
680
- float ctrlY2 = PApplet.parseFloat(pathTokens[i + 2]);
681
- float endX = PApplet.parseFloat(pathTokens[i + 3]);
682
- float endY = PApplet.parseFloat(pathTokens[i + 4]);
683
- parsePathCurveto(ctrlX, ctrlY, ctrlX2, ctrlY2, endX, endY);
684
- cx = endX;
685
- cy = endY;
686
- i += 5;
687
- prevCurve = true;
688
- }
689
- break;
690
-
691
- // s - curve to shorthand (relative)
692
- case 's': {
693
- if (!prevCurve) {
694
- ctrlX = cx;
695
- ctrlY = cy;
696
- } else {
697
- float ppx = vertices[vertexCount-2][X];
698
- float ppy = vertices[vertexCount-2][Y];
699
- float px = vertices[vertexCount-1][X];
700
- float py = vertices[vertexCount-1][Y];
701
- ctrlX = px + (px - ppx);
702
- ctrlY = py + (py - ppy);
703
- }
704
- float ctrlX2 = cx + PApplet.parseFloat(pathTokens[i + 1]);
705
- float ctrlY2 = cy + PApplet.parseFloat(pathTokens[i + 2]);
706
- float endX = cx + PApplet.parseFloat(pathTokens[i + 3]);
707
- float endY = cy + PApplet.parseFloat(pathTokens[i + 4]);
708
- parsePathCurveto(ctrlX, ctrlY, ctrlX2, ctrlY2, endX, endY);
709
- cx = endX;
710
- cy = endY;
711
- i += 5;
712
- prevCurve = true;
713
- }
714
- break;
715
-
716
- // Q - quadratic curve to (absolute)
717
- // Draws a quadratic Bézier curve from the current point to (x,y) using
718
- // (x1,y1) as the control point. Q (uppercase) indicates that absolute
719
- // coordinates will follow; q (lowercase) indicates that relative
720
- // coordinates will follow. Multiple sets of coordinates may be specified
721
- // to draw a polybézier. At the end of the command, the new current point
722
- // becomes the final (x,y) coordinate pair used in the polybézier.
723
- case 'Q': {
724
- ctrlX = PApplet.parseFloat(pathTokens[i + 1]);
725
- ctrlY = PApplet.parseFloat(pathTokens[i + 2]);
726
- float endX = PApplet.parseFloat(pathTokens[i + 3]);
727
- float endY = PApplet.parseFloat(pathTokens[i + 4]);
728
- //parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY);
729
- parsePathQuadto(ctrlX, ctrlY, endX, endY);
730
- cx = endX;
731
- cy = endY;
732
- i += 5;
733
- prevCurve = true;
734
- }
735
- break;
736
-
737
- // q - quadratic curve to (relative)
738
- case 'q': {
739
- ctrlX = cx + PApplet.parseFloat(pathTokens[i + 1]);
740
- ctrlY = cy + PApplet.parseFloat(pathTokens[i + 2]);
741
- float endX = cx + PApplet.parseFloat(pathTokens[i + 3]);
742
- float endY = cy + PApplet.parseFloat(pathTokens[i + 4]);
743
- //parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY);
744
- parsePathQuadto(ctrlX, ctrlY, endX, endY);
745
- cx = endX;
746
- cy = endY;
747
- i += 5;
748
- prevCurve = true;
749
- }
750
- break;
751
-
752
- // T - quadratic curveto shorthand (absolute)
753
- // The control point is assumed to be the reflection of the control
754
- // point on the previous command relative to the current point.
755
- case 'T': {
756
- // If there is no previous command or if the previous command was
757
- // not a Q, q, T or t, assume the control point is coincident
758
- // with the current point.
759
- if (!prevCurve) {
760
- ctrlX = cx;
761
- ctrlY = cy;
762
- } else {
763
- float ppx = vertices[vertexCount-2][X];
764
- float ppy = vertices[vertexCount-2][Y];
765
- float px = vertices[vertexCount-1][X];
766
- float py = vertices[vertexCount-1][Y];
767
- ctrlX = px + (px - ppx);
768
- ctrlY = py + (py - ppy);
769
- }
770
- float endX = PApplet.parseFloat(pathTokens[i + 1]);
771
- float endY = PApplet.parseFloat(pathTokens[i + 2]);
772
- //parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY);
773
- parsePathQuadto(ctrlX, ctrlY, endX, endY);
774
- cx = endX;
775
- cy = endY;
776
- i += 3;
777
- prevCurve = true;
778
- }
779
- break;
780
-
781
- // t - quadratic curveto shorthand (relative)
782
- case 't': {
783
- if (!prevCurve) {
784
- ctrlX = cx;
785
- ctrlY = cy;
786
- } else {
787
- float ppx = vertices[vertexCount-2][X];
788
- float ppy = vertices[vertexCount-2][Y];
789
- float px = vertices[vertexCount-1][X];
790
- float py = vertices[vertexCount-1][Y];
791
- ctrlX = px + (px - ppx);
792
- ctrlY = py + (py - ppy);
793
- }
794
- float endX = cx + PApplet.parseFloat(pathTokens[i + 1]);
795
- float endY = cy + PApplet.parseFloat(pathTokens[i + 2]);
796
- //parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY);
797
- parsePathQuadto(ctrlX, ctrlY, endX, endY);
798
- cx = endX;
799
- cy = endY;
800
- i += 3;
801
- prevCurve = true;
802
- }
803
- break;
804
-
805
- // A - elliptical arc to (absolute)
806
- case 'A': {
807
- float rx = PApplet.parseFloat(pathTokens[i + 1]);
808
- float ry = PApplet.parseFloat(pathTokens[i + 2]);
809
- float angle = PApplet.parseFloat(pathTokens[i + 3]);
810
- boolean fa = PApplet.parseFloat(pathTokens[i + 4]) != 0;
811
- boolean fs = PApplet.parseFloat(pathTokens[i + 5]) != 0;
812
- float endX = PApplet.parseFloat(pathTokens[i + 6]);
813
- float endY = PApplet.parseFloat(pathTokens[i + 7]);
814
- parsePathArcto(cx, cy, rx, ry, angle, fa, fs, endX, endY);
815
- cx = endX;
816
- cy = endY;
817
- i += 8;
818
- prevCurve = true;
819
- }
820
- break;
821
-
822
- // a - elliptical arc to (relative)
823
- case 'a': {
824
- float rx = PApplet.parseFloat(pathTokens[i + 1]);
825
- float ry = PApplet.parseFloat(pathTokens[i + 2]);
826
- float angle = PApplet.parseFloat(pathTokens[i + 3]);
827
- boolean fa = PApplet.parseFloat(pathTokens[i + 4]) != 0;
828
- boolean fs = PApplet.parseFloat(pathTokens[i + 5]) != 0;
829
- float endX = cx + PApplet.parseFloat(pathTokens[i + 6]);
830
- float endY = cy + PApplet.parseFloat(pathTokens[i + 7]);
831
- parsePathArcto(cx, cy, rx, ry, angle, fa, fs, endX, endY);
832
- cx = endX;
833
- cy = endY;
834
- i += 8;
835
- prevCurve = true;
836
- }
837
- break;
838
-
839
- case 'Z':
840
- case 'z':
841
- // since closing the path, the 'current' point needs
842
- // to return back to the last moveto location.
843
- // http://code.google.com/p/processing/issues/detail?id=1058
844
- cx = movetoX;
845
- cy = movetoY;
846
- close = true;
847
- i++;
848
- break;
849
-
850
- default:
851
- String parsed =
852
- PApplet.join(PApplet.subset(pathTokens, 0, i), ",");
853
- String unparsed =
854
- PApplet.join(PApplet.subset(pathTokens, i), ",");
855
- System.err.println("parsed: " + parsed);
856
- System.err.println("unparsed: " + unparsed);
857
- throw new RuntimeException("shape command not handled: " + pathTokens[i]);
858
- }
554
+ boolean prevCurve = false;
555
+ float ctrlX, ctrlY;
556
+ // store values for closepath so that relative coords work properly
557
+ float movetoX = 0;
558
+ float movetoY = 0;
559
+
560
+ while (i < pathTokens.length) {
561
+ char c = pathTokens[i].charAt(0);
562
+ if (((c >= '0' && c <= '9') || (c == '-')) && implicitCommand != '\0') {
563
+ c = implicitCommand;
564
+ i--;
565
+ } else {
566
+ implicitCommand = c;
567
+ }
568
+ switch (c) {
569
+
570
+ case 'M': // M - move to (absolute)
571
+ cx = PApplet.parseFloat(pathTokens[i + 1]);
572
+ cy = PApplet.parseFloat(pathTokens[i + 2]);
573
+ movetoX = cx;
574
+ movetoY = cy;
575
+ parsePathMoveto(cx, cy);
576
+ implicitCommand = 'L';
577
+ i += 3;
578
+ break;
579
+
580
+ case 'm': // m - move to (relative)
581
+ cx = cx + PApplet.parseFloat(pathTokens[i + 1]);
582
+ cy = cy + PApplet.parseFloat(pathTokens[i + 2]);
583
+ movetoX = cx;
584
+ movetoY = cy;
585
+ parsePathMoveto(cx, cy);
586
+ implicitCommand = 'l';
587
+ i += 3;
588
+ break;
589
+
590
+ case 'L':
591
+ cx = PApplet.parseFloat(pathTokens[i + 1]);
592
+ cy = PApplet.parseFloat(pathTokens[i + 2]);
593
+ parsePathLineto(cx, cy);
594
+ i += 3;
595
+ break;
596
+
597
+ case 'l':
598
+ cx = cx + PApplet.parseFloat(pathTokens[i + 1]);
599
+ cy = cy + PApplet.parseFloat(pathTokens[i + 2]);
600
+ parsePathLineto(cx, cy);
601
+ i += 3;
602
+ break;
603
+
604
+ // horizontal lineto absolute
605
+ case 'H':
606
+ cx = PApplet.parseFloat(pathTokens[i + 1]);
607
+ parsePathLineto(cx, cy);
608
+ i += 2;
609
+ break;
610
+
611
+ // horizontal lineto relative
612
+ case 'h':
613
+ cx = cx + PApplet.parseFloat(pathTokens[i + 1]);
614
+ parsePathLineto(cx, cy);
615
+ i += 2;
616
+ break;
617
+
618
+ case 'V':
619
+ cy = PApplet.parseFloat(pathTokens[i + 1]);
620
+ parsePathLineto(cx, cy);
621
+ i += 2;
622
+ break;
623
+
624
+ case 'v':
625
+ cy = cy + PApplet.parseFloat(pathTokens[i + 1]);
626
+ parsePathLineto(cx, cy);
627
+ i += 2;
628
+ break;
629
+
630
+ // C - curve to (absolute)
631
+ case 'C': {
632
+ float ctrlX1 = PApplet.parseFloat(pathTokens[i + 1]);
633
+ float ctrlY1 = PApplet.parseFloat(pathTokens[i + 2]);
634
+ float ctrlX2 = PApplet.parseFloat(pathTokens[i + 3]);
635
+ float ctrlY2 = PApplet.parseFloat(pathTokens[i + 4]);
636
+ float endX = PApplet.parseFloat(pathTokens[i + 5]);
637
+ float endY = PApplet.parseFloat(pathTokens[i + 6]);
638
+ parsePathCurveto(ctrlX1, ctrlY1, ctrlX2, ctrlY2, endX, endY);
639
+ cx = endX;
640
+ cy = endY;
641
+ i += 7;
642
+ prevCurve = true;
643
+ }
644
+ break;
645
+
646
+ // c - curve to (relative)
647
+ case 'c': {
648
+ float ctrlX1 = cx + PApplet.parseFloat(pathTokens[i + 1]);
649
+ float ctrlY1 = cy + PApplet.parseFloat(pathTokens[i + 2]);
650
+ float ctrlX2 = cx + PApplet.parseFloat(pathTokens[i + 3]);
651
+ float ctrlY2 = cy + PApplet.parseFloat(pathTokens[i + 4]);
652
+ float endX = cx + PApplet.parseFloat(pathTokens[i + 5]);
653
+ float endY = cy + PApplet.parseFloat(pathTokens[i + 6]);
654
+ parsePathCurveto(ctrlX1, ctrlY1, ctrlX2, ctrlY2, endX, endY);
655
+ cx = endX;
656
+ cy = endY;
657
+ i += 7;
658
+ prevCurve = true;
659
+ }
660
+ break;
661
+
662
+ // S - curve to shorthand (absolute)
663
+ // Draws a cubic Bézier curve from the current point to (x,y). The first
664
+ // control point is assumed to be the reflection of the second control
665
+ // point on the previous command relative to the current point.
666
+ // (x2,y2) is the second control point (i.e., the control point
667
+ // at the end of the curve). S (uppercase) indicates that absolute
668
+ // coordinates will follow; s (lowercase) indicates that relative
669
+ // coordinates will follow. Multiple sets of coordinates may be specified
670
+ // to draw a polybézier. At the end of the command, the new current point
671
+ // becomes the final (x,y) coordinate pair used in the polybézier.
672
+ case 'S': {
673
+ // (If there is no previous command or if the previous command was not
674
+ // an C, c, S or s, assume the first control point is coincident with
675
+ // the current point.)
676
+ if (!prevCurve) {
677
+ ctrlX = cx;
678
+ ctrlY = cy;
679
+ } else {
680
+ float ppx = vertices[vertexCount - 2][X];
681
+ float ppy = vertices[vertexCount - 2][Y];
682
+ float px = vertices[vertexCount - 1][X];
683
+ float py = vertices[vertexCount - 1][Y];
684
+ ctrlX = px + (px - ppx);
685
+ ctrlY = py + (py - ppy);
686
+ }
687
+ float ctrlX2 = PApplet.parseFloat(pathTokens[i + 1]);
688
+ float ctrlY2 = PApplet.parseFloat(pathTokens[i + 2]);
689
+ float endX = PApplet.parseFloat(pathTokens[i + 3]);
690
+ float endY = PApplet.parseFloat(pathTokens[i + 4]);
691
+ parsePathCurveto(ctrlX, ctrlY, ctrlX2, ctrlY2, endX, endY);
692
+ cx = endX;
693
+ cy = endY;
694
+ i += 5;
695
+ prevCurve = true;
696
+ }
697
+ break;
698
+
699
+ // s - curve to shorthand (relative)
700
+ case 's': {
701
+ if (!prevCurve) {
702
+ ctrlX = cx;
703
+ ctrlY = cy;
704
+ } else {
705
+ float ppx = vertices[vertexCount - 2][X];
706
+ float ppy = vertices[vertexCount - 2][Y];
707
+ float px = vertices[vertexCount - 1][X];
708
+ float py = vertices[vertexCount - 1][Y];
709
+ ctrlX = px + (px - ppx);
710
+ ctrlY = py + (py - ppy);
711
+ }
712
+ float ctrlX2 = cx + PApplet.parseFloat(pathTokens[i + 1]);
713
+ float ctrlY2 = cy + PApplet.parseFloat(pathTokens[i + 2]);
714
+ float endX = cx + PApplet.parseFloat(pathTokens[i + 3]);
715
+ float endY = cy + PApplet.parseFloat(pathTokens[i + 4]);
716
+ parsePathCurveto(ctrlX, ctrlY, ctrlX2, ctrlY2, endX, endY);
717
+ cx = endX;
718
+ cy = endY;
719
+ i += 5;
720
+ prevCurve = true;
721
+ }
722
+ break;
723
+
724
+ // Q - quadratic curve to (absolute)
725
+ // Draws a quadratic Bézier curve from the current point to (x,y) using
726
+ // (x1,y1) as the control point. Q (uppercase) indicates that absolute
727
+ // coordinates will follow; q (lowercase) indicates that relative
728
+ // coordinates will follow. Multiple sets of coordinates may be specified
729
+ // to draw a polybézier. At the end of the command, the new current point
730
+ // becomes the final (x,y) coordinate pair used in the polybézier.
731
+ case 'Q': {
732
+ ctrlX = PApplet.parseFloat(pathTokens[i + 1]);
733
+ ctrlY = PApplet.parseFloat(pathTokens[i + 2]);
734
+ float endX = PApplet.parseFloat(pathTokens[i + 3]);
735
+ float endY = PApplet.parseFloat(pathTokens[i + 4]);
736
+ //parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY);
737
+ parsePathQuadto(ctrlX, ctrlY, endX, endY);
738
+ cx = endX;
739
+ cy = endY;
740
+ i += 5;
741
+ prevCurve = true;
742
+ }
743
+ break;
744
+
745
+ // q - quadratic curve to (relative)
746
+ case 'q': {
747
+ ctrlX = cx + PApplet.parseFloat(pathTokens[i + 1]);
748
+ ctrlY = cy + PApplet.parseFloat(pathTokens[i + 2]);
749
+ float endX = cx + PApplet.parseFloat(pathTokens[i + 3]);
750
+ float endY = cy + PApplet.parseFloat(pathTokens[i + 4]);
751
+ //parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY);
752
+ parsePathQuadto(ctrlX, ctrlY, endX, endY);
753
+ cx = endX;
754
+ cy = endY;
755
+ i += 5;
756
+ prevCurve = true;
757
+ }
758
+ break;
759
+
760
+ // T - quadratic curveto shorthand (absolute)
761
+ // The control point is assumed to be the reflection of the control
762
+ // point on the previous command relative to the current point.
763
+ case 'T': {
764
+ // If there is no previous command or if the previous command was
765
+ // not a Q, q, T or t, assume the control point is coincident
766
+ // with the current point.
767
+ if (!prevCurve) {
768
+ ctrlX = cx;
769
+ ctrlY = cy;
770
+ } else {
771
+ float ppx = vertices[vertexCount - 2][X];
772
+ float ppy = vertices[vertexCount - 2][Y];
773
+ float px = vertices[vertexCount - 1][X];
774
+ float py = vertices[vertexCount - 1][Y];
775
+ ctrlX = px + (px - ppx);
776
+ ctrlY = py + (py - ppy);
777
+ }
778
+ float endX = PApplet.parseFloat(pathTokens[i + 1]);
779
+ float endY = PApplet.parseFloat(pathTokens[i + 2]);
780
+ //parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY);
781
+ parsePathQuadto(ctrlX, ctrlY, endX, endY);
782
+ cx = endX;
783
+ cy = endY;
784
+ i += 3;
785
+ prevCurve = true;
786
+ }
787
+ break;
788
+
789
+ // t - quadratic curveto shorthand (relative)
790
+ case 't': {
791
+ if (!prevCurve) {
792
+ ctrlX = cx;
793
+ ctrlY = cy;
794
+ } else {
795
+ float ppx = vertices[vertexCount - 2][X];
796
+ float ppy = vertices[vertexCount - 2][Y];
797
+ float px = vertices[vertexCount - 1][X];
798
+ float py = vertices[vertexCount - 1][Y];
799
+ ctrlX = px + (px - ppx);
800
+ ctrlY = py + (py - ppy);
801
+ }
802
+ float endX = cx + PApplet.parseFloat(pathTokens[i + 1]);
803
+ float endY = cy + PApplet.parseFloat(pathTokens[i + 2]);
804
+ //parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY);
805
+ parsePathQuadto(ctrlX, ctrlY, endX, endY);
806
+ cx = endX;
807
+ cy = endY;
808
+ i += 3;
809
+ prevCurve = true;
810
+ }
811
+ break;
812
+
813
+ // A - elliptical arc to (absolute)
814
+ case 'A': {
815
+ float rx = PApplet.parseFloat(pathTokens[i + 1]);
816
+ float ry = PApplet.parseFloat(pathTokens[i + 2]);
817
+ float angle = PApplet.parseFloat(pathTokens[i + 3]);
818
+ boolean fa = PApplet.parseFloat(pathTokens[i + 4]) != 0;
819
+ boolean fs = PApplet.parseFloat(pathTokens[i + 5]) != 0;
820
+ float endX = PApplet.parseFloat(pathTokens[i + 6]);
821
+ float endY = PApplet.parseFloat(pathTokens[i + 7]);
822
+ parsePathArcto(cx, cy, rx, ry, angle, fa, fs, endX, endY);
823
+ cx = endX;
824
+ cy = endY;
825
+ i += 8;
826
+ prevCurve = true;
827
+ }
828
+ break;
829
+
830
+ // a - elliptical arc to (relative)
831
+ case 'a': {
832
+ float rx = PApplet.parseFloat(pathTokens[i + 1]);
833
+ float ry = PApplet.parseFloat(pathTokens[i + 2]);
834
+ float angle = PApplet.parseFloat(pathTokens[i + 3]);
835
+ boolean fa = PApplet.parseFloat(pathTokens[i + 4]) != 0;
836
+ boolean fs = PApplet.parseFloat(pathTokens[i + 5]) != 0;
837
+ float endX = cx + PApplet.parseFloat(pathTokens[i + 6]);
838
+ float endY = cy + PApplet.parseFloat(pathTokens[i + 7]);
839
+ parsePathArcto(cx, cy, rx, ry, angle, fa, fs, endX, endY);
840
+ cx = endX;
841
+ cy = endY;
842
+ i += 8;
843
+ prevCurve = true;
844
+ }
845
+ break;
846
+
847
+ case 'Z':
848
+ case 'z':
849
+ // since closing the path, the 'current' point needs
850
+ // to return back to the last moveto location.
851
+ // http://code.google.com/p/processing/issues/detail?id=1058
852
+ cx = movetoX;
853
+ cy = movetoY;
854
+ close = true;
855
+ i++;
856
+ break;
857
+
858
+ default:
859
+ String parsed
860
+ = PApplet.join(PApplet.subset(pathTokens, 0, i), ",");
861
+ String unparsed
862
+ = PApplet.join(PApplet.subset(pathTokens, i), ",");
863
+ System.err.println("parsed: " + parsed);
864
+ System.err.println("unparsed: " + unparsed);
865
+ throw new RuntimeException("shape command not handled: " + pathTokens[i]);
866
+ }
859
867
  // prevCommand = c;
868
+ }
860
869
  }
861
- }
862
-
863
870
 
864
871
  // private void parsePathCheck(int num) {
865
872
  // if (vertexCount + num-1 >= vertices.length) {
@@ -869,51 +876,46 @@ public class PShapeSVG extends PShape {
869
876
  // vertices = temp;
870
877
  // }
871
878
  // }
872
-
873
- private void parsePathVertex(float x, float y) {
874
- if (vertexCount == vertices.length) {
875
- //vertices = (float[][]) PApplet.expand(vertices);
876
- float[][] temp = new float[vertexCount << 1][2];
877
- System.arraycopy(vertices, 0, temp, 0, vertexCount);
878
- vertices = temp;
879
+ private void parsePathVertex(float x, float y) {
880
+ if (vertexCount == vertices.length) {
881
+ //vertices = (float[][]) PApplet.expand(vertices);
882
+ float[][] temp = new float[vertexCount << 1][2];
883
+ System.arraycopy(vertices, 0, temp, 0, vertexCount);
884
+ vertices = temp;
885
+ }
886
+ vertices[vertexCount][X] = x;
887
+ vertices[vertexCount][Y] = y;
888
+ vertexCount++;
879
889
  }
880
- vertices[vertexCount][X] = x;
881
- vertices[vertexCount][Y] = y;
882
- vertexCount++;
883
- }
884
-
885
890
 
886
- private void parsePathCode(int what) {
887
- if (vertexCodeCount == vertexCodes.length) {
888
- vertexCodes = PApplet.expand(vertexCodes);
891
+ private void parsePathCode(int what) {
892
+ if (vertexCodeCount == vertexCodes.length) {
893
+ vertexCodes = PApplet.expand(vertexCodes);
894
+ }
895
+ vertexCodes[vertexCodeCount++] = what;
889
896
  }
890
- vertexCodes[vertexCodeCount++] = what;
891
- }
892
897
 
893
-
894
- private void parsePathMoveto(float px, float py) {
895
- if (vertexCount > 0) {
896
- parsePathCode(BREAK);
898
+ private void parsePathMoveto(float px, float py) {
899
+ if (vertexCount > 0) {
900
+ parsePathCode(BREAK);
901
+ }
902
+ parsePathCode(VERTEX);
903
+ parsePathVertex(px, py);
897
904
  }
898
- parsePathCode(VERTEX);
899
- parsePathVertex(px, py);
900
- }
901
-
902
-
903
- private void parsePathLineto(float px, float py) {
904
- parsePathCode(VERTEX);
905
- parsePathVertex(px, py);
906
- }
907
905
 
906
+ private void parsePathLineto(float px, float py) {
907
+ parsePathCode(VERTEX);
908
+ parsePathVertex(px, py);
909
+ }
908
910
 
909
- private void parsePathCurveto(float x1, float y1,
910
- float x2, float y2,
911
- float x3, float y3) {
912
- parsePathCode(BEZIER_VERTEX);
913
- parsePathVertex(x1, y1);
914
- parsePathVertex(x2, y2);
915
- parsePathVertex(x3, y3);
916
- }
911
+ private void parsePathCurveto(float x1, float y1,
912
+ float x2, float y2,
913
+ float x3, float y3) {
914
+ parsePathCode(BEZIER_VERTEX);
915
+ parsePathVertex(x1, y1);
916
+ parsePathVertex(x2, y2);
917
+ parsePathVertex(x3, y3);
918
+ }
917
919
 
918
920
  // private void parsePathQuadto(float x1, float y1,
919
921
  // float cx, float cy,
@@ -927,415 +929,419 @@ public class PShapeSVG extends PShape {
927
929
  // parsePathVertex(x2 + ((cx-x2)*2/3.0f), y2 + ((cy-y2)*2/3.0f));
928
930
  // parsePathVertex(x2, y2);
929
931
  // }
930
-
931
- private void parsePathQuadto(float cx, float cy,
932
- float x2, float y2) {
933
- //System.out.println("quadto: " + x1 + "," + y1 + " " + cx + "," + cy + " " + x2 + "," + y2);
932
+ private void parsePathQuadto(float cx, float cy,
933
+ float x2, float y2) {
934
+ //System.out.println("quadto: " + x1 + "," + y1 + " " + cx + "," + cy + " " + x2 + "," + y2);
934
935
  // parsePathCode(BEZIER_VERTEX);
935
- parsePathCode(QUADRATIC_VERTEX);
936
- // x1/y1 already covered by last moveto, lineto, or curveto
937
- parsePathVertex(cx, cy);
938
- parsePathVertex(x2, y2);
939
- }
940
-
941
-
942
- // Approximates elliptical arc by several bezier segments.
943
- // Meets SVG standard requirements from:
944
- // http://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands
945
- // http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
946
- // Based on arc to bezier curve equations from:
947
- // http://www.spaceroots.org/documents/ellipse/node22.html
948
- private void parsePathArcto(float x1, float y1,
949
- float rx, float ry,
950
- float angle,
951
- boolean fa, boolean fs,
952
- float x2, float y2) {
953
- if (x1 == x2 && y1 == y2) return;
954
- if (rx == 0 || ry == 0) { parsePathLineto(x2, y2); return; }
955
-
956
- rx = PApplet.abs(rx); ry = PApplet.abs(ry);
957
-
958
- float phi = PApplet.radians(((angle % 360) + 360) % 360);
959
- float cosPhi = PApplet.cos(phi), sinPhi = PApplet.sin(phi);
960
-
961
- float x1r = ( cosPhi * (x1 - x2) + sinPhi * (y1 - y2)) / 2;
962
- float y1r = (-sinPhi * (x1 - x2) + cosPhi * (y1 - y2)) / 2;
963
-
964
- float cxr, cyr;
965
- {
966
- float A = (x1r*x1r) / (rx*rx) + (y1r*y1r) / (ry*ry);
967
- if (A > 1) {
968
- // No solution, scale ellipse up according to SVG standard
969
- float sqrtA = PApplet.sqrt(A);
970
- rx *= sqrtA; cxr = 0;
971
- ry *= sqrtA; cyr = 0;
972
- } else {
973
- float k = ((fa == fs) ? -1f : 1f) *
974
- PApplet.sqrt((rx*rx * ry*ry) / ((rx*rx * y1r*y1r) + (ry*ry * x1r*x1r)) - 1f);
975
- cxr = k * rx * y1r / ry;
976
- cyr = -k * ry * x1r / rx;
977
- }
936
+ parsePathCode(QUADRATIC_VERTEX);
937
+ // x1/y1 already covered by last moveto, lineto, or curveto
938
+ parsePathVertex(cx, cy);
939
+ parsePathVertex(x2, y2);
978
940
  }
979
941
 
980
- float cx = cosPhi * cxr - sinPhi * cyr + (x1 + x2) / 2;
981
- float cy = sinPhi * cxr + cosPhi * cyr + (y1 + y2) / 2;
982
-
983
- float phi1, phiDelta;
984
- {
985
- float sx = ( x1r - cxr) / rx, sy = ( y1r - cyr) / ry;
986
- float tx = (-x1r - cxr) / rx, ty = (-y1r - cyr) / ry;
987
- phi1 = PApplet.atan2(sy, sx);
988
- phiDelta = (((PApplet.atan2(ty, tx) - phi1) % TWO_PI) + TWO_PI) % TWO_PI;
989
- if (!fs) phiDelta -= TWO_PI;
990
- }
942
+ // Approximates elliptical arc by several bezier segments.
943
+ // Meets SVG standard requirements from:
944
+ // http://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands
945
+ // http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
946
+ // Based on arc to bezier curve equations from:
947
+ // http://www.spaceroots.org/documents/ellipse/node22.html
948
+ private void parsePathArcto(float x1, float y1,
949
+ float rx, float ry,
950
+ float angle,
951
+ boolean fa, boolean fs,
952
+ float x2, float y2) {
953
+ if (x1 == x2 && y1 == y2) {
954
+ return;
955
+ }
956
+ if (rx == 0 || ry == 0) {
957
+ parsePathLineto(x2, y2);
958
+ return;
959
+ }
991
960
 
992
- // One segment can not cover more that PI, less than PI/2 is
993
- // recommended to avoid visible inaccuracies caused by rounding errors
994
- int segmentCount = PApplet.ceil(PApplet.abs(phiDelta) / TWO_PI * 4);
961
+ rx = PApplet.abs(rx);
962
+ ry = PApplet.abs(ry);
963
+
964
+ float phi = PApplet.radians(((angle % 360) + 360) % 360);
965
+ float cosPhi = PApplet.cos(phi), sinPhi = PApplet.sin(phi);
966
+
967
+ float x1r = (cosPhi * (x1 - x2) + sinPhi * (y1 - y2)) / 2;
968
+ float y1r = (-sinPhi * (x1 - x2) + cosPhi * (y1 - y2)) / 2;
969
+
970
+ float cxr, cyr;
971
+ {
972
+ float A = (x1r * x1r) / (rx * rx) + (y1r * y1r) / (ry * ry);
973
+ if (A > 1) {
974
+ // No solution, scale ellipse up according to SVG standard
975
+ float sqrtA = PApplet.sqrt(A);
976
+ rx *= sqrtA;
977
+ cxr = 0;
978
+ ry *= sqrtA;
979
+ cyr = 0;
980
+ } else {
981
+ float k = ((fa == fs) ? -1f : 1f)
982
+ * PApplet.sqrt((rx * rx * ry * ry) / ((rx * rx * y1r * y1r) + (ry * ry * x1r * x1r)) - 1f);
983
+ cxr = k * rx * y1r / ry;
984
+ cyr = -k * ry * x1r / rx;
985
+ }
986
+ }
995
987
 
996
- float inc = phiDelta / segmentCount;
997
- float a = PApplet.sin(inc) *
998
- (PApplet.sqrt(4 + 3 * PApplet.sq(PApplet.tan(inc / 2))) - 1) / 3;
988
+ float cx = cosPhi * cxr - sinPhi * cyr + (x1 + x2) / 2;
989
+ float cy = sinPhi * cxr + cosPhi * cyr + (y1 + y2) / 2;
990
+
991
+ float phi1, phiDelta;
992
+ {
993
+ float sx = (x1r - cxr) / rx, sy = (y1r - cyr) / ry;
994
+ float tx = (-x1r - cxr) / rx, ty = (-y1r - cyr) / ry;
995
+ phi1 = PApplet.atan2(sy, sx);
996
+ phiDelta = (((PApplet.atan2(ty, tx) - phi1) % TWO_PI) + TWO_PI) % TWO_PI;
997
+ if (!fs) {
998
+ phiDelta -= TWO_PI;
999
+ }
1000
+ }
999
1001
 
1000
- float sinPhi1 = PApplet.sin(phi1), cosPhi1 = PApplet.cos(phi1);
1002
+ // One segment can not cover more that PI, less than PI/2 is
1003
+ // recommended to avoid visible inaccuracies caused by rounding errors
1004
+ int segmentCount = PApplet.ceil(PApplet.abs(phiDelta) / TWO_PI * 4);
1001
1005
 
1002
- float p1x = x1;
1003
- float p1y = y1;
1004
- float relq1x = a * (-rx * cosPhi * sinPhi1 - ry * sinPhi * cosPhi1);
1005
- float relq1y = a * (-rx * sinPhi * sinPhi1 + ry * cosPhi * cosPhi1);
1006
+ float inc = phiDelta / segmentCount;
1007
+ float a = PApplet.sin(inc)
1008
+ * (PApplet.sqrt(4 + 3 * PApplet.sq(PApplet.tan(inc / 2))) - 1) / 3;
1006
1009
 
1007
- for (int i = 0; i < segmentCount; i++) {
1008
- float eta = phi1 + (i + 1) * inc;
1009
- float sinEta = PApplet.sin(eta), cosEta = PApplet.cos(eta);
1010
+ float sinPhi1 = PApplet.sin(phi1), cosPhi1 = PApplet.cos(phi1);
1010
1011
 
1011
- float p2x = cx + rx * cosPhi * cosEta - ry * sinPhi * sinEta;
1012
- float p2y = cy + rx * sinPhi * cosEta + ry * cosPhi * sinEta;
1013
- float relq2x = a * (-rx * cosPhi * sinEta - ry * sinPhi * cosEta);
1014
- float relq2y = a * (-rx * sinPhi * sinEta + ry * cosPhi * cosEta);
1012
+ float p1x = x1;
1013
+ float p1y = y1;
1014
+ float relq1x = a * (-rx * cosPhi * sinPhi1 - ry * sinPhi * cosPhi1);
1015
+ float relq1y = a * (-rx * sinPhi * sinPhi1 + ry * cosPhi * cosPhi1);
1015
1016
 
1016
- if (i == segmentCount - 1) { p2x = x2; p2y = y2; }
1017
+ for (int i = 0; i < segmentCount; i++) {
1018
+ float eta = phi1 + (i + 1) * inc;
1019
+ float sinEta = PApplet.sin(eta), cosEta = PApplet.cos(eta);
1017
1020
 
1018
- parsePathCode(BEZIER_VERTEX);
1019
- parsePathVertex(p1x + relq1x, p1y + relq1y);
1020
- parsePathVertex(p2x - relq2x, p2y - relq2y);
1021
- parsePathVertex(p2x, p2y);
1021
+ float p2x = cx + rx * cosPhi * cosEta - ry * sinPhi * sinEta;
1022
+ float p2y = cy + rx * sinPhi * cosEta + ry * cosPhi * sinEta;
1023
+ float relq2x = a * (-rx * cosPhi * sinEta - ry * sinPhi * cosEta);
1024
+ float relq2y = a * (-rx * sinPhi * sinEta + ry * cosPhi * cosEta);
1022
1025
 
1023
- p1x = p2x; relq1x = relq2x;
1024
- p1y = p2y; relq1y = relq2y;
1025
- }
1026
- }
1027
-
1028
-
1029
- /**
1030
- * Parse the specified SVG matrix into a PMatrix2D. Note that PMatrix2D
1031
- * is rotated relative to the SVG definition, so parameters are rearranged
1032
- * here. More about the transformation matrices in
1033
- * <a href="http://www.w3.org/TR/SVG/coords.html#TransformAttribute">this section</a>
1034
- * of the SVG documentation.
1035
- * @param matrixStr text of the matrix param.
1036
- * @return a good old-fashioned PMatrix2D
1037
- */
1038
- static protected PMatrix2D parseTransform(String matrixStr) {
1039
- matrixStr = matrixStr.trim();
1040
- PMatrix2D outgoing = null;
1041
- int start = 0;
1042
- int stop = -1;
1043
- while ((stop = matrixStr.indexOf(')', start)) != -1) {
1044
- PMatrix2D m = parseSingleTransform(matrixStr.substring(start, stop+1));
1045
- if (outgoing == null) {
1046
- outgoing = m;
1047
- } else {
1048
- outgoing.apply(m);
1049
- }
1050
- start = stop + 1;
1051
- }
1052
- return outgoing;
1053
- }
1026
+ if (i == segmentCount - 1) {
1027
+ p2x = x2;
1028
+ p2y = y2;
1029
+ }
1054
1030
 
1031
+ parsePathCode(BEZIER_VERTEX);
1032
+ parsePathVertex(p1x + relq1x, p1y + relq1y);
1033
+ parsePathVertex(p2x - relq2x, p2y - relq2y);
1034
+ parsePathVertex(p2x, p2y);
1055
1035
 
1056
- static protected PMatrix2D parseSingleTransform(String matrixStr) {
1057
- //String[] pieces = PApplet.match(matrixStr, "^\\s*(\\w+)\\((.*)\\)\\s*$");
1058
- String[] pieces = PApplet.match(matrixStr, "[,\\s]*(\\w+)\\((.*)\\)");
1059
- if (pieces == null) {
1060
- System.err.println("Could not parse transform " + matrixStr);
1061
- return null;
1062
- }
1063
- float[] m = PApplet.parseFloat(PApplet.splitTokens(pieces[2], ", "));
1064
- if (pieces[1].equals("matrix")) {
1065
- return new PMatrix2D(m[0], m[2], m[4], m[1], m[3], m[5]);
1066
-
1067
- } else if (pieces[1].equals("translate")) {
1068
- float tx = m[0];
1069
- float ty = (m.length == 2) ? m[1] : m[0];
1070
- return new PMatrix2D(1, 0, tx, 0, 1, ty);
1071
-
1072
- } else if (pieces[1].equals("scale")) {
1073
- float sx = m[0];
1074
- float sy = (m.length == 2) ? m[1] : m[0];
1075
- return new PMatrix2D(sx, 0, 0, 0, sy, 0);
1076
-
1077
- } else if (pieces[1].equals("rotate")) {
1078
- float angle = m[0];
1079
-
1080
- if (m.length == 1) {
1081
- float c = PApplet.cos(angle);
1082
- float s = PApplet.sin(angle);
1083
- // SVG version is cos(a) sin(a) -sin(a) cos(a) 0 0
1084
- return new PMatrix2D(c, -s, 0, s, c, 0);
1085
-
1086
- } else if (m.length == 3) {
1087
- PMatrix2D mat = new PMatrix2D(0, 1, m[1], 1, 0, m[2]);
1088
- mat.rotate(m[0]);
1089
- mat.translate(-m[1], -m[2]);
1090
- return mat;
1091
- }
1092
-
1093
- } else if (pieces[1].equals("skewX")) {
1094
- return new PMatrix2D(1, 0, 1, PApplet.tan(m[0]), 0, 0);
1095
-
1096
- } else if (pieces[1].equals("skewY")) {
1097
- return new PMatrix2D(1, 0, 1, 0, PApplet.tan(m[0]), 0);
1036
+ p1x = p2x;
1037
+ relq1x = relq2x;
1038
+ p1y = p2y;
1039
+ relq1y = relq2y;
1040
+ }
1098
1041
  }
1099
- return null;
1100
- }
1101
1042
 
1102
-
1103
- protected void parseColors(XML properties) {
1104
- if (properties.hasAttribute("opacity")) {
1105
- String opacityText = properties.getString("opacity");
1106
- setOpacity(opacityText);
1043
+ /**
1044
+ * Parse the specified SVG matrix into a PMatrix2D. Note that PMatrix2D is
1045
+ * rotated relative to the SVG definition, so parameters are rearranged
1046
+ * here. More about the transformation matrices in
1047
+ * <a href="http://www.w3.org/TR/SVG/coords.html#TransformAttribute">this
1048
+ * section</a>
1049
+ * of the SVG documentation.
1050
+ *
1051
+ * @param matrixStr text of the matrix param.
1052
+ * @return a good old-fashioned PMatrix2D
1053
+ */
1054
+ static protected PMatrix2D parseTransform(String matrixStr) {
1055
+ matrixStr = matrixStr.trim();
1056
+ PMatrix2D outgoing = null;
1057
+ int start = 0;
1058
+ int stop = -1;
1059
+ while ((stop = matrixStr.indexOf(')', start)) != -1) {
1060
+ PMatrix2D m = parseSingleTransform(matrixStr.substring(start, stop + 1));
1061
+ if (outgoing == null) {
1062
+ outgoing = m;
1063
+ } else {
1064
+ outgoing.apply(m);
1065
+ }
1066
+ start = stop + 1;
1067
+ }
1068
+ return outgoing;
1107
1069
  }
1108
1070
 
1109
- if (properties.hasAttribute("stroke")) {
1110
- String strokeText = properties.getString("stroke");
1111
- setColor(strokeText, false);
1112
- }
1071
+ static protected PMatrix2D parseSingleTransform(String matrixStr) {
1072
+ //String[] pieces = PApplet.match(matrixStr, "^\\s*(\\w+)\\((.*)\\)\\s*$");
1073
+ String[] pieces = PApplet.match(matrixStr, "[,\\s]*(\\w+)\\((.*)\\)");
1074
+ if (pieces == null) {
1075
+ System.err.println("Could not parse transform " + matrixStr);
1076
+ return null;
1077
+ }
1078
+ float[] m = PApplet.parseFloat(PApplet.splitTokens(pieces[2], ", "));
1079
+ if (pieces[1].equals("matrix")) {
1080
+ return new PMatrix2D(m[0], m[2], m[4], m[1], m[3], m[5]);
1081
+
1082
+ } else if (pieces[1].equals("translate")) {
1083
+ float tx = m[0];
1084
+ float ty = (m.length == 2) ? m[1] : m[0];
1085
+ return new PMatrix2D(1, 0, tx, 0, 1, ty);
1086
+
1087
+ } else if (pieces[1].equals("scale")) {
1088
+ float sx = m[0];
1089
+ float sy = (m.length == 2) ? m[1] : m[0];
1090
+ return new PMatrix2D(sx, 0, 0, 0, sy, 0);
1091
+
1092
+ } else if (pieces[1].equals("rotate")) {
1093
+ float angle = m[0];
1094
+
1095
+ if (m.length == 1) {
1096
+ float c = PApplet.cos(angle);
1097
+ float s = PApplet.sin(angle);
1098
+ // SVG version is cos(a) sin(a) -sin(a) cos(a) 0 0
1099
+ return new PMatrix2D(c, -s, 0, s, c, 0);
1100
+
1101
+ } else if (m.length == 3) {
1102
+ PMatrix2D mat = new PMatrix2D(0, 1, m[1], 1, 0, m[2]);
1103
+ mat.rotate(m[0]);
1104
+ mat.translate(-m[1], -m[2]);
1105
+ return mat;
1106
+ }
1113
1107
 
1114
- if (properties.hasAttribute("stroke-opacity")) {
1115
- String strokeOpacityText = properties.getString("stroke-opacity");
1116
- setStrokeOpacity(strokeOpacityText);
1117
- }
1108
+ } else if (pieces[1].equals("skewX")) {
1109
+ return new PMatrix2D(1, 0, 1, PApplet.tan(m[0]), 0, 0);
1118
1110
 
1119
- if (properties.hasAttribute("stroke-width")) {
1120
- // if NaN (i.e. if it's 'inherit') then default back to the inherit setting
1121
- String lineweight = properties.getString("stroke-width");
1122
- setStrokeWeight(lineweight);
1111
+ } else if (pieces[1].equals("skewY")) {
1112
+ return new PMatrix2D(1, 0, 1, 0, PApplet.tan(m[0]), 0);
1113
+ }
1114
+ return null;
1123
1115
  }
1124
1116
 
1125
- if (properties.hasAttribute("stroke-linejoin")) {
1126
- String linejoin = properties.getString("stroke-linejoin");
1127
- setStrokeJoin(linejoin);
1128
- }
1117
+ protected void parseColors(XML properties) {
1118
+ if (properties.hasAttribute("opacity")) {
1119
+ String opacityText = properties.getString("opacity");
1120
+ setOpacity(opacityText);
1121
+ }
1129
1122
 
1130
- if (properties.hasAttribute("stroke-linecap")) {
1131
- String linecap = properties.getString("stroke-linecap");
1132
- setStrokeCap(linecap);
1133
- }
1123
+ if (properties.hasAttribute("stroke")) {
1124
+ String strokeText = properties.getString("stroke");
1125
+ setColor(strokeText, false);
1126
+ }
1134
1127
 
1135
- // fill defaults to black (though stroke defaults to "none")
1136
- // http://www.w3.org/TR/SVG/painting.html#FillProperties
1137
- if (properties.hasAttribute("fill")) {
1138
- String fillText = properties.getString("fill");
1139
- setColor(fillText, true);
1140
- }
1128
+ if (properties.hasAttribute("stroke-opacity")) {
1129
+ String strokeOpacityText = properties.getString("stroke-opacity");
1130
+ setStrokeOpacity(strokeOpacityText);
1131
+ }
1141
1132
 
1142
- if (properties.hasAttribute("fill-opacity")) {
1143
- String fillOpacityText = properties.getString("fill-opacity");
1144
- setFillOpacity(fillOpacityText);
1145
- }
1133
+ if (properties.hasAttribute("stroke-width")) {
1134
+ // if NaN (i.e. if it's 'inherit') then default back to the inherit setting
1135
+ String lineweight = properties.getString("stroke-width");
1136
+ setStrokeWeight(lineweight);
1137
+ }
1146
1138
 
1147
- if (properties.hasAttribute("style")) {
1148
- String styleText = properties.getString("style");
1149
- String[] styleTokens = PApplet.splitTokens(styleText, ";");
1139
+ if (properties.hasAttribute("stroke-linejoin")) {
1140
+ String linejoin = properties.getString("stroke-linejoin");
1141
+ setStrokeJoin(linejoin);
1142
+ }
1150
1143
 
1151
- //PApplet.println(styleTokens);
1152
- for (int i = 0; i < styleTokens.length; i++) {
1153
- String[] tokens = PApplet.splitTokens(styleTokens[i], ":");
1154
- //PApplet.println(tokens);
1144
+ if (properties.hasAttribute("stroke-linecap")) {
1145
+ String linecap = properties.getString("stroke-linecap");
1146
+ setStrokeCap(linecap);
1147
+ }
1155
1148
 
1156
- tokens[0] = PApplet.trim(tokens[0]);
1149
+ // fill defaults to black (though stroke defaults to "none")
1150
+ // http://www.w3.org/TR/SVG/painting.html#FillProperties
1151
+ if (properties.hasAttribute("fill")) {
1152
+ String fillText = properties.getString("fill");
1153
+ setColor(fillText, true);
1154
+ }
1157
1155
 
1158
- if (tokens[0].equals("fill")) {
1159
- setColor(tokens[1], true);
1156
+ if (properties.hasAttribute("fill-opacity")) {
1157
+ String fillOpacityText = properties.getString("fill-opacity");
1158
+ setFillOpacity(fillOpacityText);
1159
+ }
1160
1160
 
1161
- } else if(tokens[0].equals("fill-opacity")) {
1162
- setFillOpacity(tokens[1]);
1161
+ if (properties.hasAttribute("style")) {
1162
+ String styleText = properties.getString("style");
1163
+ String[] styleTokens = PApplet.splitTokens(styleText, ";");
1163
1164
 
1164
- } else if(tokens[0].equals("stroke")) {
1165
- setColor(tokens[1], false);
1165
+ //PApplet.println(styleTokens);
1166
+ for (int i = 0; i < styleTokens.length; i++) {
1167
+ String[] tokens = PApplet.splitTokens(styleTokens[i], ":");
1168
+ //PApplet.println(tokens);
1166
1169
 
1167
- } else if(tokens[0].equals("stroke-width")) {
1168
- setStrokeWeight(tokens[1]);
1170
+ tokens[0] = PApplet.trim(tokens[0]);
1169
1171
 
1170
- } else if(tokens[0].equals("stroke-linecap")) {
1171
- setStrokeCap(tokens[1]);
1172
+ if (tokens[0].equals("fill")) {
1173
+ setColor(tokens[1], true);
1172
1174
 
1173
- } else if(tokens[0].equals("stroke-linejoin")) {
1174
- setStrokeJoin(tokens[1]);
1175
+ } else if (tokens[0].equals("fill-opacity")) {
1176
+ setFillOpacity(tokens[1]);
1175
1177
 
1176
- } else if(tokens[0].equals("stroke-opacity")) {
1177
- setStrokeOpacity(tokens[1]);
1178
+ } else if (tokens[0].equals("stroke")) {
1179
+ setColor(tokens[1], false);
1178
1180
 
1179
- } else if(tokens[0].equals("opacity")) {
1180
- setOpacity(tokens[1]);
1181
+ } else if (tokens[0].equals("stroke-width")) {
1182
+ setStrokeWeight(tokens[1]);
1181
1183
 
1182
- } else {
1183
- // Other attributes are not yet implemented
1184
- }
1185
- }
1186
- }
1187
- }
1184
+ } else if (tokens[0].equals("stroke-linecap")) {
1185
+ setStrokeCap(tokens[1]);
1188
1186
 
1187
+ } else if (tokens[0].equals("stroke-linejoin")) {
1188
+ setStrokeJoin(tokens[1]);
1189
1189
 
1190
- void setOpacity(String opacityText) {
1191
- opacity = PApplet.parseFloat(opacityText);
1192
- strokeColor = ((int) (opacity * 255)) << 24 | strokeColor & 0xFFFFFF;
1193
- fillColor = ((int) (opacity * 255)) << 24 | fillColor & 0xFFFFFF;
1194
- }
1190
+ } else if (tokens[0].equals("stroke-opacity")) {
1191
+ setStrokeOpacity(tokens[1]);
1195
1192
 
1193
+ } else if (tokens[0].equals("opacity")) {
1194
+ setOpacity(tokens[1]);
1196
1195
 
1197
- void setStrokeWeight(String lineweight) {
1198
- strokeWeight = parseUnitSize(lineweight, svgSizeXY);
1199
- }
1196
+ } else {
1197
+ // Other attributes are not yet implemented
1198
+ }
1199
+ }
1200
+ }
1201
+ }
1200
1202
 
1203
+ void setOpacity(String opacityText) {
1204
+ opacity = PApplet.parseFloat(opacityText);
1205
+ strokeColor = ((int) (opacity * 255)) << 24 | strokeColor & 0xFFFFFF;
1206
+ fillColor = ((int) (opacity * 255)) << 24 | fillColor & 0xFFFFFF;
1207
+ }
1201
1208
 
1202
- void setStrokeOpacity(String opacityText) {
1203
- strokeOpacity = PApplet.parseFloat(opacityText);
1204
- strokeColor = ((int) (strokeOpacity * 255)) << 24 | strokeColor & 0xFFFFFF;
1205
- }
1209
+ void setStrokeWeight(String lineweight) {
1210
+ strokeWeight = parseUnitSize(lineweight, svgSizeXY);
1211
+ }
1206
1212
 
1213
+ void setStrokeOpacity(String opacityText) {
1214
+ strokeOpacity = PApplet.parseFloat(opacityText);
1215
+ strokeColor = ((int) (strokeOpacity * 255)) << 24 | strokeColor & 0xFFFFFF;
1216
+ }
1207
1217
 
1208
- void setStrokeJoin(String linejoin) {
1209
- if (linejoin.equals("inherit")) {
1210
- // do nothing, will inherit automatically
1218
+ void setStrokeJoin(String linejoin) {
1219
+ if (linejoin.equals("inherit")) {
1220
+ // do nothing, will inherit automatically
1211
1221
 
1212
- } else if (linejoin.equals("miter")) {
1213
- strokeJoin = PConstants.MITER;
1222
+ } else if (linejoin.equals("miter")) {
1223
+ strokeJoin = PConstants.MITER;
1214
1224
 
1215
- } else if (linejoin.equals("round")) {
1216
- strokeJoin = PConstants.ROUND;
1225
+ } else if (linejoin.equals("round")) {
1226
+ strokeJoin = PConstants.ROUND;
1217
1227
 
1218
- } else if (linejoin.equals("bevel")) {
1219
- strokeJoin = PConstants.BEVEL;
1228
+ } else if (linejoin.equals("bevel")) {
1229
+ strokeJoin = PConstants.BEVEL;
1230
+ }
1220
1231
  }
1221
- }
1222
-
1223
1232
 
1224
- void setStrokeCap(String linecap) {
1225
- if (linecap.equals("inherit")) {
1226
- // do nothing, will inherit automatically
1233
+ void setStrokeCap(String linecap) {
1234
+ if (linecap.equals("inherit")) {
1235
+ // do nothing, will inherit automatically
1227
1236
 
1228
- } else if (linecap.equals("butt")) {
1229
- strokeCap = PConstants.SQUARE;
1237
+ } else if (linecap.equals("butt")) {
1238
+ strokeCap = PConstants.SQUARE;
1230
1239
 
1231
- } else if (linecap.equals("round")) {
1232
- strokeCap = PConstants.ROUND;
1240
+ } else if (linecap.equals("round")) {
1241
+ strokeCap = PConstants.ROUND;
1233
1242
 
1234
- } else if (linecap.equals("square")) {
1235
- strokeCap = PConstants.PROJECT;
1243
+ } else if (linecap.equals("square")) {
1244
+ strokeCap = PConstants.PROJECT;
1245
+ }
1236
1246
  }
1237
- }
1238
-
1239
-
1240
- void setFillOpacity(String opacityText) {
1241
- fillOpacity = PApplet.parseFloat(opacityText);
1242
- fillColor = ((int) (fillOpacity * 255)) << 24 | fillColor & 0xFFFFFF;
1243
- }
1244
1247
 
1248
+ void setFillOpacity(String opacityText) {
1249
+ fillOpacity = PApplet.parseFloat(opacityText);
1250
+ fillColor = ((int) (fillOpacity * 255)) << 24 | fillColor & 0xFFFFFF;
1251
+ }
1245
1252
 
1246
- void setColor(String colorText, boolean isFill) {
1247
- colorText = colorText.trim();
1248
- int opacityMask = fillColor & 0xFF000000;
1249
- boolean visible = true;
1250
- int color = 0;
1251
- String name = "";
1253
+ void setColor(String colorText, boolean isFill) {
1254
+ colorText = colorText.trim();
1255
+ int opacityMask = fillColor & 0xFF000000;
1256
+ boolean visible = true;
1257
+ int color = 0;
1258
+ String name = "";
1252
1259
  // String lColorText = colorText.toLowerCase();
1253
- Gradient gradient = null;
1260
+ Gradient gradient = null;
1254
1261
  // Object paint = null;
1255
- if (colorText.equals("none")) {
1256
- visible = false;
1257
- } else if (colorText.startsWith("url(#")) {
1258
- name = colorText.substring(5, colorText.length() - 1);
1259
- Object object = findChild(name);
1260
- if (object instanceof Gradient) {
1261
- gradient = (Gradient) object;
1262
- // in 3.0a11, do this on first draw inside PShapeJava2D
1262
+ if (colorText.equals("none")) {
1263
+ visible = false;
1264
+ } else if (colorText.startsWith("url(#")) {
1265
+ name = colorText.substring(5, colorText.length() - 1);
1266
+ Object object = findChild(name);
1267
+ if (object instanceof Gradient) {
1268
+ gradient = (Gradient) object;
1269
+ // in 3.0a11, do this on first draw inside PShapeJava2D
1263
1270
  // paint = calcGradientPaint(gradient); //, opacity);
1264
- } else {
1271
+ } else {
1265
1272
  // visible = false;
1266
- System.err.println("url " + name + " refers to unexpected data: " + object);
1267
- }
1268
- } else {
1269
- // Prints errors itself.
1270
- color = opacityMask | parseSimpleColor(colorText);
1271
- }
1272
- if (isFill) {
1273
- fill = visible;
1274
- fillColor = color;
1275
- fillName = name;
1276
- fillGradient = gradient;
1273
+ System.err.println("url " + name + " refers to unexpected data: " + object);
1274
+ }
1275
+ } else {
1276
+ // Prints errors itself.
1277
+ color = opacityMask | parseSimpleColor(colorText);
1278
+ }
1279
+ if (isFill) {
1280
+ fill = visible;
1281
+ fillColor = color;
1282
+ fillName = name;
1283
+ fillGradient = gradient;
1277
1284
  // fillGradientPaint = paint;
1278
- } else {
1279
- stroke = visible;
1280
- strokeColor = color;
1281
- strokeName = name;
1282
- strokeGradient = gradient;
1285
+ } else {
1286
+ stroke = visible;
1287
+ strokeColor = color;
1288
+ strokeName = name;
1289
+ strokeGradient = gradient;
1283
1290
  // strokeGradientPaint = paint;
1291
+ }
1284
1292
  }
1285
- }
1286
1293
 
1287
-
1288
- /**
1289
- * Parses the "color" datatype only, and prints an error if it is not of this form.
1290
- * http://www.w3.org/TR/SVG/types.html#DataTypeColor
1291
- * @return 0xRRGGBB (no alpha). Zero on error.
1292
- */
1293
- static protected int parseSimpleColor(String colorText) {
1294
- colorText = colorText.toLowerCase().trim();
1295
- //if (colorNames.containsKey(colorText)) {
1296
- if (colorNames.hasKey(colorText)) {
1297
- return colorNames.get(colorText);
1298
- } else if (colorText.startsWith("#")) {
1299
- if (colorText.length() == 4) {
1300
- // Short form: #ABC, transform to long form #AABBCC
1301
- colorText = colorText.replaceAll("^#(.)(.)(.)$", "#$1$1$2$2$3$3");
1302
- }
1303
- return (Integer.parseInt(colorText.substring(1), 16)) & 0xFFFFFF;
1304
- //System.out.println("hex for fill is " + PApplet.hex(fillColor));
1305
- } else if (colorText.startsWith("rgb")) {
1306
- return parseRGB(colorText);
1307
- } else {
1308
- System.err.println("Cannot parse \"" + colorText + "\".");
1309
- return 0;
1294
+ /**
1295
+ * Parses the "color" datatype only, and prints an error if it is not of
1296
+ * this form. http://www.w3.org/TR/SVG/types.html#DataTypeColor
1297
+ *
1298
+ * @return 0xRRGGBB (no alpha). Zero on error.
1299
+ */
1300
+ static protected int parseSimpleColor(String colorText) {
1301
+ colorText = colorText.toLowerCase().trim();
1302
+ //if (colorNames.containsKey(colorText)) {
1303
+ if (colorNames.hasKey(colorText)) {
1304
+ return colorNames.get(colorText);
1305
+ } else if (colorText.startsWith("#")) {
1306
+ if (colorText.length() == 4) {
1307
+ // Short form: #ABC, transform to long form #AABBCC
1308
+ colorText = colorText.replaceAll("^#(.)(.)(.)$", "#$1$1$2$2$3$3");
1309
+ }
1310
+ return (Integer.parseInt(colorText.substring(1), 16)) & 0xFFFFFF;
1311
+ //System.out.println("hex for fill is " + PApplet.hex(fillColor));
1312
+ } else if (colorText.startsWith("rgb")) {
1313
+ return parseRGB(colorText);
1314
+ } else {
1315
+ System.err.println("Cannot parse \"" + colorText + "\".");
1316
+ return 0;
1317
+ }
1310
1318
  }
1311
- }
1312
-
1313
1319
 
1314
- /**
1315
- * Deliberately conforms to the HTML 4.01 color spec + en-gb grey, rather
1316
- * than the (unlikely to be useful) entire 147-color system used in SVG.
1317
- */
1318
- static protected IntDict colorNames = new IntDict(new Object[][] {
1319
- { "aqua", 0x00ffff },
1320
- { "black", 0x000000 },
1321
- { "blue", 0x0000ff },
1322
- { "fuchsia", 0xff00ff },
1323
- { "gray", 0x808080 },
1324
- { "grey", 0x808080 },
1325
- { "green", 0x008000 },
1326
- { "lime", 0x00ff00 },
1327
- { "maroon", 0x800000 },
1328
- { "navy", 0x000080 },
1329
- { "olive", 0x808000 },
1330
- { "purple", 0x800080 },
1331
- { "red", 0xff0000 },
1332
- { "silver", 0xc0c0c0 },
1333
- { "teal", 0x008080 },
1334
- { "white", 0xffffff },
1335
- { "yellow", 0xffff00 }
1336
- });
1337
-
1338
- /*
1320
+ /**
1321
+ * Deliberately conforms to the HTML 4.01 color spec + en-gb grey, rather
1322
+ * than the (unlikely to be useful) entire 147-color system used in SVG.
1323
+ */
1324
+ static protected IntDict colorNames = new IntDict(new Object[][]{
1325
+ {"aqua", 0x00ffff},
1326
+ {"black", 0x000000},
1327
+ {"blue", 0x0000ff},
1328
+ {"fuchsia", 0xff00ff},
1329
+ {"gray", 0x808080},
1330
+ {"grey", 0x808080},
1331
+ {"green", 0x008000},
1332
+ {"lime", 0x00ff00},
1333
+ {"maroon", 0x800000},
1334
+ {"navy", 0x000080},
1335
+ {"olive", 0x808000},
1336
+ {"purple", 0x800080},
1337
+ {"red", 0xff0000},
1338
+ {"silver", 0xc0c0c0},
1339
+ {"teal", 0x008080},
1340
+ {"white", 0xffffff},
1341
+ {"yellow", 0xffff00}
1342
+ });
1343
+
1344
+ /*
1339
1345
  static protected Map<String, Integer> colorNames;
1340
1346
  static {
1341
1347
  colorNames = new HashMap<String, Integer>();
@@ -1357,446 +1363,431 @@ public class PShapeSVG extends PShape {
1357
1363
  colorNames.put("white", 0xffffff);
1358
1364
  colorNames.put("yellow", 0xffff00);
1359
1365
  }
1360
- */
1361
-
1362
- static protected int parseRGB(String what) {
1363
- int leftParen = what.indexOf('(') + 1;
1364
- int rightParen = what.indexOf(')');
1365
- String sub = what.substring(leftParen, rightParen);
1366
- String[] values = PApplet.splitTokens(sub, ", ");
1367
- int rgbValue = 0;
1368
- if (values.length == 3) {
1369
- // Color spec allows for rgb values to be percentages.
1370
- for (int i = 0; i < 3; i++) {
1371
- rgbValue <<= 8;
1372
- if (values[i].endsWith("%")) {
1373
- rgbValue |= (int)(PApplet.constrain(255*parseFloatOrPercent(values[i]), 0, 255));
1366
+ */
1367
+ static protected int parseRGB(String what) {
1368
+ int leftParen = what.indexOf('(') + 1;
1369
+ int rightParen = what.indexOf(')');
1370
+ String sub = what.substring(leftParen, rightParen);
1371
+ String[] values = PApplet.splitTokens(sub, ", ");
1372
+ int rgbValue = 0;
1373
+ if (values.length == 3) {
1374
+ // Color spec allows for rgb values to be percentages.
1375
+ for (int i = 0; i < 3; i++) {
1376
+ rgbValue <<= 8;
1377
+ if (values[i].endsWith("%")) {
1378
+ rgbValue |= (int) (PApplet.constrain(255 * parseFloatOrPercent(values[i]), 0, 255));
1379
+ } else {
1380
+ rgbValue |= PApplet.constrain(PApplet.parseInt(values[i]), 0, 255);
1381
+ }
1382
+ }
1374
1383
  } else {
1375
- rgbValue |= PApplet.constrain(PApplet.parseInt(values[i]), 0, 255);
1384
+ System.err.println("Could not read color \"" + what + "\".");
1376
1385
  }
1377
- }
1378
- } else System.err.println("Could not read color \"" + what + "\".");
1379
-
1380
- return rgbValue;
1381
- }
1382
1386
 
1387
+ return rgbValue;
1388
+ }
1383
1389
 
1384
- //static protected Map<String, String> parseStyleAttributes(String style) {
1385
- static protected StringDict parseStyleAttributes(String style) {
1386
- //Map<String, String> table = new HashMap<String, String>();
1387
- StringDict table = new StringDict();
1390
+ //static protected Map<String, String> parseStyleAttributes(String style) {
1391
+ static protected StringDict parseStyleAttributes(String style) {
1392
+ //Map<String, String> table = new HashMap<String, String>();
1393
+ StringDict table = new StringDict();
1388
1394
  // if (style == null) return table;
1389
- if (style != null) {
1390
- String[] pieces = style.split(";");
1391
- for (int i = 0; i < pieces.length; i++) {
1392
- String[] parts = pieces[i].split(":");
1393
- //table.put(parts[0], parts[1]);
1394
- table.set(parts[0], parts[1]);
1395
- }
1395
+ if (style != null) {
1396
+ String[] pieces = style.split(";");
1397
+ for (int i = 0; i < pieces.length; i++) {
1398
+ String[] parts = pieces[i].split(":");
1399
+ //table.put(parts[0], parts[1]);
1400
+ table.set(parts[0], parts[1]);
1401
+ }
1402
+ }
1403
+ return table;
1396
1404
  }
1397
- return table;
1398
- }
1399
1405
 
1400
-
1401
- /**
1402
- * Used in place of element.getFloatAttribute(a) because we can
1403
- * have a unit suffix (length or coordinate).
1404
- * @param element what to parse
1405
- * @param attribute name of the attribute to get
1406
- * @param relativeTo (float) Used for %. When relative to viewbox, should
1407
- * be svgWidth for horizontal dimentions, svgHeight for vertical, and
1408
- * svgXYSize for anything else.
1409
- * @return unit-parsed version of the data
1410
- */
1411
- static protected float getFloatWithUnit(XML element, String attribute, float relativeTo) {
1412
- String val = element.getString(attribute);
1413
- return (val == null) ? 0 : parseUnitSize(val, relativeTo);
1414
- }
1415
-
1416
-
1417
- /**
1418
- * Parse a size that may have a suffix for its units.
1419
- * This assumes 90dpi, which implies, as given in the
1420
- * <A HREF="http://www.w3.org/TR/SVG/coords.html#Units">units</A> spec:
1421
- * <UL>
1422
- * <LI>"1pt" equals "1.25px" (and therefore 1.25 user units)
1423
- * <LI>"1pc" equals "15px" (and therefore 15 user units)
1424
- * <LI>"1mm" would be "3.543307px" (3.543307 user units)
1425
- * <LI>"1cm" equals "35.43307px" (and therefore 35.43307 user units)
1426
- * <LI>"1in" equals "90px" (and therefore 90 user units)
1427
- * </UL>
1428
- * @param relativeTo (float) Used for %. When relative to viewbox, should
1429
- * be svgWidth for horizontal dimentions, svgHeight for vertical, and
1430
- * svgXYSize for anything else.
1431
- */
1432
- static protected float parseUnitSize(String text, float relativeTo) {
1433
- int len = text.length() - 2;
1434
-
1435
- if (text.endsWith("pt")) {
1436
- return PApplet.parseFloat(text.substring(0, len)) * 1.25f;
1437
- } else if (text.endsWith("pc")) {
1438
- return PApplet.parseFloat(text.substring(0, len)) * 15;
1439
- } else if (text.endsWith("mm")) {
1440
- return PApplet.parseFloat(text.substring(0, len)) * 3.543307f;
1441
- } else if (text.endsWith("cm")) {
1442
- return PApplet.parseFloat(text.substring(0, len)) * 35.43307f;
1443
- } else if (text.endsWith("in")) {
1444
- return PApplet.parseFloat(text.substring(0, len)) * 90;
1445
- } else if (text.endsWith("px")) {
1446
- return PApplet.parseFloat(text.substring(0, len));
1447
- } else if (text.endsWith("%")) {
1448
- return relativeTo * parseFloatOrPercent(text);
1449
- } else {
1450
- return PApplet.parseFloat(text);
1406
+ /**
1407
+ * Used in place of element.getFloatAttribute(a) because we can have a unit
1408
+ * suffix (length or coordinate).
1409
+ *
1410
+ * @param element what to parse
1411
+ * @param attribute name of the attribute to get
1412
+ * @param relativeTo (float) Used for %. When relative to viewbox, should be
1413
+ * svgWidth for horizontal dimentions, svgHeight for vertical, and svgXYSize
1414
+ * for anything else.
1415
+ * @return unit-parsed version of the data
1416
+ */
1417
+ static protected float getFloatWithUnit(XML element, String attribute, float relativeTo) {
1418
+ String val = element.getString(attribute);
1419
+ return (val == null) ? 0 : parseUnitSize(val, relativeTo);
1451
1420
  }
1452
- }
1453
-
1454
1421
 
1455
- static protected float parseFloatOrPercent(String text) {
1456
- text = text.trim();
1457
- if (text.endsWith("%")) {
1458
- return Float.parseFloat(text.substring(0, text.length() - 1)) / 100.0f;
1459
- } else {
1460
- return Float.parseFloat(text);
1422
+ /**
1423
+ * Parse a size that may have a suffix for its units. This assumes 90dpi,
1424
+ * which implies, as given in the
1425
+ * <A HREF="http://www.w3.org/TR/SVG/coords.html#Units">units</A> spec:
1426
+ * <UL>
1427
+ * <LI>"1pt" equals "1.25px" (and therefore 1.25 user units)
1428
+ * <LI>"1pc" equals "15px" (and therefore 15 user units)
1429
+ * <LI>"1mm" would be "3.543307px" (3.543307 user units)
1430
+ * <LI>"1cm" equals "35.43307px" (and therefore 35.43307 user units)
1431
+ * <LI>"1in" equals "90px" (and therefore 90 user units)
1432
+ * </UL>
1433
+ *
1434
+ * @param relativeTo (float) Used for %. When relative to viewbox, should be
1435
+ * svgWidth for horizontal dimentions, svgHeight for vertical, and svgXYSize
1436
+ * for anything else.
1437
+ */
1438
+ static protected float parseUnitSize(String text, float relativeTo) {
1439
+ int len = text.length() - 2;
1440
+
1441
+ if (text.endsWith("pt")) {
1442
+ return PApplet.parseFloat(text.substring(0, len)) * 1.25f;
1443
+ } else if (text.endsWith("pc")) {
1444
+ return PApplet.parseFloat(text.substring(0, len)) * 15;
1445
+ } else if (text.endsWith("mm")) {
1446
+ return PApplet.parseFloat(text.substring(0, len)) * 3.543307f;
1447
+ } else if (text.endsWith("cm")) {
1448
+ return PApplet.parseFloat(text.substring(0, len)) * 35.43307f;
1449
+ } else if (text.endsWith("in")) {
1450
+ return PApplet.parseFloat(text.substring(0, len)) * 90;
1451
+ } else if (text.endsWith("px")) {
1452
+ return PApplet.parseFloat(text.substring(0, len));
1453
+ } else if (text.endsWith("%")) {
1454
+ return relativeTo * parseFloatOrPercent(text);
1455
+ } else {
1456
+ return PApplet.parseFloat(text);
1457
+ }
1461
1458
  }
1462
- }
1463
-
1464
-
1465
- // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1466
-
1467
-
1468
- static public class Gradient extends PShapeSVG {
1469
- AffineTransform transform;
1470
-
1471
- public float[] offset;
1472
- public int[] color;
1473
- public int count;
1474
1459
 
1475
- public Gradient(PShapeSVG parent, XML properties) {
1476
- super(parent, properties, true);
1477
-
1478
- XML elements[] = properties.getChildren();
1479
- offset = new float[elements.length];
1480
- color = new int[elements.length];
1460
+ static protected float parseFloatOrPercent(String text) {
1461
+ text = text.trim();
1462
+ if (text.endsWith("%")) {
1463
+ return Float.parseFloat(text.substring(0, text.length() - 1)) / 100.0f;
1464
+ } else {
1465
+ return Float.parseFloat(text);
1466
+ }
1467
+ }
1481
1468
 
1482
- // <stop offset="0" style="stop-color:#967348"/>
1483
- for (int i = 0; i < elements.length; i++) {
1484
- XML elem = elements[i];
1485
- String name = elem.getName();
1486
- if (name.equals("stop")) {
1487
- String offsetAttr = elem.getString("offset");
1488
- offset[count] = parseFloatOrPercent(offsetAttr);
1489
-
1490
- String style = elem.getString("style");
1491
- //Map<String, String> styles = parseStyleAttributes(style);
1492
- StringDict styles = parseStyleAttributes(style);
1493
-
1494
- String colorStr = styles.get("stop-color");
1495
- if (colorStr == null) {
1496
- colorStr = elem.getString("stop-color");
1497
- if (colorStr == null) colorStr = "#000000";
1498
- }
1499
- String opacityStr = styles.get("stop-opacity");
1500
- if (opacityStr == null) {
1501
- opacityStr = elem.getString("stop-opacity");
1502
- if (opacityStr == null) opacityStr = "1";
1503
- }
1504
- int tupacity = PApplet.constrain(
1505
- (int)(PApplet.parseFloat(opacityStr) * 255), 0, 255);
1506
- color[count] = (tupacity << 24) | parseSimpleColor(colorStr);
1507
- count++;
1469
+ // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1470
+ static public class Gradient extends PShapeSVG {
1471
+
1472
+ AffineTransform transform;
1473
+
1474
+ public float[] offset;
1475
+ public int[] color;
1476
+ public int count;
1477
+
1478
+ public Gradient(PShapeSVG parent, XML properties) {
1479
+ super(parent, properties, true);
1480
+
1481
+ XML elements[] = properties.getChildren();
1482
+ offset = new float[elements.length];
1483
+ color = new int[elements.length];
1484
+
1485
+ // <stop offset="0" style="stop-color:#967348"/>
1486
+ for (int i = 0; i < elements.length; i++) {
1487
+ XML elem = elements[i];
1488
+ String name = elem.getName();
1489
+ if (name.equals("stop")) {
1490
+ String offsetAttr = elem.getString("offset");
1491
+ offset[count] = parseFloatOrPercent(offsetAttr);
1492
+
1493
+ String style = elem.getString("style");
1494
+ //Map<String, String> styles = parseStyleAttributes(style);
1495
+ StringDict styles = parseStyleAttributes(style);
1496
+
1497
+ String colorStr = styles.get("stop-color");
1498
+ if (colorStr == null) {
1499
+ colorStr = elem.getString("stop-color");
1500
+ if (colorStr == null) {
1501
+ colorStr = "#000000";
1502
+ }
1503
+ }
1504
+ String opacityStr = styles.get("stop-opacity");
1505
+ if (opacityStr == null) {
1506
+ opacityStr = elem.getString("stop-opacity");
1507
+ if (opacityStr == null) {
1508
+ opacityStr = "1";
1509
+ }
1510
+ }
1511
+ int tupacity = PApplet.constrain(
1512
+ (int) (PApplet.parseFloat(opacityStr) * 255), 0, 255);
1513
+ color[count] = (tupacity << 24) | parseSimpleColor(colorStr);
1514
+ count++;
1515
+ }
1516
+ }
1517
+ offset = PApplet.subset(offset, 0, count);
1518
+ color = PApplet.subset(color, 0, count);
1508
1519
  }
1509
- }
1510
- offset = PApplet.subset(offset, 0, count);
1511
- color = PApplet.subset(color, 0, count);
1512
1520
  }
1513
- }
1514
1521
 
1522
+ public class LinearGradient extends Gradient {
1515
1523
 
1516
- public class LinearGradient extends Gradient {
1517
- public float x1, y1, x2, y2;
1524
+ public float x1, y1, x2, y2;
1518
1525
 
1519
- public LinearGradient(PShapeSVG parent, XML properties) {
1520
- super(parent, properties);
1526
+ public LinearGradient(PShapeSVG parent, XML properties) {
1527
+ super(parent, properties);
1521
1528
 
1522
- this.x1 = getFloatWithUnit(properties, "x1", svgWidth);
1523
- this.y1 = getFloatWithUnit(properties, "y1", svgHeight);
1524
- this.x2 = getFloatWithUnit(properties, "x2", svgWidth);
1525
- this.y2 = getFloatWithUnit(properties, "y2", svgHeight);
1529
+ this.x1 = getFloatWithUnit(properties, "x1", svgWidth);
1530
+ this.y1 = getFloatWithUnit(properties, "y1", svgHeight);
1531
+ this.x2 = getFloatWithUnit(properties, "x2", svgWidth);
1532
+ this.y2 = getFloatWithUnit(properties, "y2", svgHeight);
1526
1533
 
1527
- String transformStr =
1528
- properties.getString("gradientTransform");
1534
+ String transformStr
1535
+ = properties.getString("gradientTransform");
1529
1536
 
1530
- if (transformStr != null) {
1531
- float t[] = parseTransform(transformStr).get(null);
1532
- this.transform = new AffineTransform(t[0], t[3], t[1], t[4], t[2], t[5]);
1537
+ if (transformStr != null) {
1538
+ float t[] = parseTransform(transformStr).get(null);
1539
+ this.transform = new AffineTransform(t[0], t[3], t[1], t[4], t[2], t[5]);
1533
1540
 
1534
- Point2D t1 = transform.transform(new Point2D.Float(x1, y1), null);
1535
- Point2D t2 = transform.transform(new Point2D.Float(x2, y2), null);
1541
+ Point2D t1 = transform.transform(new Point2D.Float(x1, y1), null);
1542
+ Point2D t2 = transform.transform(new Point2D.Float(x2, y2), null);
1536
1543
 
1537
- this.x1 = (float) t1.getX();
1538
- this.y1 = (float) t1.getY();
1539
- this.x2 = (float) t2.getX();
1540
- this.y2 = (float) t2.getY();
1541
- }
1544
+ this.x1 = (float) t1.getX();
1545
+ this.y1 = (float) t1.getY();
1546
+ this.x2 = (float) t2.getX();
1547
+ this.y2 = (float) t2.getY();
1548
+ }
1549
+ }
1542
1550
  }
1543
- }
1544
1551
 
1552
+ public class RadialGradient extends Gradient {
1545
1553
 
1546
- public class RadialGradient extends Gradient {
1547
- public float cx, cy, r;
1554
+ public float cx, cy, r;
1548
1555
 
1549
- public RadialGradient(PShapeSVG parent, XML properties) {
1550
- super(parent, properties);
1556
+ public RadialGradient(PShapeSVG parent, XML properties) {
1557
+ super(parent, properties);
1551
1558
 
1552
- this.cx = getFloatWithUnit(properties, "cx", svgWidth);
1553
- this.cy = getFloatWithUnit(properties, "cy", svgHeight);
1554
- this.r = getFloatWithUnit(properties, "r", svgSizeXY);
1559
+ this.cx = getFloatWithUnit(properties, "cx", svgWidth);
1560
+ this.cy = getFloatWithUnit(properties, "cy", svgHeight);
1561
+ this.r = getFloatWithUnit(properties, "r", svgSizeXY);
1555
1562
 
1556
- String transformStr =
1557
- properties.getString("gradientTransform");
1563
+ String transformStr
1564
+ = properties.getString("gradientTransform");
1558
1565
 
1559
- if (transformStr != null) {
1560
- float t[] = parseTransform(transformStr).get(null);
1561
- this.transform = new AffineTransform(t[0], t[3], t[1], t[4], t[2], t[5]);
1566
+ if (transformStr != null) {
1567
+ float t[] = parseTransform(transformStr).get(null);
1568
+ this.transform = new AffineTransform(t[0], t[3], t[1], t[4], t[2], t[5]);
1562
1569
 
1563
- Point2D t1 = transform.transform(new Point2D.Float(cx, cy), null);
1564
- Point2D t2 = transform.transform(new Point2D.Float(cx + r, cy), null);
1570
+ Point2D t1 = transform.transform(new Point2D.Float(cx, cy), null);
1571
+ Point2D t2 = transform.transform(new Point2D.Float(cx + r, cy), null);
1565
1572
 
1566
- this.cx = (float) t1.getX();
1567
- this.cy = (float) t1.getY();
1568
- this.r = (float) (t2.getX() - t1.getX());
1569
- }
1573
+ this.cx = (float) t1.getX();
1574
+ this.cy = (float) t1.getY();
1575
+ this.r = (float) (t2.getX() - t1.getX());
1576
+ }
1577
+ }
1570
1578
  }
1571
- }
1572
1579
 
1580
+ // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1581
+ public static class Font extends PShapeSVG {
1573
1582
 
1574
- // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1583
+ public FontFace face;
1575
1584
 
1585
+ public Map<String, FontGlyph> namedGlyphs;
1586
+ public Map<Character, FontGlyph> unicodeGlyphs;
1576
1587
 
1577
- public static class Font extends PShapeSVG {
1578
- public FontFace face;
1588
+ public int glyphCount;
1589
+ public FontGlyph[] glyphs;
1590
+ public FontGlyph missingGlyph;
1579
1591
 
1580
- public Map<String, FontGlyph> namedGlyphs;
1581
- public Map<Character, FontGlyph> unicodeGlyphs;
1592
+ int horizAdvX;
1582
1593
 
1583
- public int glyphCount;
1584
- public FontGlyph[] glyphs;
1585
- public FontGlyph missingGlyph;
1586
-
1587
- int horizAdvX;
1588
-
1589
-
1590
- public Font(PShapeSVG parent, XML properties) {
1591
- super(parent, properties, false);
1594
+ public Font(PShapeSVG parent, XML properties) {
1595
+ super(parent, properties, false);
1592
1596
  // handle(parent, properties);
1593
1597
 
1594
- XML[] elements = properties.getChildren();
1595
-
1596
- horizAdvX = properties.getInt("horiz-adv-x", 0);
1597
-
1598
- namedGlyphs = new HashMap<String, FontGlyph>();
1599
- unicodeGlyphs = new HashMap<Character, FontGlyph>();
1600
- glyphCount = 0;
1601
- glyphs = new FontGlyph[elements.length];
1602
-
1603
- for (int i = 0; i < elements.length; i++) {
1604
- String name = elements[i].getName();
1605
- XML elem = elements[i];
1606
- if (name == null) {
1607
- // skip it
1608
- } else if (name.equals("glyph")) {
1609
- FontGlyph fg = new FontGlyph(this, elem, this);
1610
- if (fg.isLegit()) {
1611
- if (fg.name != null) {
1612
- namedGlyphs.put(fg.name, fg);
1613
- }
1614
- if (fg.unicode != 0) {
1615
- unicodeGlyphs.put(Character.valueOf(fg.unicode), fg);
1616
- }
1617
- }
1618
- glyphs[glyphCount++] = fg;
1619
-
1620
- } else if (name.equals("missing-glyph")) {
1598
+ XML[] elements = properties.getChildren();
1599
+
1600
+ horizAdvX = properties.getInt("horiz-adv-x", 0);
1601
+
1602
+ namedGlyphs = new HashMap<String, FontGlyph>();
1603
+ unicodeGlyphs = new HashMap<Character, FontGlyph>();
1604
+ glyphCount = 0;
1605
+ glyphs = new FontGlyph[elements.length];
1606
+
1607
+ for (int i = 0; i < elements.length; i++) {
1608
+ String name = elements[i].getName();
1609
+ XML elem = elements[i];
1610
+ if (name == null) {
1611
+ // skip it
1612
+ } else if (name.equals("glyph")) {
1613
+ FontGlyph fg = new FontGlyph(this, elem, this);
1614
+ if (fg.isLegit()) {
1615
+ if (fg.name != null) {
1616
+ namedGlyphs.put(fg.name, fg);
1617
+ }
1618
+ if (fg.unicode != 0) {
1619
+ unicodeGlyphs.put(Character.valueOf(fg.unicode), fg);
1620
+ }
1621
+ }
1622
+ glyphs[glyphCount++] = fg;
1623
+
1624
+ } else if (name.equals("missing-glyph")) {
1621
1625
  // System.out.println("got missing glyph inside <font>");
1622
- missingGlyph = new FontGlyph(this, elem, this);
1623
- } else if (name.equals("font-face")) {
1624
- face = new FontFace(this, elem);
1625
- } else {
1626
- System.err.println("Ignoring " + name + " inside <font>");
1626
+ missingGlyph = new FontGlyph(this, elem, this);
1627
+ } else if (name.equals("font-face")) {
1628
+ face = new FontFace(this, elem);
1629
+ } else {
1630
+ System.err.println("Ignoring " + name + " inside <font>");
1631
+ }
1632
+ }
1627
1633
  }
1628
- }
1629
- }
1630
1634
 
1631
-
1632
- protected void drawShape() {
1633
- // does nothing for fonts
1634
- }
1635
-
1636
-
1637
- public void drawString(PGraphics g, String str, float x, float y, float size) {
1638
- // 1) scale by the 1.0/unitsPerEm
1639
- // 2) scale up by a font size
1640
- g.pushMatrix();
1641
- float s = size / face.unitsPerEm;
1642
- //System.out.println("scale is " + s);
1643
- // swap y coord at the same time, since fonts have y=0 at baseline
1644
- g.translate(x, y);
1645
- g.scale(s, -s);
1646
- char[] c = str.toCharArray();
1647
- for (int i = 0; i < c.length; i++) {
1648
- // call draw on each char (pulling it w/ the unicode table)
1649
- FontGlyph fg = unicodeGlyphs.get(Character.valueOf(c[i]));
1650
- if (fg != null) {
1651
- fg.draw(g);
1652
- // add horizAdvX/unitsPerEm to the x coordinate along the way
1653
- g.translate(fg.horizAdvX, 0);
1654
- } else {
1655
- System.err.println("'" + c[i] + "' not available.");
1635
+ protected void drawShape() {
1636
+ // does nothing for fonts
1656
1637
  }
1657
- }
1658
- g.popMatrix();
1659
- }
1660
1638
 
1639
+ public void drawString(PGraphics g, String str, float x, float y, float size) {
1640
+ // 1) scale by the 1.0/unitsPerEm
1641
+ // 2) scale up by a font size
1642
+ g.pushMatrix();
1643
+ float s = size / face.unitsPerEm;
1644
+ //System.out.println("scale is " + s);
1645
+ // swap y coord at the same time, since fonts have y=0 at baseline
1646
+ g.translate(x, y);
1647
+ g.scale(s, -s);
1648
+ char[] c = str.toCharArray();
1649
+ for (int i = 0; i < c.length; i++) {
1650
+ // call draw on each char (pulling it w/ the unicode table)
1651
+ FontGlyph fg = unicodeGlyphs.get(Character.valueOf(c[i]));
1652
+ if (fg != null) {
1653
+ fg.draw(g);
1654
+ // add horizAdvX/unitsPerEm to the x coordinate along the way
1655
+ g.translate(fg.horizAdvX, 0);
1656
+ } else {
1657
+ System.err.println("'" + c[i] + "' not available.");
1658
+ }
1659
+ }
1660
+ g.popMatrix();
1661
+ }
1661
1662
 
1662
- public void drawChar(PGraphics g, char c, float x, float y, float size) {
1663
- g.pushMatrix();
1664
- float s = size / face.unitsPerEm;
1665
- g.translate(x, y);
1666
- g.scale(s, -s);
1667
- FontGlyph fg = unicodeGlyphs.get(Character.valueOf(c));
1668
- if (fg != null) g.shape(fg);
1669
- g.popMatrix();
1670
- }
1671
-
1663
+ public void drawChar(PGraphics g, char c, float x, float y, float size) {
1664
+ g.pushMatrix();
1665
+ float s = size / face.unitsPerEm;
1666
+ g.translate(x, y);
1667
+ g.scale(s, -s);
1668
+ FontGlyph fg = unicodeGlyphs.get(Character.valueOf(c));
1669
+ if (fg != null) {
1670
+ g.shape(fg);
1671
+ }
1672
+ g.popMatrix();
1673
+ }
1672
1674
 
1673
- public float textWidth(String str, float size) {
1674
- float w = 0;
1675
- char[] c = str.toCharArray();
1676
- for (int i = 0; i < c.length; i++) {
1677
- // call draw on each char (pulling it w/ the unicode table)
1678
- FontGlyph fg = unicodeGlyphs.get(Character.valueOf(c[i]));
1679
- if (fg != null) {
1680
- w += (float) fg.horizAdvX / face.unitsPerEm;
1675
+ public float textWidth(String str, float size) {
1676
+ float w = 0;
1677
+ char[] c = str.toCharArray();
1678
+ for (int i = 0; i < c.length; i++) {
1679
+ // call draw on each char (pulling it w/ the unicode table)
1680
+ FontGlyph fg = unicodeGlyphs.get(Character.valueOf(c[i]));
1681
+ if (fg != null) {
1682
+ w += (float) fg.horizAdvX / face.unitsPerEm;
1683
+ }
1684
+ }
1685
+ return w * size;
1681
1686
  }
1682
- }
1683
- return w * size;
1684
1687
  }
1685
- }
1686
-
1687
1688
 
1688
- // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1689
+ // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1690
+ static class FontFace extends PShapeSVG {
1689
1691
 
1690
-
1691
- static class FontFace extends PShapeSVG {
1692
- int horizOriginX; // dflt 0
1693
- int horizOriginY; // dflt 0
1692
+ int horizOriginX; // dflt 0
1693
+ int horizOriginY; // dflt 0
1694
1694
  // int horizAdvX; // no dflt?
1695
- int vertOriginX; // dflt horizAdvX/2
1696
- int vertOriginY; // dflt ascent
1697
- int vertAdvY; // dflt 1em (unitsPerEm value)
1698
-
1699
- String fontFamily;
1700
- int fontWeight; // can also be normal or bold (also comma separated)
1701
- String fontStretch;
1702
- int unitsPerEm; // dflt 1000
1703
- int[] panose1; // dflt "0 0 0 0 0 0 0 0 0 0"
1704
- int ascent;
1705
- int descent;
1706
- int[] bbox; // spec says comma separated, tho not w/ forge
1707
- int underlineThickness;
1708
- int underlinePosition;
1709
- //String unicodeRange; // gonna ignore for now
1710
-
1711
-
1712
- public FontFace(PShapeSVG parent, XML properties) {
1713
- super(parent, properties, true);
1714
-
1715
- unitsPerEm = properties.getInt("units-per-em", 1000);
1716
- }
1717
-
1718
-
1719
- protected void drawShape() {
1720
- // nothing to draw in the font face attribute
1721
- }
1722
- }
1723
-
1724
-
1725
- // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1726
-
1727
-
1728
- public static class FontGlyph extends PShapeSVG { // extends Path
1729
- public String name;
1730
- char unicode;
1731
- int horizAdvX;
1732
-
1733
- public FontGlyph(PShapeSVG parent, XML properties, Font font) {
1734
- super(parent, properties, true);
1735
- super.parsePath(); // ??
1695
+ int vertOriginX; // dflt horizAdvX/2
1696
+ int vertOriginY; // dflt ascent
1697
+ int vertAdvY; // dflt 1em (unitsPerEm value)
1698
+
1699
+ String fontFamily;
1700
+ int fontWeight; // can also be normal or bold (also comma separated)
1701
+ String fontStretch;
1702
+ int unitsPerEm; // dflt 1000
1703
+ int[] panose1; // dflt "0 0 0 0 0 0 0 0 0 0"
1704
+ int ascent;
1705
+ int descent;
1706
+ int[] bbox; // spec says comma separated, tho not w/ forge
1707
+ int underlineThickness;
1708
+ int underlinePosition;
1709
+ //String unicodeRange; // gonna ignore for now
1710
+
1711
+ public FontFace(PShapeSVG parent, XML properties) {
1712
+ super(parent, properties, true);
1713
+
1714
+ unitsPerEm = properties.getInt("units-per-em", 1000);
1715
+ }
1736
1716
 
1737
- name = properties.getString("glyph-name");
1738
- String u = properties.getString("unicode");
1739
- unicode = 0;
1740
- if (u != null) {
1741
- if (u.length() == 1) {
1742
- unicode = u.charAt(0);
1743
- //System.out.println("unicode for " + name + " is " + u);
1744
- } else {
1745
- System.err.println("unicode for " + name +
1746
- " is more than one char: " + u);
1717
+ protected void drawShape() {
1718
+ // nothing to draw in the font face attribute
1747
1719
  }
1748
- }
1749
- if (properties.hasAttribute("horiz-adv-x")) {
1750
- horizAdvX = properties.getInt("horiz-adv-x");
1751
- } else {
1752
- horizAdvX = font.horizAdvX;
1753
- }
1754
1720
  }
1755
1721
 
1722
+ // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1723
+ public static class FontGlyph extends PShapeSVG { // extends Path
1724
+
1725
+ public String name;
1726
+ char unicode;
1727
+ int horizAdvX;
1728
+
1729
+ public FontGlyph(PShapeSVG parent, XML properties, Font font) {
1730
+ super(parent, properties, true);
1731
+ super.parsePath(); // ??
1732
+
1733
+ name = properties.getString("glyph-name");
1734
+ String u = properties.getString("unicode");
1735
+ unicode = 0;
1736
+ if (u != null) {
1737
+ if (u.length() == 1) {
1738
+ unicode = u.charAt(0);
1739
+ //System.out.println("unicode for " + name + " is " + u);
1740
+ } else {
1741
+ System.err.println("unicode for " + name
1742
+ + " is more than one char: " + u);
1743
+ }
1744
+ }
1745
+ if (properties.hasAttribute("horiz-adv-x")) {
1746
+ horizAdvX = properties.getInt("horiz-adv-x");
1747
+ } else {
1748
+ horizAdvX = font.horizAdvX;
1749
+ }
1750
+ }
1756
1751
 
1757
- protected boolean isLegit() { // TODO need a better way to handle this...
1758
- return vertexCount != 0;
1752
+ protected boolean isLegit() { // TODO need a better way to handle this...
1753
+ return vertexCount != 0;
1754
+ }
1759
1755
  }
1760
- }
1761
1756
 
1762
-
1763
- // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1764
-
1765
-
1766
- /**
1767
- * Get a particular element based on its SVG ID. When editing SVG by hand,
1768
- * this is the id="" tag on any SVG element. When editing from Illustrator,
1769
- * these IDs can be edited by expanding the layers palette. The names used
1770
- * in the layers palette, both for the layers or the shapes and groups
1771
- * beneath them can be used here.
1772
- * <PRE>
1773
- * // This code grabs "Layer 3" and the shapes beneath it.
1774
- * PShape layer3 = svg.getChild("Layer 3");
1775
- * </PRE>
1776
- */
1777
- @Override
1778
- public PShape getChild(String name) {
1779
- PShape found = super.getChild(name);
1780
- if (found == null) {
1781
- // Otherwise try with underscores instead of spaces
1782
- // (this is how Illustrator handles spaces in the layer names).
1783
- found = super.getChild(name.replace(' ', '_'));
1784
- }
1785
- // Set bounding box based on the parent bounding box
1786
- if (found != null) {
1757
+ // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1758
+ /**
1759
+ * Get a particular element based on its SVG ID. When editing SVG by hand,
1760
+ * this is the id="" tag on any SVG element. When editing from Illustrator,
1761
+ * these IDs can be edited by expanding the layers palette. The names used
1762
+ * in the layers palette, both for the layers or the shapes and groups
1763
+ * beneath them can be used here.
1764
+ * <PRE>
1765
+ * // This code grabs "Layer 3" and the shapes beneath it.
1766
+ * PShape layer3 = svg.getChild("Layer 3");
1767
+ * </PRE>
1768
+ */
1769
+ @Override
1770
+ public PShape getChild(String name) {
1771
+ PShape found = super.getChild(name);
1772
+ if (found == null) {
1773
+ // Otherwise try with underscores instead of spaces
1774
+ // (this is how Illustrator handles spaces in the layer names).
1775
+ found = super.getChild(name.replace(' ', '_'));
1776
+ }
1777
+ // Set bounding box based on the parent bounding box
1778
+ if (found != null) {
1787
1779
  // found.x = this.x;
1788
1780
  // found.y = this.y;
1789
- found.width = this.width;
1790
- found.height = this.height;
1781
+ found.width = this.width;
1782
+ found.height = this.height;
1783
+ }
1784
+ return found;
1791
1785
  }
1792
- return found;
1793
- }
1794
-
1795
1786
 
1796
- /**
1797
- * Prints out the SVG document. Useful for parsing.
1798
- */
1799
- public void print() {
1800
- PApplet.println(element.toString());
1801
- }
1787
+ /**
1788
+ * Prints out the SVG document. Useful for parsing.
1789
+ */
1790
+ public void print() {
1791
+ PApplet.println(element.toString());
1792
+ }
1802
1793
  }