merge svg changes

This commit is contained in:
benfry
2010-06-25 15:38:48 +00:00
parent a97bae1566
commit 89bc93af0f
3 changed files with 390 additions and 205 deletions

View File

@@ -68,7 +68,7 @@ public class PShape implements PConstants {
protected int family;
/** ELLIPSE, LINE, QUAD; TRIANGLE_FAN, QUAD_STRIP; etc. */
protected int kind;
protected int primitive;
protected PMatrix matrix;
@@ -80,7 +80,17 @@ public class PShape implements PConstants {
//protected float y;
//protected float width;
//protected float height;
/**
* The width of the PShape document.
* @webref
* @brief Shape document width
*/
public float width;
/**
* The width of the PShape document.
* @webref
* @brief Shape document height
*/
public float height;
public float depth;
@@ -114,7 +124,7 @@ public class PShape implements PConstants {
static public final int BEZIER_VERTEX = 1;
static public final int CURVE_VERTEX = 2;
static public final int BREAK = 3;
/** Array of VERTEX, BEZIER_VERTEX, and CURVE_VERTEXT calls. */
/** Array of VERTEX, BEZIER_VERTEX, and CURVE_VERTEX calls. */
protected int vertexCodeCount;
protected int[] vertexCodes;
/** True if this is a closed path. */
@@ -178,21 +188,39 @@ public class PShape implements PConstants {
return name;
}
/**
* Returns a boolean value "true" if the image is set to be visible, "false" if not. This is modified with the <b>setVisible()</b> parameter.
* <br><br>The visibility of a shape is usually controlled by whatever program created the SVG file.
* For instance, this parameter is controlled by showing or hiding the shape in the layers palette in Adobe Illustrator.
*
* @webref
* @brief Returns a boolean value "true" if the image is set to be visible, "false" if not
*/
public boolean isVisible() {
return visible;
}
/**
* Sets the shape to be visible or invisible. This is determined by the value of the <b>visible</b> parameter.
* <br><br>The visibility of a shape is usually controlled by whatever program created the SVG file.
* For instance, this parameter is controlled by showing or hiding the shape in the layers palette in Adobe Illustrator.
* @param visible "false" makes the shape invisible and "true" makes it visible
* @webref
* @brief Sets the shape to be visible or invisible
*/
public void setVisible(boolean visible) {
this.visible = visible;
}
/**
* Disables the shape's style data and uses Processing's current styles. Styles include attributes such as colors, stroke weight, and stroke joints.
* =advanced
* Overrides this shape's style information and uses PGraphics styles and
* colors. Identical to ignoreStyles(true). Also disables styles for all
* child shapes.
* @webref
* @brief Disables the shape's style data and uses Processing styles
*/
public void disableStyle() {
style = false;
@@ -204,7 +232,9 @@ public class PShape implements PConstants {
/**
* Re-enables style information (fill and stroke) set in the shape.
* Enables the shape's style data and ignores Processing's current styles. Styles include attributes such as colors, stroke weight, and stroke joints.
* @webref
* @brief Enables the shape's style data and ignores the Processing styles
*/
public void enableStyle() {
style = true;
@@ -398,10 +428,10 @@ public class PShape implements PConstants {
protected void drawPrimitive(PGraphics g) {
if (kind == POINT) {
if (primitive == POINT) {
g.point(params[0], params[1]);
} else if (kind == LINE) {
} else if (primitive == LINE) {
if (params.length == 4) { // 2D
g.line(params[0], params[1],
params[2], params[3]);
@@ -410,18 +440,18 @@ public class PShape implements PConstants {
params[3], params[4], params[5]);
}
} else if (kind == TRIANGLE) {
} else if (primitive == TRIANGLE) {
g.triangle(params[0], params[1],
params[2], params[3],
params[4], params[5]);
} else if (kind == QUAD) {
} else if (primitive == QUAD) {
g.quad(params[0], params[1],
params[2], params[3],
params[4], params[5],
params[6], params[7]);
} else if (kind == RECT) {
} else if (primitive == RECT) {
if (image != null) {
g.imageMode(CORNER);
g.image(image, params[0], params[1], params[2], params[3]);
@@ -430,29 +460,29 @@ public class PShape implements PConstants {
g.rect(params[0], params[1], params[2], params[3]);
}
} else if (kind == ELLIPSE) {
} else if (primitive == ELLIPSE) {
g.ellipseMode(CORNER);
g.ellipse(params[0], params[1], params[2], params[3]);
} else if (kind == ARC) {
} else if (primitive == ARC) {
g.ellipseMode(CORNER);
g.arc(params[0], params[1], params[2], params[3], params[4], params[5]);
} else if (kind == BOX) {
} else if (primitive == BOX) {
if (params.length == 1) {
g.box(params[0]);
} else {
g.box(params[0], params[1], params[2]);
}
} else if (kind == SPHERE) {
} else if (primitive == SPHERE) {
g.sphere(params[0]);
}
}
protected void drawGeometry(PGraphics g) {
g.beginShape(kind);
g.beginShape(primitive);
if (style) {
for (int i = 0; i < vertexCount; i++) {
g.vertex(vertices[i]);
@@ -608,12 +638,21 @@ public class PShape implements PConstants {
return childCount;
}
/**
*
* @param index the layer position of the shape to get
*/
public PShape getChild(int index) {
return children[index];
}
/**
* Extracts a child shape from a parent shape. Specify the name of the shape with the <b>target</b> parameter.
* The shape is returned as a <b>PShape</b> object, or <b>null</b> is returned if there is an error.
* @param target the name of the shape to get
* @webref
* @brief Returns a child element of a shape as a PShape object
*/
public PShape getChild(String target) {
if (name != null && name.equals(target)) {
return this;
@@ -632,7 +671,7 @@ public class PShape implements PConstants {
/**
* Same as getChild(name), except that it first walks all the way up the
* hierarchy to the farthest parent, so that children can be found anywhere.
* hierarchy to the eldest grandparent, so that children can be found anywhere.
*/
public PShape findChild(String target) {
if (parent == null) {
@@ -684,6 +723,114 @@ public class PShape implements PConstants {
// }
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
/** The shape type, one of GROUP, PRIMITIVE, PATH, or GEOMETRY. */
public int getFamily() {
return family;
}
public int getPrimitive() {
return primitive;
}
public float[] getParams() {
return getParams(null);
}
public float[] getParams(float[] target) {
if (target == null || target.length != params.length) {
target = new float[params.length];
}
PApplet.arrayCopy(params, target);
return target;
}
public float getParam(int index) {
return params[index];
}
public int getVertexCount() {
return vertexCount;
}
public float[] getVertex(int index) {
if (index < 0 || index >= vertexCount) {
String msg = "No vertex " + index + " for this shape, " +
"only vertices 0 through " + (vertexCount-1) + ".";
throw new IllegalArgumentException(msg);
}
return vertices[index];
}
public float getVertexX(int index) {
return vertices[index][X];
}
public float getVertexY(int index) {
return vertices[index][Y];
}
public float getVertexZ(int index) {
return vertices[index][Z];
}
public int[] getVertexCodes() {
if (vertexCodes.length != vertexCodeCount) {
vertexCodes = PApplet.subset(vertexCodes, 0, vertexCodeCount);
}
return vertexCodes;
}
public int getVertexCodeCount() {
return vertexCodeCount;
}
/**
* One of VERTEX, BEZIER_VERTEX, CURVE_VERTEX, or BREAK.
*/
public int getVertexCode(int index) {
return vertexCodes[index];
}
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
// http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
public boolean contains(float x, float y) {
if (family == PATH) {
boolean c = false;
for (int i = 0, j = vertexCount-1; i < vertexCount; j = i++) {
if (((vertices[i][Y] > y) != (vertices[j][Y] > y)) &&
(x <
(vertices[j][X]-vertices[i][X]) *
(y-vertices[i][Y]) /
(vertices[j][1]-vertices[i][Y]) +
vertices[i][X])) {
c = !c;
}
}
return c;
} else {
throw new IllegalArgumentException("The contains() method is only implemented for paths.");
}
}
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
@@ -692,58 +839,119 @@ public class PShape implements PConstants {
// if matrix is null when one is called,
// it is created and set to identity
public void translate(float tx, float ty) {
checkMatrix(2);
matrix.translate(tx, ty);
}
/**
* Specifies an amount to displace the shape. The <b>x</b> parameter specifies left/right translation, the <b>y</b> parameter specifies up/down translation, and the <b>z</b> parameter specifies translations toward/away from the screen. Subsequent calls to the method accumulates the effect. For example, calling <b>translate(50, 0)</b> and then <b>translate(20, 0)</b> is the same as <b>translate(70, 0)</b>. This transformation is applied directly to the shape, it's not refreshed each time <b>draw()</b> is run.
* <br><br>Using this method with the <b>z</b> parameter requires using the P3D or OPENGL parameter in combination with size.
* @webref
* @param tx left/right translation
* @param ty up/down translation
* @param tz forward/back translation
* @brief Displaces the shape
*/
public void translate(float tx, float ty, float tz) {
checkMatrix(3);
matrix.translate(tx, ty, 0);
}
/**
* Rotates a shape around the x-axis the amount specified by the <b>angle</b> parameter. Angles should be specified in radians (values from 0 to TWO_PI) or converted to radians with the <b>radians()</b> method.
* <br><br>Shapes are always rotated around the upper-left corner of their bounding box. Positive numbers rotate objects in a clockwise direction.
* Subsequent calls to the method accumulates the effect. For example, calling <b>rotateX(HALF_PI)</b> and then <b>rotateX(HALF_PI)</b> is the same as <b>rotateX(PI)</b>.
* This transformation is applied directly to the shape, it's not refreshed each time <b>draw()</b> is run.
* <br><br>This method requires a 3D renderer. You need to pass P3D or OPENGL as a third parameter into the <b>size()</b> method as shown in the example above.
* @param angle angle of rotation specified in radians
* @webref
* @brief Rotates the shape around the x-axis
*/
public void rotateX(float angle) {
rotate(angle, 1, 0, 0);
}
/**
* Rotates a shape around the y-axis the amount specified by the <b>angle</b> parameter. Angles should be specified in radians (values from 0 to TWO_PI) or converted to radians with the <b>radians()</b> method.
* <br><br>Shapes are always rotated around the upper-left corner of their bounding box. Positive numbers rotate objects in a clockwise direction.
* Subsequent calls to the method accumulates the effect. For example, calling <b>rotateY(HALF_PI)</b> and then <b>rotateY(HALF_PI)</b> is the same as <b>rotateY(PI)</b>.
* This transformation is applied directly to the shape, it's not refreshed each time <b>draw()</b> is run.
* <br><br>This method requires a 3D renderer. You need to pass P3D or OPENGL as a third parameter into the <b>size()</b> method as shown in the example above.
* @param angle angle of rotation specified in radians
* @webref
* @brief Rotates the shape around the y-axis
*/
public void rotateY(float angle) {
rotate(angle, 0, 1, 0);
}
/**
* Rotates a shape around the z-axis the amount specified by the <b>angle</b> parameter. Angles should be specified in radians (values from 0 to TWO_PI) or converted to radians with the <b>radians()</b> method.
* <br><br>Shapes are always rotated around the upper-left corner of their bounding box. Positive numbers rotate objects in a clockwise direction.
* Subsequent calls to the method accumulates the effect. For example, calling <b>rotateZ(HALF_PI)</b> and then <b>rotateZ(HALF_PI)</b> is the same as <b>rotateZ(PI)</b>.
* This transformation is applied directly to the shape, it's not refreshed each time <b>draw()</b> is run.
* <br><br>This method requires a 3D renderer. You need to pass P3D or OPENGL as a third parameter into the <b>size()</b> method as shown in the example above.
* @param angle angle of rotation specified in radians
* @webref
* @brief Rotates the shape around the z-axis
*/
public void rotateZ(float angle) {
rotate(angle, 0, 0, 1);
}
/**
* Rotates a shape the amount specified by the <b>angle</b> parameter. Angles should be specified in radians (values from 0 to TWO_PI) or converted to radians with the <b>radians()</b> method.
* <br><br>Shapes are always rotated around the upper-left corner of their bounding box. Positive numbers rotate objects in a clockwise direction.
* Transformations apply to everything that happens after and subsequent calls to the method accumulates the effect.
* For example, calling <b>rotate(HALF_PI)</b> and then <b>rotate(HALF_PI)</b> is the same as <b>rotate(PI)</b>.
* This transformation is applied directly to the shape, it's not refreshed each time <b>draw()</b> is run.
* @param angle angle of rotation specified in radians
* @webref
* @brief Rotates the shape
*/
public void rotate(float angle) {
rotate(angle, 0, 0, 1);
checkMatrix(2); // at least 2...
matrix.rotate(angle);
}
public void rotate(float angle, float v0, float v1, float v2) {
checkMatrix(3);
matrix.rotate(angle, v0, v1, v2);
}
//
/**
* @param s percentage to scale the object
*/
public void scale(float s) {
checkMatrix(2); // at least 2...
matrix.scale(s);
}
public void scale(float sx, float sy) {
public void scale(float x, float y) {
checkMatrix(2);
matrix.scale(sx, sy);
matrix.scale(x, y);
}
/**
* Increases or decreases the size of a shape by expanding and contracting vertices. Shapes always scale from the relative origin of their bounding box.
* Scale values are specified as decimal percentages. For example, the method call <b>scale(2.0)</b> increases the dimension of a shape by 200%.
* Subsequent calls to the method multiply the effect. For example, calling <b>scale(2.0)</b> and then <b>scale(1.5)</b> is the same as <b>scale(3.0)</b>.
* This transformation is applied directly to the shape, it's not refreshed each time <b>draw()</b> is run.
* <br><br>Using this fuction with the <b>z</b> parameter requires passing P3D or OPENGL into the size() parameter.
* @param x percentage to scale the object in the x-axis
* @param y percentage to scale the object in the y-axis
* @param z percentage to scale the object in the z-axis
* @webref
* @brief Increases and decreases the size of a shape
*/
public void scale(float x, float y, float z) {
checkMatrix(3);
matrix.scale(x, y, z);

View File

@@ -341,7 +341,8 @@ public class PShape3D extends PShape implements PConstants {
return numVertices;
}
public PVector getVertex(int idx) {
// public PVector getVertex(int idx) {
public float[] getVertex(int idx) {
if (updateElement != VERTICES) {
throw new RuntimeException("PShape3D: update mode is not set to VERTICES");
}
@@ -350,10 +351,11 @@ public class PShape3D extends PShape implements PConstants {
float y = vertexArray[3 * idx + 1];
float z = vertexArray[3 * idx + 2];
PVector res = new PVector(x, y, z);
return res;
// PVector res = new PVector(x, y, z);
// return res;
return new float[] { x, y, z };
}
public float[] getVertexArray() {
if (updateElement != VERTICES) {
@@ -371,7 +373,7 @@ public class PShape3D extends PShape implements PConstants {
PApplet.arrayCopy(vertexArray, firstUpd * 3, data, firstData * 3, length * 3);
return data;
}
public ArrayList<PVector> getVertexArrayList() {
if (updateElement != VERTICES) {
@@ -381,7 +383,10 @@ public class PShape3D extends PShape implements PConstants {
ArrayList<PVector> res;
res = new ArrayList<PVector>();
for (int i = 0; i < numVertices; i++) res.add(getVertex(i));
for (int i = 0; i < numVertices; i++) {
float[] v = getVertex(i);
res.add(new PVector(v[0], v[1], v[2]));
}
return res;
}

View File

@@ -207,7 +207,9 @@ public class PShapeSVG extends PShape {
public PShapeSVG(PShapeSVG parent, XMLElement properties) {
//super(GROUP);
// Need to set this so that findChild() works.
// Otherwise 'parent' is null until addChild() is called later.
this.parent = parent;
if (parent == null) {
// set values to their defaults according to the SVG spec
@@ -257,10 +259,10 @@ public class PShapeSVG extends PShape {
element = properties;
name = properties.getStringAttribute("id");
// @#$(* adobe illustrator
// @#$(* adobe illustrator mangles names of objects when re-saving
if (name != null) {
while (true) {
String[] m = PApplet.match(name, "_x(.*)_");
String[] m = PApplet.match(name, "_x([A-Za-z0-9]{2})_");
if (m == null) break;
char repair = (char) PApplet.unhex(m[1]);
name = name.replace(m[0], "" + repair);
@@ -354,8 +356,9 @@ public class PShapeSVG extends PShape {
} else if (name.equals("linearGradient")) {
return new LinearGradient(this, elem);
} else if (name.equals("text")) {
PGraphics.showWarning("Text in SVG files is not currently supported, " +
} else if (name.equals("text") || name.equals("font")) {
PGraphics.showWarning("Text and fonts in SVG files " +
"are not currently supported, " +
"convert text to outlines instead.");
} else if (name.equals("filter")) {
@@ -364,9 +367,15 @@ public class PShapeSVG extends PShape {
} else if (name.equals("mask")) {
PGraphics.showWarning("Masks are not supported.");
} else if (name.equals("pattern")) {
PGraphics.showWarning("Patterns are not supported.");
} else if (name.equals("stop")) {
// stop tag is handled by gradient parser, so don't warn about it
} else if (name.equals("sodipodi:namedview")) {
// these are always in Inkscape files, the warnings get tedious
} else {
PGraphics.showWarning("Ignoring <" + name + "> tag.");
}
@@ -375,18 +384,14 @@ public class PShapeSVG extends PShape {
protected void parseLine() {
kind = LINE;
primitive = LINE;
family = PRIMITIVE;
params = new float[] {
element.getFloatAttribute("x1"),
element.getFloatAttribute("y1"),
element.getFloatAttribute("x2"),
element.getFloatAttribute("y2"),
getFloatWithUnit(element, "x1"),
getFloatWithUnit(element, "y1"),
getFloatWithUnit(element, "x2"),
getFloatWithUnit(element, "y2")
};
// x = params[0];
// y = params[1];
// width = params[2];
// height = params[3];
}
@@ -395,19 +400,19 @@ public class PShapeSVG extends PShape {
* @param circle true if this is a circle and not an ellipse
*/
protected void parseEllipse(boolean circle) {
kind = ELLIPSE;
primitive = ELLIPSE;
family = PRIMITIVE;
params = new float[4];
params[0] = element.getFloatAttribute("cx");
params[1] = element.getFloatAttribute("cy");
params[0] = getFloatWithUnit(element, "cx");
params[1] = getFloatWithUnit(element, "cy");
float rx, ry;
if (circle) {
rx = ry = element.getFloatAttribute("r");
rx = ry = getFloatWithUnit(element, "r");
} else {
rx = element.getFloatAttribute("rx");
ry = element.getFloatAttribute("ry");
rx = getFloatWithUnit(element, "rx");
ry = getFloatWithUnit(element, "ry");
}
params[0] -= rx;
params[1] -= ry;
@@ -418,13 +423,13 @@ public class PShapeSVG extends PShape {
protected void parseRect() {
kind = RECT;
primitive = RECT;
family = PRIMITIVE;
params = new float[] {
element.getFloatAttribute("x"),
element.getFloatAttribute("y"),
element.getFloatAttribute("width"),
element.getFloatAttribute("height"),
getFloatWithUnit(element, "x"),
getFloatWithUnit(element, "y"),
getFloatWithUnit(element, "width"),
getFloatWithUnit(element, "height")
};
}
@@ -453,7 +458,7 @@ public class PShapeSVG extends PShape {
protected void parsePath() {
family = PATH;
kind = 0;
primitive = 0;
String pathData = element.getStringAttribute("d");
if (pathData == null) return;
@@ -485,7 +490,7 @@ public class PShapeSVG extends PShape {
separate = false;
}
if (c == '-' && !lastSeparate) {
// allow for 'e' notation in numbers, e.g. 2.10e-9
// allow for 'e' notation in numbers, e.g. 2.10e-9
// http://dev.processing.org/bugs/show_bug.cgi?id=1408
if (i == 0 || pathDataChars[i-1] != 'e') {
pathBuffer.append("|");
@@ -509,6 +514,7 @@ public class PShapeSVG extends PShape {
float cx = 0;
float cy = 0;
int i = 0;
char implicitCommand = '\0';
while (i < pathDataKeys.length) {
@@ -651,7 +657,7 @@ public class PShapeSVG extends PShape {
float ctrlY = PApplet.parseFloat(pathDataKeys[i + 2]);
float endX = PApplet.parseFloat(pathDataKeys[i + 3]);
float endY = PApplet.parseFloat(pathDataKeys[i + 4]);
parsePathCurveto(ctrlX, ctrlY, ctrlX, ctrlY, endX, endY);
parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY);
cx = endX;
cy = endY;
i += 5;
@@ -664,7 +670,7 @@ public class PShapeSVG extends PShape {
float ctrlY = cy + PApplet.parseFloat(pathDataKeys[i + 2]);
float endX = cx + PApplet.parseFloat(pathDataKeys[i + 3]);
float endY = cy + PApplet.parseFloat(pathDataKeys[i + 4]);
parsePathCurveto(ctrlX, ctrlY, ctrlX, ctrlY, endX, endY);
parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY);
cx = endX;
cy = endY;
i += 5;
@@ -686,7 +692,7 @@ public class PShapeSVG extends PShape {
float ctrlY = py + (py - ppy);
float endX = PApplet.parseFloat(pathDataKeys[i + 1]);
float endY = PApplet.parseFloat(pathDataKeys[i + 2]);
parsePathCurveto(ctrlX, ctrlY, ctrlX, ctrlY, endX, endY);
parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY);
cx = endX;
cy = endY;
i += 3;
@@ -703,7 +709,7 @@ public class PShapeSVG extends PShape {
float ctrlY = py + (py - ppy);
float endX = cx + PApplet.parseFloat(pathDataKeys[i + 1]);
float endY = cy + PApplet.parseFloat(pathDataKeys[i + 2]);
parsePathCurveto(ctrlX, ctrlY, ctrlX, ctrlY, endX, endY);
parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY);
cx = endX;
cy = endY;
i += 3;
@@ -788,6 +794,16 @@ public class PShapeSVG extends PShape {
parsePathVertex(x3, y3);
}
private void parsePathQuadto(float x1, float y1,
float cx, float cy,
float x2, float y2) {
parsePathCode(BEZIER_VERTEX);
// x1/y1 already covered by last moveto, lineto, or curveto
parsePathVertex(x1 + ((cx-x1)*2/3.0f), y1 + ((cy-y1)*2/3.0f));
parsePathVertex(x2 + ((cx-x2)*2/3.0f), y2 + ((cy-y2)*2/3.0f));
parsePathVertex(x2, y2);
}
/**
* Parse the specified SVG matrix into a PMatrix2D. Note that PMatrix2D
@@ -854,7 +870,12 @@ public class PShapeSVG extends PShape {
if (properties.hasAttribute("stroke")) {
String strokeText = properties.getStringAttribute("stroke");
setStroke(strokeText);
setColor(strokeText, false);
}
if (properties.hasAttribute("stroke-opacity")) {
String strokeOpacityText = properties.getStringAttribute("stroke-opacity");
setStrokeOpacity(strokeOpacityText);
}
if (properties.hasAttribute("stroke-width")) {
@@ -873,15 +894,18 @@ public class PShapeSVG extends PShape {
setStrokeCap(linecap);
}
// fill defaults to black (though stroke defaults to "none")
// http://www.w3.org/TR/SVG/painting.html#FillProperties
if (properties.hasAttribute("fill")) {
String fillText = properties.getStringAttribute("fill");
setFill(fillText);
setColor(fillText, true);
}
if (properties.hasAttribute("fill-opacity")) {
String fillOpacityText = properties.getStringAttribute("fill-opacity");
setFillOpacity(fillOpacityText);
}
if (properties.hasAttribute("style")) {
String styleText = properties.getStringAttribute("style");
String[] styleTokens = PApplet.splitTokens(styleText, ";");
@@ -894,13 +918,13 @@ public class PShapeSVG extends PShape {
tokens[0] = PApplet.trim(tokens[0]);
if (tokens[0].equals("fill")) {
setFill(tokens[1]);
setColor(tokens[1], true);
} else if(tokens[0].equals("fill-opacity")) {
setFillOpacity(tokens[1]);
} else if(tokens[0].equals("stroke")) {
setStroke(tokens[1]);
setColor(tokens[1], false);
} else if(tokens[0].equals("stroke-width")) {
setStrokeWeight(tokens[1]);
@@ -924,43 +948,24 @@ public class PShapeSVG extends PShape {
}
}
void setOpacity(String opacityText) {
opacity = PApplet.parseFloat(opacityText);
strokeColor = ((int) (opacity * 255)) << 24 | strokeColor & 0xFFFFFF;
fillColor = ((int) (opacity * 255)) << 24 | fillColor & 0xFFFFFF;
}
void setStrokeWeight(String lineweight) {
strokeWeight = PApplet.parseFloat(lineweight);
strokeWeight = parseUnitSize(lineweight);
}
void setStrokeOpacity(String opacityText) {
strokeOpacity = PApplet.parseFloat(opacityText);
strokeColor = ((int) (strokeOpacity * 255)) << 24 | strokeColor & 0xFFFFFF;
}
void setStroke(String strokeText) {
int opacityMask = strokeColor & 0xFF000000;
if (strokeText.equals("none")) {
stroke = false;
} else if (strokeText.startsWith("#")) {
stroke = true;
strokeColor = opacityMask |
(Integer.parseInt(strokeText.substring(1), 16)) & 0xFFFFFF;
} else if (strokeText.startsWith("rgb")) {
stroke = true;
strokeColor = opacityMask | parseRGB(strokeText);
} else if (strokeText.startsWith("url(#")) {
strokeName = strokeText.substring(5, strokeText.length() - 1);
Object strokeObject = findChild(strokeName);
if (strokeObject instanceof Gradient) {
strokeGradient = (Gradient) strokeObject;
strokeGradientPaint = calcGradientPaint(strokeGradient); //, opacity);
} else {
System.err.println("url " + strokeName + " refers to unexpected data");
}
}
}
void setStrokeJoin(String linejoin) {
if (linejoin.equals("inherit")) {
@@ -977,6 +982,7 @@ public class PShapeSVG extends PShape {
}
}
void setStrokeCap(String linecap) {
if (linecap.equals("inherit")) {
// do nothing, will inherit automatically
@@ -991,41 +997,67 @@ public class PShapeSVG extends PShape {
strokeCap = PConstants.PROJECT;
}
}
void setFillOpacity(String opacityText) {
fillOpacity = PApplet.parseFloat(opacityText);
fillColor = ((int) (fillOpacity * 255)) << 24 | fillColor & 0xFFFFFF;
}
void setFill(String fillText) {
void setColor(String colorText, boolean isFill) {
int opacityMask = fillColor & 0xFF000000;
if (fillText.equals("none")) {
fill = false;
} else if (fillText.startsWith("#")) {
fill = true;
fillColor = opacityMask |
(Integer.parseInt(fillText.substring(1), 16)) & 0xFFFFFF;
boolean visible = true;
int color = 0;
String name = "";
Gradient gradient = null;
Shader paint = null;
if (colorText.equals("none")) {
visible = false;
} else if (colorText.equals("black")) {
color = opacityMask;
} else if (colorText.equals("white")) {
color = opacityMask | 0xFFFFFF;
} else if (colorText.startsWith("#")) {
if (colorText.length() == 4) {
// Short form: #ABC, transform to long form #AABBCC
colorText = colorText.replaceAll("^#(.)(.)(.)$", "#$1$1$2$2$3$3");
}
color = opacityMask |
(Integer.parseInt(colorText.substring(1), 16)) & 0xFFFFFF;
//System.out.println("hex for fill is " + PApplet.hex(fillColor));
} else if (fillText.startsWith("rgb")) {
fill = true;
fillColor = opacityMask | parseRGB(fillText);
} else if (fillText.startsWith("url(#")) {
fillName = fillText.substring(5, fillText.length() - 1);
//PApplet.println("looking for " + fillName);
Object fillObject = findChild(fillName);
} else if (colorText.startsWith("rgb")) {
color = opacityMask | parseRGB(colorText);
} else if (colorText.startsWith("url(#")) {
name = colorText.substring(5, colorText.length() - 1);
// PApplet.println("looking for " + name);
Object object = findChild(name);
//PApplet.println("found " + fillObject);
if (fillObject instanceof Gradient) {
fill = true;
fillGradient = (Gradient) fillObject;
fillGradientPaint = calcGradientPaint(fillGradient); //, opacity);
if (object instanceof Gradient) {
gradient = (Gradient) object;
paint = calcGradientPaint(gradient); //, opacity);
//PApplet.println("got filla " + fillObject);
} else {
System.err.println("url " + fillName + " refers to unexpected data");
// visible = false;
System.err.println("url " + name + " refers to unexpected data: " + object);
}
}
if (isFill) {
fill = visible;
fillColor = color;
fillName = name;
fillGradient = gradient;
fillGradientPaint = paint;
} else {
stroke = visible;
strokeColor = color;
strokeName = name;
strokeGradient = gradient;
strokeGradientPaint = paint;
}
}
static protected int parseRGB(String what) {
int leftParen = what.indexOf('(') + 1;
int rightParen = what.indexOf(')');
@@ -1046,6 +1078,19 @@ public class PShapeSVG extends PShape {
}
/**
* Used in place of element.getFloatAttribute(a) because we can
* have a unit suffix (length or coordinate).
* @param element what to parse
* @param attribute name of the attribute to get
* @return unit-parsed version of the data
*/
static protected float getFloatWithUnit(XMLElement element, String attribute) {
String val = element.getStringAttribute(attribute);
return (val == null) ? 0 : parseUnitSize(val);
}
/**
* Parse a size that may have a suffix for its units.
* Ignoring cases where this could also be a percentage.
@@ -1079,85 +1124,6 @@ public class PShapeSVG extends PShape {
}
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
// these are a set of hacks so that gradients can be hacked into Java 2D.
/*
protected Paint getGradient(String name, float cx, float cy, float r) {
BaseObject obj = (BaseObject) findChild(name);
if (obj != null) {
if (obj.fillGradient != null) {
return obj.calcGradientPaint(obj.fillGradient, cx, cy, r);
}
}
throw new RuntimeException("No gradient found for shape " + name);
}
protected Paint getGradient(String name, float x1, float y1, float x2, float y2) {
BaseObject obj = (BaseObject) findChild(name);
if (obj != null) {
if (obj.fillGradient != null) {
return obj.calcGradientPaint(obj.fillGradient, x1, y1, x2, y2);
}
}
throw new RuntimeException("No gradient found for shape " + name);
}
protected void strokeGradient(PGraphics g, String name, float x, float y, float r) {
Paint paint = getGradient(name, x, y, r);
if (g instanceof PGraphicsJava2D) {
PGraphicsJava2D p2d = (PGraphicsJava2D) g;
p2d.strokeGradient = true;
p2d.strokeGradientObject = paint;
}
}
protected void strokeGradient(PGraphics g, String name, float x1, float y1, float x2, float y2) {
Paint paint = getGradient(name, x1, y1, x2, y2);
if (g instanceof PGraphicsJava2D) {
PGraphicsJava2D p2d = (PGraphicsJava2D) g;
p2d.strokeGradient = true;
p2d.strokeGradientObject = paint;
}
}
protected void fillGradient(PGraphics g, String name, float x, float y, float r) {
Paint paint = getGradient(name, x, y, r);
if (g instanceof PGraphicsJava2D) {
PGraphicsJava2D p2d = (PGraphicsJava2D) g;
p2d.fillGradient = true;
p2d.fillGradientObject = paint;
}
}
protected void fillGradient(PGraphics g, String name, float x1, float y1, float x2, float y2) {
Paint paint = getGradient(name, x1, y1, x2, y2);
if (g instanceof PGraphicsJava2D) {
PGraphicsJava2D p2d = (PGraphicsJava2D) g;
p2d.fillGradient = true;
p2d.fillGradientObject = paint;
}
}
*/
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
@@ -1180,7 +1146,13 @@ public class PShapeSVG extends PShape {
XMLElement elem = elements[i];
String name = elem.getName();
if (name.equals("stop")) {
offset[count] = elem.getFloatAttribute("offset");
String offsetAttr = elem.getStringAttribute("offset");
float div = 1.0f;
if (offsetAttr.endsWith("%")) {
div = 100.0f;
offsetAttr = offsetAttr.substring(0, offsetAttr.length() - 1);
}
offset[count] = PApplet.parseFloat(offsetAttr) / div;
String style = elem.getStringAttribute("style");
HashMap<String, String> styles = parseStyleAttributes(style);
@@ -1206,10 +1178,10 @@ public class PShapeSVG extends PShape {
public LinearGradient(PShapeSVG parent, XMLElement properties) {
super(parent, properties);
this.x1 = properties.getFloatAttribute("x1");
this.y1 = properties.getFloatAttribute("y1");
this.x2 = properties.getFloatAttribute("x2");
this.y2 = properties.getFloatAttribute("y2");
this.x1 = getFloatWithUnit(properties, "x1");
this.y1 = getFloatWithUnit(properties, "y1");
this.x2 = getFloatWithUnit(properties, "x2");
this.y2 = getFloatWithUnit(properties, "y2");
String transformStr =
properties.getStringAttribute("gradientTransform");
@@ -1250,9 +1222,9 @@ public class PShapeSVG extends PShape {
public RadialGradient(PShapeSVG parent, XMLElement properties) {
super(parent, properties);
this.cx = properties.getFloatAttribute("cx");
this.cy = properties.getFloatAttribute("cy");
this.r = properties.getFloatAttribute("r");
this.cx = getFloatWithUnit(properties, "cx");
this.cy = getFloatWithUnit(properties, "cy");
this.r = getFloatWithUnit(properties, "r");
String transformStr =
properties.getStringAttribute("gradientTransform");
@@ -1589,7 +1561,7 @@ public class PShapeSVG extends PShape {
* beneath them can be used here.
* <PRE>
* // This code grabs "Layer 3" and the shapes beneath it.
* SVG layer3 = svg.getChild("Layer 3");
* PShape layer3 = svg.getChild("Layer 3");
* </PRE>
*/
public PShape getChild(String name) {