propane 3.4.2-java → 3.5.0-java

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