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