picrate 0.7.0-java → 0.8.0-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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
  */