picrate 0.7.0-java → 0.8.0-java

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