diff --git a/build/shared/tools/howto.txt b/build/shared/tools/howto.txt index 1dde0ae3a..c7e905186 100644 --- a/build/shared/tools/howto.txt +++ b/build/shared/tools/howto.txt @@ -80,7 +80,7 @@ The naming of jar and zip files in the tool/* directory doesn't matter. When Processing loads, the jar and zip files will be searched for Mangler.class. Even though this tool is found in package poos.shoe, -it will be sussed out. Package names are encouraged/recommended/required. +it will be sussed out. Package names are required. Loose .class files are not supported, use only jar and zip files. diff --git a/candy/.classpath b/candy/.classpath index 6afd8691b..339bc47d7 100644 --- a/candy/.classpath +++ b/candy/.classpath @@ -1,6 +1,6 @@ - + diff --git a/candy/.settings/org.eclipse.jdt.core.prefs b/candy/.settings/org.eclipse.jdt.core.prefs index 35f78d435..f5f97c9fc 100644 --- a/candy/.settings/org.eclipse.jdt.core.prefs +++ b/candy/.settings/org.eclipse.jdt.core.prefs @@ -1,12 +1,12 @@ -#Sat Nov 11 10:35:15 EST 2006 +#Thu Aug 28 18:42:23 EDT 2008 eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5 org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=1.3 +org.eclipse.jdt.core.compiler.compliance=1.5 org.eclipse.jdt.core.compiler.debug.lineNumber=generate org.eclipse.jdt.core.compiler.debug.localVariable=generate org.eclipse.jdt.core.compiler.debug.sourceFile=generate -org.eclipse.jdt.core.compiler.problem.assertIdentifier=ignore -org.eclipse.jdt.core.compiler.problem.enumIdentifier=ignore -org.eclipse.jdt.core.compiler.source=1.3 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.5 diff --git a/candy/.settings/org.eclipse.jdt.ui.prefs b/candy/.settings/org.eclipse.jdt.ui.prefs deleted file mode 100644 index 03b94fb20..000000000 --- a/candy/.settings/org.eclipse.jdt.ui.prefs +++ /dev/null @@ -1,3 +0,0 @@ -#Sat Nov 11 10:35:15 EST 2006 -eclipse.preferences.version=1 -internal.default.compliance=default diff --git a/candy/src/processing/candy/BaseObject.java b/candy/src/processing/candy/BaseObject.java new file mode 100644 index 000000000..a3d5af7ab --- /dev/null +++ b/candy/src/processing/candy/BaseObject.java @@ -0,0 +1,609 @@ +package processing.candy; + +import java.awt.Paint; +import java.util.HashMap; + +import processing.core.*; +import processing.xml.XMLElement; + + +public class BaseObject extends PShape { + XMLElement element; + + float opacity; + + Gradient strokeGradient; + Paint strokeGradientPaint; + String strokeName; // id of another object, gradients only? + + Gradient fillGradient; + Paint fillGradientPaint; + String fillName; // id of another object + + + + public BaseObject(BaseObject parent, XMLElement properties) { + //super(GROUP); + + if (parent == null) { + // set values to their defaults according to the SVG spec + stroke = false; + strokeColor = 0xff000000; + strokeWeight = 1; + strokeCap = PConstants.SQUARE; // equivalent to BUTT in svg spec + strokeJoin = PConstants.MITER; + strokeGradient = null; + strokeGradientPaint = null; + strokeName = null; + + fill = true; + fillColor = 0xff000000; + fillGradient = null; + fillGradientPaint = null; + fillName = null; + + //hasTransform = false; + //transformation = null; //new float[] { 1, 0, 0, 1, 0, 0 }; + + opacity = 1; + + } else { + stroke = parent.stroke; + strokeColor = parent.strokeColor; + strokeWeight = parent.strokeWeight; + strokeCap = parent.strokeCap; + strokeJoin = parent.strokeJoin; + strokeGradient = parent.strokeGradient; + strokeGradientPaint = parent.strokeGradientPaint; + strokeName = parent.strokeName; + + fill = parent.fill; + fillColor = parent.fillColor; + fillGradient = parent.fillGradient; + fillGradientPaint = parent.fillGradientPaint; + fillName = parent.fillName; + + //hasTransform = parent.hasTransform; + //transformation = parent.transformation; + + opacity = parent.opacity; + } + + element = properties; + + name = properties.getStringAttribute("id"); + if (name != null) { + table.put(name, this); + //System.out.println("now parsing " + id); + } + + String displayStr = properties.getStringAttribute("display", "inline"); + visible = !displayStr.equals("none"); + + String transformStr = properties.getStringAttribute("transform"); + if (transformStr != null) { + float[] t = parseMatrix(transformStr); + matrix = new PMatrix3D(t[0], t[1], t[2], t[3], t[4], t[5]); + } + + parseColors(properties); + } + + + + // http://www.w3.org/TR/SVG/coords.html#TransformAttribute + static protected float[] parseMatrix(String matrixStr) { + /* + String prefix = "matrix("; + int start = matrixStr.indexOf(prefix); + if (start == -1) return null; + int stop = matrixStr.indexOf(')'); + String content = matrixStr.substring(start + matrixStr.length(), stop); + return PApplet.parseFloat(PApplet.splitTokens(content.trim())); + */ + String[] pieces = PApplet.match(matrixStr, "\\s*(\\w+)\\((.*)\\)"); + if (pieces.length != 2) { + System.err.println("Could not parse transform " + matrixStr); + return null; + } + float[] m = PApplet.parseFloat(PApplet.splitTokens(pieces[1])); + + if (pieces[0].equals("matrix")) { + return m; + + } else if (pieces[0].equals("translate")) { + float tx = m[0]; + float ty = (m.length == 2) ? m[1] : m[0]; + return new float[] { 1, 0, tx, 0, 1, ty }; + + } else if (pieces[0].equals("scale")) { + float sx = m[0]; + float sy = (m.length == 2) ? m[1] : m[0]; + return new float[] { sx, 0, 0, 0, sy, 0 }; + + } else if (pieces[0].equals("rotate")) { + float angle = m[0]; + + if (m.length == 1) { + float c = PApplet.cos(angle); + float s = PApplet.sin(angle); + return new float[] { c, -s, 0, s, c, 0 }; + + } else if (m.length == 3) { + PMatrix2D mat = new PMatrix2D(0, 1, m[1], 1, 0, m[2]); + mat.rotate(m[0]); + mat.translate(-m[1], -m[2]); + return mat.get(null); + } + + } else if (pieces[0].equals("skewX")) { + return new float[] { 1, PApplet.tan(m[0]), 0, 0, 1, 0 }; + + } else if (pieces[0].equals("skewY")) { + return new float[] { 1, 0, 0, PApplet.tan(m[0]), 1, 0 }; + } + return null; + } + + + /* + protected void parseTransformation2(XMLElement properties) { + String transform = + if (transform != null) { + //this.hasTransform = true; + transform = transform.substring(7, transform.length() - 2); + String tf[] = PApplet.splitTokens(transform); + float[] transformation = PApplet.parseFloat(tf); + matrix = new PMatrix3D(transformation[0], transformation[1], transformation[2], + transformation[3], transformation[4], transformation[5]); + } + } + */ + + + /* + static protected AffineTransform parseTransform(String what) { + if (what != null) { + if (what.startsWith("matrix(") && what.endsWith(")")) { + // columns go first with AT constructor + what = what.substring(7, what.length() - 1); + return new AffineTransform(PApplet.parseFloat(PApplet.split(what, ' '))); + } + } + return null; + } + */ + + + protected void parseColors(XMLElement properties) { + if (properties.hasAttribute("opacity")) { + opacity = properties.getFloatAttribute("opacity"); + } + int opacityMask = ((int) (opacity * 255)) << 24; + + if (properties.hasAttribute("stroke")) { + String strokeText = properties.getStringAttribute("stroke"); + 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 = table.get(strokeName); + if (strokeObject instanceof Gradient) { + strokeGradient = (Gradient) strokeObject; + strokeGradientPaint = calcGradientPaint(strokeGradient); //, opacity); + } else { + System.err.println("url " + strokeName + " refers to unexpected data"); + } + } + } + + if (properties.hasAttribute("stroke-width")) { + // if NaN (i.e. if it's 'inherit') then default back to the inherit setting + strokeWeight = properties.getFloatAttribute("stroke-width", strokeWeight); + } + + if (properties.hasAttribute("stroke-linejoin")) { + String linejoin = properties.getStringAttribute("stroke-linejoin"); + if (linejoin.equals("inherit")) { + // do nothing, will inherit automatically + + } else if (linejoin.equals("miter")) { + strokeJoin = PConstants.MITER; + + } else if (linejoin.equals("round")) { + strokeJoin = PConstants.ROUND; + + } else if (linejoin.equals("bevel")) { + strokeJoin = PConstants.BEVEL; + } + } + + if (properties.hasAttribute("stroke-linecap")) { + String linecap = properties.getStringAttribute("stroke-linecap"); + if (linecap.equals("inherit")) { + // do nothing, will inherit automatically + + } else if (linecap.equals("butt")) { + strokeCap = PConstants.SQUARE; + + } else if (linecap.equals("round")) { + strokeCap = PConstants.ROUND; + + } else if (linecap.equals("square")) { + strokeCap = PConstants.PROJECT; + } + } + + // 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"); + if (fillText.equals("none")) { + fill = false; + } else if (fillText.startsWith("#")) { + fill = true; + fillColor = opacityMask | + (Integer.parseInt(fillText.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 = table.get(fillName); + //PApplet.println("found " + fillObject); + if (fillObject instanceof Gradient) { + fill = true; + fillGradient = (Gradient) fillObject; + fillGradientPaint = calcGradientPaint(fillGradient); //, opacity); + //PApplet.println("got filla " + fillObject); + } else { + System.err.println("url " + fillName + " refers to unexpected data"); + } + } + } + } + + + static protected int parseRGB(String what) { + int leftParen = what.indexOf('(') + 1; + int rightParen = what.indexOf(')'); + String sub = what.substring(leftParen, rightParen); + int[] values = PApplet.parseInt(PApplet.splitTokens(sub, ", ")); + return (values[0] << 16) | (values[1] << 8) | (values[2]); + } + + + static protected HashMap parseStyleAttributes(String style) { + HashMap table = new HashMap(); + String[] pieces = style.split(";"); + for (int i = 0; i < pieces.length; i++) { + String[] parts = pieces[i].split(":"); + table.put(parts[0], parts[1]); + } + return table; + } + + + /** + * Parse a size that may have a suffix for its units. + * Ignoring cases where this could also be a percentage. + * The units spec: + *
    + *
  • "1pt" equals "1.25px" (and therefore 1.25 user units) + *
  • "1pc" equals "15px" (and therefore 15 user units) + *
  • "1mm" would be "3.543307px" (3.543307 user units) + *
  • "1cm" equals "35.43307px" (and therefore 35.43307 user units) + *
  • "1in" equals "90px" (and therefore 90 user units) + *
+ */ + static protected float parseUnitSize(String text) { + int len = text.length() - 2; + + if (text.endsWith("pt")) { + return PApplet.parseFloat(text.substring(0, len)) * 1.25f; + } else if (text.endsWith("pc")) { + return PApplet.parseFloat(text.substring(0, len)) * 15; + } else if (text.endsWith("mm")) { + return PApplet.parseFloat(text.substring(0, len)) * 3.543307f; + } else if (text.endsWith("cm")) { + return PApplet.parseFloat(text.substring(0, len)) * 35.43307f; + } else if (text.endsWith("in")) { + return PApplet.parseFloat(text.substring(0, len)) * 90; + } else if (text.endsWith("px")) { + return PApplet.parseFloat(text.substring(0, len)); + } else { + return PApplet.parseFloat(text); + } + } + + + // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + + + // 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; + } + } + */ + + + // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + + + protected Paint calcGradientPaint(Gradient gradient) { + if (gradient instanceof LinearGradient) { + LinearGradient grad = (LinearGradient) gradient; + return new LinearGradientPaint(grad.x1, grad.y1, grad.x2, grad.y2, + grad.offset, grad.color, grad.count, + opacity); + + } else if (gradient instanceof RadialGradient) { + RadialGradient grad = (RadialGradient) gradient; + return new RadialGradientPaint(grad.cx, grad.cy, grad.r, + grad.offset, grad.color, grad.count, + opacity); + } + return null; + } + + + protected Paint calcGradientPaint(Gradient gradient, + float x1, float y1, float x2, float y2) { + if (gradient instanceof LinearGradient) { + LinearGradient grad = (LinearGradient) gradient; + return new LinearGradientPaint(x1, y1, x2, y2, + grad.offset, grad.color, grad.count, + opacity); + } + throw new RuntimeException("Not a linear gradient."); + } + + + protected Paint calcGradientPaint(Gradient gradient, + float cx, float cy, float r) { + if (gradient instanceof RadialGradient) { + RadialGradient grad = (RadialGradient) gradient; + return new RadialGradientPaint(cx, cy, r, + grad.offset, grad.color, grad.count, + opacity); + } + throw new RuntimeException("Not a radial gradient."); + } + + + // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + + + protected void styles(PGraphics g) { + super.styles(g); + + if (g instanceof PGraphicsJava2D) { + PGraphicsJava2D p2d = (PGraphicsJava2D) g; + + if (strokeGradient != null) { + p2d.strokeGradient = true; + p2d.strokeGradientObject = strokeGradientPaint; + } else { + // need to shut off, in case parent object has a gradient applied + //p2d.strokeGradient = false; + } + if (fillGradient != null) { + p2d.fillGradient = true; + p2d.fillGradientObject = fillGradientPaint; + } else { + // need to shut off, in case parent object has a gradient applied + //p2d.fillGradient = false; + } + } + } + + + /** + * Overrides SVG-set styles and uses PGraphics styles and colors. + * Identical to ignoreStyles(true). + */ + public void ignoreStyles() { + //ignoreStyles(true); + styles = false; + } + + + /** + * Enables or disables style information (fill and stroke) set in the file. + * @param state true to use user-specified stroke/fill, false for svg version + */ + public void ignoreStyles(boolean state) { + //ignoreStyles = state; + styles = !state; + } + + + // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + + + public void drawImpl(PGraphics g) { + // do nothing + } + + + // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + + + /** + * Get a particular element based on its SVG ID. When editing SVG by hand, + * this is the id="" tag on any SVG element. When editing from Illustrator, + * these IDs can be edited by expanding the layers palette. The names used + * in the layers palette, both for the layers or the shapes and groups + * beneath them can be used here. + *
+	 * // This code grabs "Layer 3" and the shapes beneath it.
+	 * SVG layer3 = svg.get("Layer 3");
+	 * 
+ */ + public PShape getChild(String name) { + PShape found = super.getChild(name); + if (found != null) return found; + // otherwise try with underscores instead of spaces + return super.getChild(name.replace(' ', '_')); + } + + + protected void parseGroup(XMLElement graphics) { + XMLElement[] elements = graphics.getChildren(); + //objects = new BaseObject[elements.length]; + children = new PShape[elements.length]; + childCount = 0; + + for (XMLElement elem : elements) { + addChild(parseGroupChild(elem)); + } + + // for (int i = 0; i < elements.length; i++) { + // String name = elements[i].getName(); //getElement(); + // XMLElement elem = elements[i]; + // + // addChild(parseGroupChild(elem, name)); + // } + } + + + protected PShape parseGroupChild(XMLElement elem) { + String name = elem.getName(); + + if (name.equals("g")) { + return new BaseObject(this, elem); + + } else if (name.equals("defs")) { + // generally this will contain gradient info, so may + // as well just throw it into a group element for parsing + return new BaseObject(this, elem); + + } else if (name.equals("line")) { + return new Line(this, elem); + + } else if (name.equals("circle")) { + return new Circle(this, elem); + + } else if (name.equals("ellipse")) { + return new Ellipse(this, elem); + + } else if (name.equals("rect")) { + return new Rect(this, elem); + + } else if (name.equals("polygon")) { + return new Poly(this, elem, true); + + } else if (name.equals("polyline")) { + return new Poly(this, elem, false); + + } else if (name.equals("path")) { + return new Path(this, elem); + + } else if (name.equals("radialGradient")) { + return new RadialGradient(this, elem); + + } else if (name.equals("linearGradient")) { + return new LinearGradient(this, elem); + + } else if (name.equals("text")) { + PApplet.println("Text is not currently handled, " + + "convert text to outlines instead."); + + } else if (name.equals("filter")) { + PApplet.println("Filters are not supported."); + + } else if (name.equals("mask")) { + PApplet.println("Masks are not supported."); + + } else { + System.err.println("Ignoring <" + name + "> tag."); + } + return null; + } + + + /** + * Prints out the SVG document useful for parsing + */ + public void print() { + PApplet.println(element.toString()); + } +} diff --git a/candy/src/processing/candy/Circle.java b/candy/src/processing/candy/Circle.java new file mode 100644 index 000000000..a075c0a42 --- /dev/null +++ b/candy/src/processing/candy/Circle.java @@ -0,0 +1,23 @@ +package processing.candy; + +import processing.core.*; +import processing.xml.XMLElement; + + +public class Circle extends BaseObject { + //float x, y, radius; + + public Circle(BaseObject parent, XMLElement properties) { + super(parent, properties); + this.x = properties.getFloatAttribute("cx"); + this.y = properties.getFloatAttribute("cy"); + float radius = properties.getFloatAttribute("r") * 2; + width = height = radius; + } + + + public void drawImpl(PGraphics g) { + g.ellipseMode(PConstants.CENTER); + g.ellipse(x, y, width, height); + } +} diff --git a/candy/src/processing/candy/Ellipse.java b/candy/src/processing/candy/Ellipse.java new file mode 100644 index 000000000..ac960bbb8 --- /dev/null +++ b/candy/src/processing/candy/Ellipse.java @@ -0,0 +1,32 @@ +package processing.candy; + +import processing.core.*; +import processing.xml.XMLElement; + + +public class Ellipse extends BaseObject { +// float x, y, rx, ry; + + public Ellipse(BaseObject parent, XMLElement properties) { + super(parent, properties); + + x = properties.getFloatAttribute("cx"); + y = properties.getFloatAttribute("cy"); + + float rx = properties.getFloatAttribute("rx"); + float ry = properties.getFloatAttribute("ry"); + + x -= rx; + y -= ry; + + width = rx * 2; + height = ry * 2; + } + + public void drawImpl(PGraphics g) { + //g.ellipseMode(PConstants.CENTER); + //g.ellipse(x, y, rx, ry); + g.ellipseMode(PConstants.CORNERS); + g.ellipse(x, y, width, height); + } +} \ No newline at end of file diff --git a/candy/src/processing/candy/Gradient.java b/candy/src/processing/candy/Gradient.java new file mode 100644 index 000000000..092d6c70c --- /dev/null +++ b/candy/src/processing/candy/Gradient.java @@ -0,0 +1,59 @@ +package processing.candy; + +import java.awt.geom.AffineTransform; +import java.util.HashMap; + +import processing.core.PApplet; +import processing.core.PGraphics; +import processing.xml.XMLElement; + + +abstract public class Gradient extends BaseObject { + AffineTransform transform; + + float[] offset; + int[] color; + int count; + + public Gradient(BaseObject parent, XMLElement properties) { + super(parent, properties); + + XMLElement elements[] = properties.getChildren(); + offset = new float[elements.length]; + color = new int[elements.length]; + + // + for (int i = 0; i < elements.length; i++) { + XMLElement elem = elements[i]; + String name = elem.getName(); + if (name.equals("stop")) { + offset[count] = elem.getFloatAttribute("offset"); + String style = elem.getStringAttribute("style"); + HashMap styles = parseStyleAttributes(style); + + String colorStr = styles.get("stop-color"); + if (colorStr == null) colorStr = "#000000"; + String opacityStr = styles.get("stop-opacity"); + if (opacityStr == null) opacityStr = "1"; + int tupacity = (int) (PApplet.parseFloat(opacityStr) * 255); + color[count] = (tupacity << 24) | + Integer.parseInt(colorStr.substring(1), 16); + count++; + //System.out.println("this color is " + PApplet.hex(color[count])); + /* + int idx = farbe.indexOf("#"); + if (idx != -1) { + color[count] = Integer.parseInt(farbe.substring(idx+1), 16); + count++; + } else { + System.err.println("problem with gradient stop " + properties); + } + */ + } + } + } + + abstract public void drawImpl(PGraphics g); +} + + diff --git a/candy/src/processing/candy/Line.java b/candy/src/processing/candy/Line.java new file mode 100644 index 000000000..135b11f0b --- /dev/null +++ b/candy/src/processing/candy/Line.java @@ -0,0 +1,25 @@ +package processing.candy; + +import processing.core.PGraphics; +import processing.xml.XMLElement; + + +public class Line extends BaseObject { + + float x1, y1, x2, y2; + + public Line(BaseObject parent, XMLElement properties) { + super(parent, properties); + + kind = LINES; + + this.x1 = properties.getFloatAttribute("x1"); + this.y1 = properties.getFloatAttribute("y1"); + this.x2 = properties.getFloatAttribute("x2"); + this.y2 = properties.getFloatAttribute("y2"); + } + + public void drawImpl(PGraphics g) { + g.line(x1, y1, x2, y2); + } +} \ No newline at end of file diff --git a/candy/src/processing/candy/LinearGradient.java b/candy/src/processing/candy/LinearGradient.java new file mode 100644 index 000000000..9ea5e2091 --- /dev/null +++ b/candy/src/processing/candy/LinearGradient.java @@ -0,0 +1,41 @@ +package processing.candy; + +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; + +import processing.core.PGraphics; +import processing.xml.XMLElement; + + +public class LinearGradient extends Gradient { + float x1, y1, x2, y2; + + public LinearGradient(BaseObject 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"); + + String transformStr = + properties.getStringAttribute("gradientTransform"); + + if (transformStr != null) { + this.transform = new AffineTransform(parseMatrix(transformStr)); + + Point2D t1 = transform.transform(new Point2D.Float(x1, y1), null); + Point2D t2 = transform.transform(new Point2D.Float(x2, y2), null); + + this.x1 = (float) t1.getX(); + this.y1 = (float) t1.getY(); + this.x2 = (float) t2.getX(); + this.y2 = (float) t2.getY(); + + } + } + + public void drawImpl(PGraphics g) { } +} + + diff --git a/candy/src/processing/candy/Path.java b/candy/src/processing/candy/Path.java new file mode 100644 index 000000000..dc36a21f0 --- /dev/null +++ b/candy/src/processing/candy/Path.java @@ -0,0 +1,416 @@ +package processing.candy; + +import processing.core.*; +import processing.core.PConstants; +import processing.core.PGraphics; +import processing.xml.XMLElement; + + +public class Path extends BaseObject { + + public int count = 0; + public float[] x = new float[4]; + public float[] y = new float[4]; + + static public final int MOVETO = 0; + static public final int LINETO = 1; + static public final int CURVETO = 2; + static public final int QCURVETO = 3; + public int[] kind = new int[4]; + + public boolean closed = false; + + + public Path(BaseObject parent, XMLElement properties) { + super(parent, properties); + String pathDataBuffer = ""; + + if (!properties.hasAttribute("d")) + return; + + pathDataBuffer = properties.getStringAttribute("d"); + StringBuffer pathChars = new StringBuffer(); + + boolean lastSeparate = false; + + for (int i = 0; i < pathDataBuffer.length(); i++) { + char c = pathDataBuffer.charAt(i); + boolean separate = false; + + if (c == 'M' || c == 'm' || + c == 'L' || c == 'l' || + c == 'H' || c == 'h' || + c == 'V' || c == 'v' || + c == 'C' || c == 'c' || // beziers + c == 'S' || c == 's' || + c == 'Q' || c == 'q' || // quadratic beziers + c == 'T' || c == 't' || + c == 'Z' || c == 'z' || // closepath + c == ',') { + separate = true; + if (i != 0) { + pathChars.append("|"); + } + } + if (c == 'Z' || c == 'z') { + separate = false; + } + if (c == '-' && !lastSeparate) { + pathChars.append("|"); + } + if (c != ',') { + pathChars.append("" + pathDataBuffer.charAt(i)); + } + if (separate && c != ',' && c != '-') { + pathChars.append("|"); + } + lastSeparate = separate; + } + + pathDataBuffer = pathChars.toString(); + + //String pathDataKeys[] = PApplet.split(pathDataBuffer, '|'); + // use whitespace constant to get rid of extra spaces and CR or LF + String pathDataKeys[] = + PApplet.splitTokens(pathDataBuffer, "|" + PConstants.WHITESPACE); + //for (int j = 0; j < pathDataKeys.length; j++) { + // PApplet.println(j + "\t" + pathDataKeys[j]); + //} + //PApplet.println(pathDataKeys); + //PApplet.println(); + + //float cp[] = {0, 0}; + float cx = 0; + float cy = 0; + + int i = 0; + //for (int i = 0; i < pathDataKeys.length; i++) { + while (i < pathDataKeys.length) { + char c = pathDataKeys[i].charAt(0); + switch (c) { + + //M - move to (absolute) + case 'M': + /* + cp[0] = PApplet.parseFloat(pathDataKeys[i + 1]); + cp[1] = PApplet.parseFloat(pathDataKeys[i + 2]); + float s[] = {cp[0], cp[1]}; + i += 2; + points.add(s); + */ + cx = PApplet.parseFloat(pathDataKeys[i + 1]); + cy = PApplet.parseFloat(pathDataKeys[i + 2]); + moveto(cx, cy); + i += 3; + break; + + + //m - move to (relative) + case 'm': + /* + cp[0] = cp[0] + PApplet.parseFloat(pathDataKeys[i + 1]); + cp[1] = cp[1] + PApplet.parseFloat(pathDataKeys[i + 2]); + float s[] = {cp[0], cp[1]}; + i += 2; + points.add(s); + */ + cx = cx + PApplet.parseFloat(pathDataKeys[i + 1]); + cy = cy + PApplet.parseFloat(pathDataKeys[i + 2]); + moveto(cx, cy); + i += 3; + break; + + + case 'L': + cx = PApplet.parseFloat(pathDataKeys[i + 1]); + cy = PApplet.parseFloat(pathDataKeys[i + 2]); + lineto(cx, cy); + i += 3; + break; + + + case 'l': + cx = cx + PApplet.parseFloat(pathDataKeys[i + 1]); + cy = cy + PApplet.parseFloat(pathDataKeys[i + 2]); + lineto(cx, cy); + i += 3; + break; + + + // horizontal lineto absolute + case 'H': + cx = PApplet.parseFloat(pathDataKeys[i + 1]); + lineto(cx, cy); + i += 2; + break; + + + // horizontal lineto relative + case 'h': + cx = cx + PApplet.parseFloat(pathDataKeys[i + 1]); + lineto(cx, cy); + i += 2; + break; + + + case 'V': + cy = PApplet.parseFloat(pathDataKeys[i + 1]); + lineto(cx, cy); + i += 2; + break; + + + case 'v': + cy = cy + PApplet.parseFloat(pathDataKeys[i + 1]); + lineto(cx, cy); + i += 2; + break; + + + // C - curve to (absolute) + case 'C': { + float ctrlX1 = PApplet.parseFloat(pathDataKeys[i + 1]); + float ctrlY1 = PApplet.parseFloat(pathDataKeys[i + 2]); + float ctrlX2 = PApplet.parseFloat(pathDataKeys[i + 3]); + float ctrlY2 = PApplet.parseFloat(pathDataKeys[i + 4]); + float endX = PApplet.parseFloat(pathDataKeys[i + 5]); + float endY = PApplet.parseFloat(pathDataKeys[i + 6]); + curveto(ctrlX1, ctrlY1, ctrlX2, ctrlY2, endX, endY); + cx = endX; + cy = endY; + i += 7; + } + break; + + // c - curve to (relative) + case 'c': { + float ctrlX1 = cx + PApplet.parseFloat(pathDataKeys[i + 1]); + float ctrlY1 = cy + PApplet.parseFloat(pathDataKeys[i + 2]); + float ctrlX2 = cx + PApplet.parseFloat(pathDataKeys[i + 3]); + float ctrlY2 = cy + PApplet.parseFloat(pathDataKeys[i + 4]); + float endX = cx + PApplet.parseFloat(pathDataKeys[i + 5]); + float endY = cy + PApplet.parseFloat(pathDataKeys[i + 6]); + curveto(ctrlX1, ctrlY1, ctrlX2, ctrlY2, endX, endY); + cx = endX; + cy = endY; + i += 7; + } + break; + + // S - curve to shorthand (absolute) + case 'S': { + float ppx = x[count-2]; + float ppy = y[count-2]; + float px = x[count-1]; + float py = y[count-1]; + float ctrlX1 = px + (px - ppx); + float ctrlY1 = py + (py - ppy); + float ctrlX2 = PApplet.parseFloat(pathDataKeys[i + 1]); + float ctrlY2 = PApplet.parseFloat(pathDataKeys[i + 2]); + float endX = PApplet.parseFloat(pathDataKeys[i + 3]); + float endY = PApplet.parseFloat(pathDataKeys[i + 4]); + curveto(ctrlX1, ctrlY1, ctrlX2, ctrlY2, endX, endY); + cx = endX; + cy = endY; + i += 5; + } + break; + + // s - curve to shorthand (relative) + case 's': { + float ppx = x[count-2]; + float ppy = y[count-2]; + float px = x[count-1]; + float py = y[count-1]; + float ctrlX1 = px + (px - ppx); + float ctrlY1 = py + (py - ppy); + float ctrlX2 = cx + PApplet.parseFloat(pathDataKeys[i + 1]); + float ctrlY2 = cy + PApplet.parseFloat(pathDataKeys[i + 2]); + float endX = cx + PApplet.parseFloat(pathDataKeys[i + 3]); + float endY = cy + PApplet.parseFloat(pathDataKeys[i + 4]); + curveto(ctrlX1, ctrlY1, ctrlX2, ctrlY2, endX, endY); + cx = endX; + cy = endY; + i += 5; + } + break; + + // Q - quadratic curve to (absolute) + case 'Q': { + float ctrlX = PApplet.parseFloat(pathDataKeys[i + 1]); + float ctrlY = PApplet.parseFloat(pathDataKeys[i + 2]); + float endX = PApplet.parseFloat(pathDataKeys[i + 3]); + float endY = PApplet.parseFloat(pathDataKeys[i + 4]); + curveto(ctrlX, ctrlY, endX, endY); + cx = endX; + cy = endY; + i += 5; + } + break; + + // q - quadratic curve to (relative) + case 'q': { + float ctrlX = cx + PApplet.parseFloat(pathDataKeys[i + 1]); + float ctrlY = cy + PApplet.parseFloat(pathDataKeys[i + 2]); + float endX = cx + PApplet.parseFloat(pathDataKeys[i + 3]); + float endY = cy + PApplet.parseFloat(pathDataKeys[i + 4]); + curveto(ctrlX, ctrlY, endX, endY); + cx = endX; + cy = endY; + i += 5; + } + break; + + // T - quadratic curve to shorthand (absolute) + // The control point is assumed to be the reflection of the + // control point on the previous command relative to the + // current point. (If there is no previous command or if the + // previous command was not a Q, q, T or t, assume the control + // point is coincident with the current point.) + case 'T': { + float ppx = x[count-2]; + float ppy = y[count-2]; + float px = x[count-1]; + float py = y[count-1]; + float ctrlX = px + (px - ppx); + float ctrlY = py + (py - ppy); + float endX = PApplet.parseFloat(pathDataKeys[i + 1]); + float endY = PApplet.parseFloat(pathDataKeys[i + 2]); + curveto(ctrlX, ctrlY, endX, endY); + cx = endX; + cy = endY; + i += 3; + } + break; + + // t - quadratic curve to shorthand (relative) + case 't': { + float ppx = x[count-2]; + float ppy = y[count-2]; + float px = x[count-1]; + float py = y[count-1]; + float ctrlX = px + (px - ppx); + float ctrlY = py + (py - ppy); + float endX = cx + PApplet.parseFloat(pathDataKeys[i + 1]); + float endY = cy + PApplet.parseFloat(pathDataKeys[i + 2]); + curveto(ctrlX, ctrlY, endX, endY); + cx = endX; + cy = endY; + i += 3; + } + break; + + case 'Z': + case 'z': + closed = true; + i++; + break; + + default: + String parsed = + PApplet.join(PApplet.subset(pathDataKeys, 0, i), ","); + String unparsed = + PApplet.join(PApplet.subset(pathDataKeys, i), ","); + System.err.println("parsed: " + parsed); + System.err.println("unparsed: " + unparsed); + throw new RuntimeException("shape command not handled: " + pathDataKeys[i]); + } + } + } + + + protected void moveto(float px, float py) { + if (count == x.length) { + x = PApplet.expand(x); + y = PApplet.expand(y); + kind = PApplet.expand(kind); + } + kind[count] = MOVETO; + x[count] = px; + y[count] = py; + count++; + } + + + protected void lineto(float px, float py) { + if (count == x.length) { + x = PApplet.expand(x); + y = PApplet.expand(y); + kind = PApplet.expand(kind); + } + kind[count] = LINETO; + x[count] = px; + y[count] = py; + count++; + } + + + /** Quadratic curveto command. */ + protected void curveto(float x1, float y1, float x2, float y2) { + if (count + 2 >= x.length) { + x = PApplet.expand(x); + y = PApplet.expand(y); + kind = PApplet.expand(kind); + } + kind[count] = QCURVETO; + x[count] = x1; + y[count] = y1; + count++; + x[count] = x2; + y[count] = y2; + count++; + } + + + /** Cubic curveto command. */ + protected void curveto(float x1, float y1, float x2, float y2, float x3, float y3) { + if (count + 2 >= x.length) { + x = PApplet.expand(x); + y = PApplet.expand(y); + kind = PApplet.expand(kind); + } + kind[count] = CURVETO; + x[count] = x1; + y[count] = y1; + count++; + x[count] = x2; + y[count] = y2; + count++; + x[count] = x3; + y[count] = y3; + count++; + } + + + public void drawImpl(PGraphics g) { + g.beginShape(); + + g.vertex(x[0], y[0]); + int i = 1; // moveto has the first point + while (i < count) { + switch (kind[i]) { + case MOVETO: + g.breakShape(); + g.vertex(x[i], y[i]); + i++; + break; + + case LINETO: + g.vertex(x[i], y[i]); + i++; + break; + + case QCURVETO: // doubles the control point + g.bezierVertex(x[i], y[i], x[i+1], y[i+1], x[i+1], y[i+1]); + i += 2; + break; + + case CURVETO: + g.bezierVertex(x[i], y[i], x[i+1], y[i+1], x[i+2], y[i+2]); + i += 3; + break; + } + } + g.endShape(closed ? PConstants.CLOSE : PConstants.OPEN); + } +} \ No newline at end of file diff --git a/candy/src/processing/candy/Poly.java b/candy/src/processing/candy/Poly.java new file mode 100644 index 000000000..c663612c8 --- /dev/null +++ b/candy/src/processing/candy/Poly.java @@ -0,0 +1,41 @@ +package processing.candy; + +import processing.core.*; +import processing.xml.XMLElement; + + +public class Poly extends BaseObject { + + float points[][] = null; + /** true if polygon, false if polyline */ + boolean closed; + + public Poly(BaseObject parent, XMLElement properties, boolean closed) { + super(parent, properties); + String pointsBuffer[] = null; + this.closed = closed; + + if (properties.hasAttribute("points")) { + pointsBuffer = PApplet.splitTokens(properties.getStringAttribute("points")); + } + + points = new float[pointsBuffer.length][2]; + for (int i = 0; i < points.length; i++) { + String pb[] = PApplet.split(pointsBuffer[i], ','); + points[i][0] = Float.valueOf(pb[0]).floatValue(); + points[i][1] = Float.valueOf(pb[1]).floatValue(); + } + } + + public void drawImpl(PGraphics g) { + if (points != null) { + if (points.length > 0) { + g.beginShape(); + for (int i = 0; i < points.length; i++) { + g.vertex(points[i][0], points[i][1]); + } + g.endShape(closed ? PConstants.CLOSE : PConstants.OPEN); + } + } + } +} \ No newline at end of file diff --git a/candy/src/processing/candy/RadialGradient.java b/candy/src/processing/candy/RadialGradient.java new file mode 100644 index 000000000..0609542d6 --- /dev/null +++ b/candy/src/processing/candy/RadialGradient.java @@ -0,0 +1,36 @@ +package processing.candy; + +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; + +import processing.core.PGraphics; +import processing.xml.XMLElement; + + +public class RadialGradient extends Gradient { + float cx, cy, r; + + public RadialGradient(BaseObject parent, XMLElement properties) { + super(parent, properties); + + this.cx = properties.getFloatAttribute("cx"); + this.cy = properties.getFloatAttribute("cy"); + this.r = properties.getFloatAttribute("r"); + + String transformStr = + properties.getStringAttribute("gradientTransform"); + + if (transformStr != null) { + this.transform = new AffineTransform(parseMatrix(transformStr)); + + Point2D t1 = transform.transform(new Point2D.Float(cx, cy), null); + Point2D t2 = transform.transform(new Point2D.Float(cx + r, cy), null); + + this.cx = (float) t1.getX(); + this.cy = (float) t1.getY(); + this.r = (float) (t2.getX() - t1.getX()); + } + } + + public void drawImpl(PGraphics g) { } +} diff --git a/candy/src/processing/candy/Rect.java b/candy/src/processing/candy/Rect.java new file mode 100644 index 000000000..bddc23312 --- /dev/null +++ b/candy/src/processing/candy/Rect.java @@ -0,0 +1,24 @@ +package processing.candy; + +import processing.core.*; +import processing.xml.XMLElement; + + +public class Rect extends BaseObject { +// float x, y, w, h; + + public Rect(BaseObject parent, XMLElement properties) { + super(parent, properties); + x = properties.getFloatAttribute("x"); + y = properties.getFloatAttribute("y"); + width = properties.getFloatAttribute("width"); + height = properties.getFloatAttribute("height"); + } + + public void drawImpl(PGraphics g) { + g.rectMode(PConstants.CORNER); + g.rect(x, y, width, height); + } +} + + diff --git a/candy/src/processing/candy/SVG.java b/candy/src/processing/candy/SVG.java index e8d449d3a..8dd56fc65 100755 --- a/candy/src/processing/candy/SVG.java +++ b/candy/src/processing/candy/SVG.java @@ -1,5 +1,5 @@ /* - Candy 2 - SVG Importer for Processing - http://processing.org + Candy 3 - SVG Importer for Processing - http://processing.org Copyright (c) 2006 Michael Chang (Flux) http://www.ghost-hack.com/ @@ -19,11 +19,6 @@ package processing.candy; -import java.awt.*; -import java.awt.geom.AffineTransform; -import java.awt.geom.Point2D; -import java.util.Hashtable; - import processing.core.*; import processing.xml.*; @@ -33,30 +28,30 @@ import processing.xml.*; * Candy was written by Michael Chang, and later revised and * expanded for use as a Processing core library by Ben Fry. *

- * SVG stands for Scalable Vector Graphics, a portable graphics - * format. It is a vector format so it allows for infinite resolution - * and relatively minute file sizes. Most modern media software - * can view SVG files, including Firefox, Adobe products, etc. - * You can use something like Illustrator to edit SVG files. + * SVG stands for Scalable Vector Graphics, a portable graphics format. It is + * a vector format so it allows for infinite resolution and relatively small + * file sizes. Most modern media software can view SVG files, including Adobe + * products, Firefox, etc. Illustrator and Inkscape can edit SVG files. *

* We have no intention of turning this into a full-featured SVG library. * The goal of this project is a basic shape importer that is small enough * to be included with applets, meaning that its download size should be * in the neighborhood of 25-30k. Because of this size, it is not made part - * of processing.core, because it would increase the download size of any - * applet by 20%, and it's not a feature that will be used by the majority - * of our audience. For more sophisticated import/export, consider the - * Batik library - * from the Apache Software Foundation. Future improvements to this - * library may focus on this properly supporting a specific subset of - * SVG, for instance the simpler SVG profiles known as + * of processing.core, as it's not a feature that will be used by the majority + * of our audience. + * + * For more sophisticated import/export, consider the + * Batik + * library from the Apache Software Foundation. Future improvements to this + * library may focus on this properly supporting a specific subset of SVG, + * for instance the simpler SVG profiles known as * SVG Tiny or Basic, * although we still would not support the interactivity options. *

* This library was specifically tested under SVG files created with Adobe * Illustrator. We can't guarantee that it will work for any SVGs created with * other software. In the future we would like to improve compatibility with - * Open Source software such as InkScape, however initial tests show its + * Open Source software such as Inkscape, however initial tests show its * base implementation produces more complicated files, and this will require * more time. *

@@ -96,6 +91,12 @@ import processing.xml.*; * *


* + * August 2008 revisions by fry (Processing 0149) + *

    + *
  • Major changes to rework around PShape. + *
  • Now implementing more of the "transform" attribute. + *
+ * * February 2008 revisions by fry (Processing 0136) *
    *
  • Added support for quadratic curves in paths (Q, q, T, and t operators) @@ -136,21 +137,7 @@ import processing.xml.*; * For those interested, the SVG specification can be found * here. */ -public class SVG { - - protected PApplet parent; - - public float width; - public float height; - - protected Hashtable table = new Hashtable(); - protected XMLElement svg; - protected BaseObject root; - - protected boolean ignoreStyles = false; - - int drawMode = PConstants.CORNER; - +public class SVG extends BaseObject { /** * Initializes a new SVG Object with the given filename. @@ -158,17 +145,16 @@ public class SVG { public SVG(PApplet parent, String filename) { // this will grab the root document, starting // the xml version and initial comments are ignored - this(parent, new XMLElement(parent, filename)); + this(new XMLElement(parent, filename)); } /** * Initializes a new SVG Object with the given filename. */ - public SVG(PApplet parent, XMLElement svg) { - this.parent = parent; - this.svg = svg; - + public SVG(XMLElement svg) { + super(null, svg); + if (!svg.getName().equals("svg")) { throw new RuntimeException("root is not , it's <" + svg.getName() + ">"); } @@ -203,91 +189,26 @@ public class SVG { } } - /* - PApplet.println("document has " + document.getChildCount() + " children"); - //Get the xml child node we need - XMLElement doc = document.getChild(1); - PApplet.println(doc); - if (true) return; - */ - - /* - //XMLElement entSVG = doc.getChild(0); - //XMLElement svg = entSVG.getChild(1); - //While we're doing that, save the width and height too - //svgWidth = svg.getIntAttribute("width"); - //svgHeight = svg.getIntAttribute("height"); - - //Catch exception when SVG doesn't have a tag - XMLElement graphics; - String nameOfFirstChild = svg.getChild(1).toString(); - if(nameOfFirstChild.equals("")) - graphics = svg.getChild(1); - else - graphics = svg; - - this.svgData = svg; - */ - - //parseChildren(document); - root = new Group(null, svg); - - /* - XMLElement graphics = null; - - //Print SVG on construction - //Use this for debugging - //svg.printElementTree(" ."); - */ + //root = new Group(null, svg); } /** * Internal method used to clone an object and return the subtree. */ - protected SVG(PApplet parent, float width, float height, Hashtable table, - BaseObject obj, boolean styleOverride) { - this.parent = parent; - this.width = width; - this.height = height; - this.table = table; - this.root = obj; - this.svg = obj.element; - this.ignoreStyles = styleOverride; - } - - - /** - * Parse a size that may have a suffix for its units. - * Ignoring cases where this could also be a percentage. - * The units spec: - *
      - *
    • "1pt" equals "1.25px" (and therefore 1.25 user units) - *
    • "1pc" equals "15px" (and therefore 15 user units) - *
    • "1mm" would be "3.543307px" (3.543307 user units) - *
    • "1cm" equals "35.43307px" (and therefore 35.43307 user units) - *
    • "1in" equals "90px" (and therefore 90 user units) - *
    - */ - public float parseUnitSize(String text) { - int len = text.length() - 2; - - if (text.endsWith("pt")) { - return PApplet.parseFloat(text.substring(0, len)) * 1.25f; - } else if (text.endsWith("pc")) { - return PApplet.parseFloat(text.substring(0, len)) * 15; - } else if (text.endsWith("mm")) { - return PApplet.parseFloat(text.substring(0, len)) * 3.543307f; - } else if (text.endsWith("cm")) { - return PApplet.parseFloat(text.substring(0, len)) * 35.43307f; - } else if (text.endsWith("in")) { - return PApplet.parseFloat(text.substring(0, len)) * 90; - } else if (text.endsWith("px")) { - return PApplet.parseFloat(text.substring(0, len)); - } else { - return PApplet.parseFloat(text); - } - } +// protected SVG(PApplet parent, float width, float height, +// HashMap table, +// BaseObject obj, boolean styleOverride) { +// super(kind); +// +// this.parent = parent; +// this.width = width; +// this.height = height; +// this.table = table; +// this.root = obj; +// this.svg = obj.element; +// this.ignoreStyles = styleOverride; +// } /** @@ -301,1368 +222,15 @@ public class SVG { * SVG layer3 = svg.get("Layer 3"); * */ - public SVG get(String name) { - BaseObject obj = (BaseObject) table.get(name); - if (obj == null) { - // try with underscores instead of spaces - obj = (BaseObject) table.get(name.replace(' ', '_')); - } - if (obj != null) { - return new SVG(parent, width, height, table, obj, ignoreStyles); - } - return null; - } - - - // grab the (fill) gradient from a particular object by name - // and apply it to either the stroke or fill - // based on - - - protected Paint getGradient(String name, float cx, float cy, float r) { - BaseObject obj = (BaseObject) table.get(name); - if (obj == null) { - // try with underscores instead of spaces - obj = (BaseObject) table.get(name.replace(' ', '_')); - } - - 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) table.get(name); - if (obj == null) { - // try with underscores instead of spaces - obj = (BaseObject) table.get(name.replace(' ', '_')); - } - - 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); - } - - - public void strokeGradient(String name, float x, float y, float r) { - Paint paint = getGradient(name, x, y, r); - - if (parent.g instanceof PGraphicsJava2D) { - PGraphicsJava2D p2d = ((PGraphicsJava2D) parent.g); - - p2d.strokeGradient = true; - p2d.strokeGradientObject = paint; - } - } - - public void strokeGradient(String name, float x1, float y1, float x2, float y2) { - Paint paint = getGradient(name, x1, y1, x2, y2); - - if (parent.g instanceof PGraphicsJava2D) { - PGraphicsJava2D p2d = ((PGraphicsJava2D) parent.g); - - p2d.strokeGradient = true; - p2d.strokeGradientObject = paint; - } - } - - - public void fillGradient(String name, float x, float y, float r) { - Paint paint = getGradient(name, x, y, r); - - if (parent.g instanceof PGraphicsJava2D) { - PGraphicsJava2D p2d = ((PGraphicsJava2D) parent.g); - - p2d.fillGradient = true; - p2d.fillGradientObject = paint; - } - } - - - public void fillGradient(String name, float x1, float y1, float x2, float y2) { - Paint paint = getGradient(name, x1, y1, x2, y2); - - if (parent.g instanceof PGraphicsJava2D) { - PGraphicsJava2D p2d = ((PGraphicsJava2D) parent.g); - - p2d.fillGradient = true; - p2d.fillGradientObject = paint; - } - } - - - /** - * Temporary hack for gradient handling. This is not supported - * and will be removed from future releases. - */ - /* - public void drawStyles() { - root.drawStyles(); - //PApplet.println(root); - - if (root instanceof VectorObject) { - ((VectorObject)root).drawStyles(); - } else { - PApplet.println("Only use drawStyles() on an object, not a group."); - } - } - */ - - - public void draw() { - if (drawMode == PConstants.CENTER) { - parent.pushMatrix(); - parent.translate(-width/2, -height/2); - drawImpl(); - parent.popMatrix(); - - } else if ((drawMode == PConstants.CORNER) || - (drawMode == PConstants.CORNERS)) { - drawImpl(); - } - } - - - /** - * Convenience method to draw at a particular location. - */ - public void draw(float x, float y) { - parent.pushMatrix(); - - if (drawMode == PConstants.CENTER) { - parent.translate(x - width/2, y - height/2); - - } else if ((drawMode == PConstants.CORNER) || - (drawMode == PConstants.CORNERS)) { - parent.translate(x, y); - } - drawImpl(); - - parent.popMatrix(); - } - - - public void draw(float x, float y, float c, float d) { - parent.pushMatrix(); - - if (drawMode == PConstants.CENTER) { - // x and y are center, c and d refer to a diameter - parent.translate(x - c/2f, y - d/2f); - parent.scale(c / width, d / height); - - } else if (drawMode == PConstants.CORNER) { - parent.translate(x, y); - parent.scale(c / width, d / height); - - } else if (drawMode == PConstants.CORNERS) { - // c and d are x2/y2, make them into width/height - c -= x; - d -= y; - // then same as above - parent.translate(x, y); - parent.scale(c / width, d / height); - } - drawImpl(); - - parent.popMatrix(); - } - - - /** - * Draws the SVG document. - */ - public void drawImpl() { - boolean stroke = parent.g.stroke; - int strokeColor = parent.g.strokeColor; - float strokeWeight = parent.g.strokeWeight; - int strokeCap = parent.g.strokeCap; - int strokeJoin= parent.g.strokeJoin; - - boolean fill = parent.g.fill; - int fillColor = parent.g.fillColor; - - int ellipseMode = parent.g.ellipseMode; - - root.draw(); - - parent.g.stroke = stroke; - parent.g.strokeColor = strokeColor; - parent.g.strokeWeight = strokeWeight; - parent.g.strokeCap = strokeCap; - parent.g.strokeJoin = strokeJoin; - - parent.g.fill = fill; - parent.g.fillColor = fillColor; - - parent.g.ellipseMode = ellipseMode; - } - - - /** - * Set the orientation for drawn objects, similar to PImage.imageMode(). - * @param which Either CORNER, CORNERS, or CENTER. - */ - public void drawMode(int which) { - drawMode = which; - } - - - /** - * Overrides SVG-set styles and uses PGraphics styles and colors. - * Identical to ignoreStyles(true). - */ - public void ignoreStyles() { - ignoreStyles(true); - } - - - /** - * Enables or disables style information (fill and stroke) set in the file. - * @param state true to use user-specified stroke/fill, false for svg version - */ - public void ignoreStyles(boolean state) { - ignoreStyles = state; - } - - - /** - * Prints out the SVG document useful for parsing - */ - public void print() { - PApplet.println(svg.toString()); - } - - - // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . - - - protected abstract class BaseObject { - String id; - XMLElement element; - - // set to false if the object is hidden in the layers palette - boolean display; - - boolean stroke; - int strokeColor; - float strokeWeight; // default is 1 - int strokeCap; - int strokeJoin; - Gradient strokeGradient; - Paint strokeGradientPaint; - String strokeName; // id of another object, gradients only? - - boolean fill; - int fillColor; - Gradient fillGradient; - Paint fillGradientPaint; - String fillName; // id of another object - - boolean hasTransform; - float[] transformation; - - float opacity; - - - public BaseObject(BaseObject parent, XMLElement properties) { - - if (parent == null) { - // set values to their defaults according to the SVG spec - stroke = false; - strokeColor = 0xff000000; - strokeWeight = 1; - strokeCap = PConstants.SQUARE; // equivalent to BUTT in svg spec - strokeJoin = PConstants.MITER; - strokeGradient = null; - strokeGradientPaint = null; - strokeName = null; - - fill = true; - fillColor = 0xff000000; - fillGradient = null; - fillGradientPaint = null; - fillName = null; - - //hasTransform = false; - //transformation = null; //new float[] { 1, 0, 0, 1, 0, 0 }; - - opacity = 1; - - } else { - stroke = parent.stroke; - strokeColor = parent.strokeColor; - strokeWeight = parent.strokeWeight; - strokeCap = parent.strokeCap; - strokeJoin = parent.strokeJoin; - strokeGradient = parent.strokeGradient; - strokeGradientPaint = parent.strokeGradientPaint; - strokeName = parent.strokeName; - - fill = parent.fill; - fillColor = parent.fillColor; - fillGradient = parent.fillGradient; - fillGradientPaint = parent.fillGradientPaint; - fillName = parent.fillName; - - //hasTransform = parent.hasTransform; - //transformation = parent.transformation; - - opacity = parent.opacity; - } - - element = properties; - - id = properties.getStringAttribute("id"); - if (id != null) { - table.put(id, this); - //System.out.println("now parsing " + id); - } - - String displayStr = properties.getStringAttribute("display", "inline"); - display = !displayStr.equals("none"); - - getColors(properties); - getTransformation(properties); - } - - - private void getTransformation(XMLElement properties) { - String transform = properties.getStringAttribute("transform"); - if (transform != null) { - this.hasTransform = true; - transform = transform.substring(7, transform.length() - 2); - String tf[] = PApplet.splitTokens(transform); - this.transformation = PApplet.parseFloat(tf); - /* - this.transformation = new float[tf.length]; - for (int i = 0; i < transformation.length; i++) { - this.transformation[i] = Float.valueOf(tf[i]).floatValue(); - } - - // Hacky code to get rotation working - // Done through the powers of trial and error [mchang] - float t[] = this.transformation; - if (t[0] < 0 && t[1] < 0 && t[2] > 0 && t[3] < 0) - this.rotation = -PApplet.acos(this.transformation[3]); - if (t[0] > 0 && t[1] < 0 && t[2] > 0 && t[3] > 0) - this.rotation = PApplet.asin(this.transformation[1]); - if (t[0] < 0 && t[1] > 0 && t[2] < 0 && t[3] < 0) - this.rotation = PApplet.acos(this.transformation[0]); - if (t[0] > 0 && t[1] > 0 && t[2] < 0 && t[3] > 0) - this.rotation = PApplet.acos(this.transformation[0]); - this.translateX = this.transformation[4]; - this.translateY = this.transformation[5]; - */ - } - } - - - /* - private int colorFromString(String color, String opacity) { - if (!color.equals("none")) { - color = color.substring(1, 7); - color = opacity + color; - return PApplet.unhex(color); - } else { - return transValue; - } - } - */ - - - protected void getColors(XMLElement properties) { - - if (properties.hasAttribute("opacity")) { - opacity = properties.getFloatAttribute("opacity"); - } - int opacityMask = ((int) (opacity * 255)) << 24; - - if (properties.hasAttribute("stroke")) { - String strokeText = properties.getStringAttribute("stroke"); - 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 = table.get(strokeName); - if (strokeObject instanceof Gradient) { - strokeGradient = (Gradient) strokeObject; - strokeGradientPaint = calcGradientPaint(strokeGradient); //, opacity); - } else { - System.err.println("url " + strokeName + " refers to unexpected data"); - } - } - } - - if (properties.hasAttribute("stroke-width")) { - // if NaN (i.e. if it's 'inherit') then default back to the inherit setting - strokeWeight = properties.getFloatAttribute("stroke-width", strokeWeight); - } - - if (properties.hasAttribute("stroke-linejoin")) { - String linejoin = properties.getStringAttribute("stroke-linejoin"); - if (linejoin.equals("inherit")) { - // do nothing, will inherit automatically - - } else if (linejoin.equals("miter")) { - strokeJoin = PConstants.MITER; - - } else if (linejoin.equals("round")) { - strokeJoin = PConstants.ROUND; - - } else if (linejoin.equals("bevel")) { - strokeJoin = PConstants.BEVEL; - } - } - - if (properties.hasAttribute("stroke-linecap")) { - String linecap = properties.getStringAttribute("stroke-linecap"); - if (linecap.equals("inherit")) { - // do nothing, will inherit automatically - - } else if (linecap.equals("butt")) { - strokeCap = PConstants.SQUARE; - - } else if (linecap.equals("round")) { - strokeCap = PConstants.ROUND; - - } else if (linecap.equals("square")) { - strokeCap = PConstants.PROJECT; - } - } - - - // 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"); - if (fillText.equals("none")) { - fill = false; - } else if (fillText.startsWith("#")) { - fill = true; - fillColor = opacityMask | - (Integer.parseInt(fillText.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 = table.get(fillName); - //PApplet.println("found " + fillObject); - if (fillObject instanceof Gradient) { - fill = true; - fillGradient = (Gradient) fillObject; - fillGradientPaint = calcGradientPaint(fillGradient); //, opacity); - //PApplet.println("got filla " + fillObject); - } else { - System.err.println("url " + fillName + " refers to unexpected data"); - } - } - } - } - - - int parseRGB(String what) { - int leftParen = what.indexOf('(') + 1; - int rightParen = what.indexOf(')'); - String sub = what.substring(leftParen, rightParen); - int[] values = PApplet.parseInt(PApplet.splitTokens(sub, ", ")); - return (values[0] << 16) | (values[1] << 8) | (values[2]); - } - - - protected Paint calcGradientPaint(Gradient gradient) { - if (gradient instanceof LinearGradient) { - LinearGradient grad = (LinearGradient) gradient; - return new LinearGradientPaint(grad.x1, grad.y1, grad.x2, grad.y2, - grad.offset, grad.color, grad.count, - opacity); - - } else if (gradient instanceof RadialGradient) { - RadialGradient grad = (RadialGradient) gradient; - return new RadialGradientPaint(grad.cx, grad.cy, grad.r, - grad.offset, grad.color, grad.count, - opacity); - } - return null; - } - - - protected Paint calcGradientPaint(Gradient gradient, - float x1, float y1, float x2, float y2) { - if (gradient instanceof LinearGradient) { - LinearGradient grad = (LinearGradient) gradient; - return new LinearGradientPaint(x1, y1, x2, y2, - grad.offset, grad.color, grad.count, - opacity); - } - throw new RuntimeException("Not a linear gradient."); - } - - - protected Paint calcGradientPaint(Gradient gradient, - float cx, float cy, float r) { - if (gradient instanceof RadialGradient) { - RadialGradient grad = (RadialGradient) gradient; - return new RadialGradientPaint(cx, cy, r, - grad.offset, grad.color, grad.count, - opacity); - } - throw new RuntimeException("Not a radial gradient."); - } - - - protected abstract void drawShape(); - - - protected void draw() { - if (!display) return; // don't display if set invisible - - if (!ignoreStyles) { - drawStyles(); - } - - if (hasTransform) { - parent.pushMatrix(); - parent.applyMatrix(transformation[0], transformation[1], transformation[2], - transformation[3], transformation[4], transformation[5]); - //parent.translate(translateX, translateY); - //parent.rotate(rotation); - } - - drawShape(); - - if (hasTransform) { - parent.popMatrix(); - } - - /* - if (parent.g instanceof PGraphicsJava2D) { - PGraphicsJava2D p2d = ((PGraphicsJava2D) parent.g); - - if (strokeGradient != null) { - p2d.strokeGradient = false; - } - if (fillGradient != null) { - p2d.fillGradient = false; - } - } - */ - } - - - protected void drawStyles() { - parent.colorMode(PConstants.RGB, 255); - - if (stroke) { - parent.stroke(strokeColor); - parent.strokeWeight(strokeWeight); - parent.strokeCap(strokeCap); - parent.strokeJoin(strokeJoin); - } else { - parent.noStroke(); - } - - if (fill) { - //System.out.println("filling " + PApplet.hex(fillColor)); - parent.fill(fillColor); - } else { - parent.noFill(); - } - - if (parent.g instanceof PGraphicsJava2D) { - PGraphicsJava2D p2d = ((PGraphicsJava2D) parent.g); - - if (strokeGradient != null) { - p2d.strokeGradient = true; - p2d.strokeGradientObject = strokeGradientPaint; - } else { - // need to shut off, in case parent object has a gradient applied - //p2d.strokeGradient = false; - } - if (fillGradient != null) { - p2d.fillGradient = true; - p2d.fillGradientObject = fillGradientPaint; - } else { - // need to shut off, in case parent object has a gradient applied - //p2d.fillGradient = false; - } - } - } - } - - - // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . - - - private class Group extends BaseObject { - BaseObject[] objects; - int objectCount; - - - public Group(BaseObject parent, XMLElement graphics) { - super(parent, graphics); - - XMLElement elements[] = graphics.getChildren(); - objects = new BaseObject[elements.length]; - - for (int i = 0; i < elements.length; i++) { - String name = elements[i].getName(); //getElement(); - XMLElement elem = elements[i]; - - if (name.equals("g")) { - objects[objectCount++] = new Group(this, elem); - - } else if (name.equals("defs")) { - // generally this will contain gradient info, so may - // as well just throw it into a group element for parsing - objects[objectCount++] = new Group(this, elem); - - } else if (name.equals("line")) { - objects[objectCount++] = new Line(this, elem); - - } else if (name.equals("circle")) { - objects[objectCount++] = new Circle(this, elem); - - } else if (name.equals("ellipse")) { - objects[objectCount++] = new Ellipse(this, elem); - -/* - } else if (name.equals("font")) { - objects[objectCount++] = new Font(this, elem); -*/ - - } else if (name.equals("rect")) { - objects[objectCount++] = new Rect(this, elem); - - } else if (name.equals("polygon")) { - objects[objectCount++] = new Poly(this, elem, true); - - } else if (name.equals("polyline")) { - objects[objectCount++] = new Poly(this, elem, false); - - } else if (name.equals("path")) { - objects[objectCount++] = new Path(this, elem); - - } else if (name.equals("radialGradient")) { - objects[objectCount++] = new RadialGradient(this, elem); - - } else if (name.equals("linearGradient")) { - objects[objectCount++] = new LinearGradient(this, elem); - - } else if (name.equals("text")) { - PApplet.println("Text is not currently handled, " + - "convert text to outlines instead."); - - } else if (name.equals("filter")) { - PApplet.println("Filters are not supported."); - - } else if (name.equals("mask")) { - PApplet.println("Masks are not supported."); - - } else { - System.err.println("Ignoring <" + name + "> tag."); - } - } - } - - - public void drawShape() { - if (display) { - for (int i = 0; i < objectCount; i++) { - objects[i].draw(); - } - } - } - } - - - // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . - - - abstract private class Gradient extends BaseObject { - AffineTransform transform; - - float[] offset; - int[] color; - int count; - - public Gradient(BaseObject parent, XMLElement properties) { - super(parent, properties); - - XMLElement elements[] = properties.getChildren(); - offset = new float[elements.length]; - color = new int[elements.length]; - - // - for (int i = 0; i < elements.length; i++) { - XMLElement elem = elements[i]; - String name = elem.getName(); - if (name.equals("stop")) { - offset[count] = elem.getFloatAttribute("offset"); - String style = elem.getStringAttribute("style"); - Hashtable styles = parseStyleAttributes(style); - - String colorStr = (String) styles.get("stop-color"); - if (colorStr == null) colorStr = "#000000"; - String opacityStr = (String) styles.get("stop-opacity"); - if (opacityStr == null) opacityStr = "1"; - int tupacity = (int) (PApplet.parseFloat(opacityStr) * 255); - color[count] = (tupacity << 24) | - Integer.parseInt(colorStr.substring(1), 16); - count++; - //System.out.println("this color is " + PApplet.hex(color[count])); - /* - int idx = farbe.indexOf("#"); - if (idx != -1) { - color[count] = Integer.parseInt(farbe.substring(idx+1), 16); - count++; - } else { - System.err.println("problem with gradient stop " + properties); - } - */ - } - } - } - - abstract protected void drawShape(); - } - - - static protected Hashtable parseStyleAttributes(String style) { - Hashtable table = new Hashtable(); - String[] pieces = style.split(";"); - for (int i = 0; i < pieces.length; i++) { - String[] parts = pieces[i].split(":"); - table.put(parts[0], parts[1]); - } - return table; - } - - - private class LinearGradient extends Gradient { - float x1, y1, x2, y2; - - public LinearGradient(BaseObject 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"); - - String transformStr = - properties.getStringAttribute("gradientTransform"); - if (transformStr != null) { - this.transform = parseTransform(transformStr); - - Point2D t1 = transform.transform(new Point2D.Float(x1, y1), null); - Point2D t2 = transform.transform(new Point2D.Float(x2, y2), null); - this.x1 = (float) t1.getX(); - this.y1 = (float) t1.getY(); - this.x2 = (float) t2.getX(); - this.y2 = (float) t2.getY(); - - } - } - - protected void drawShape() { - } - } - - - // complete version is here - // http://www.w3.org/TR/SVG/coords.html#TransformAttribute - AffineTransform parseTransform(String what) { - if (what != null) { - if (what.startsWith("matrix(") && what.endsWith(")")) { - // columns go first with AT constructor - what = what.substring(7, what.length() - 1); - return new AffineTransform(PApplet.parseFloat(PApplet.split(what, ' '))); - } - } - return null; - } - - - private class RadialGradient extends Gradient { - float cx, cy, r; - - public RadialGradient(BaseObject parent, XMLElement properties) { - super(parent, properties); - - this.cx = properties.getFloatAttribute("cx"); - this.cy = properties.getFloatAttribute("cy"); - this.r = properties.getFloatAttribute("r"); - - String transformStr = - properties.getStringAttribute("gradientTransform"); - if (transformStr != null) { - this.transform = parseTransform(transformStr); - - Point2D t1 = transform.transform(new Point2D.Float(cx, cy), null); - Point2D t2 = transform.transform(new Point2D.Float(cx + r, cy), null); - this.cx = (float) t1.getX(); - this.cy = (float) t1.getY(); - this.r = (float) (t2.getX() - t1.getX()); - } - } - - protected void drawShape() { - } - } - - - // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . - - - private class Line extends BaseObject { - - float x1, y1, x2, y2; - - public Line(BaseObject 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"); - } - - protected void drawShape() { - parent.line(x1, y1, x2, y2); - } - } - - - // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . - - - private class Circle extends BaseObject { - - float x, y, radius; - - public Circle(BaseObject parent, XMLElement properties) { - super(parent, properties); - this.x = properties.getFloatAttribute("cx"); - this.y = properties.getFloatAttribute("cy"); - this.radius = properties.getFloatAttribute("r") * 2; - } - - protected void drawShape() { - parent.ellipseMode(PConstants.CENTER); - parent.ellipse(x, y, radius, radius); - } - } - - - // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . - - - private class Ellipse extends BaseObject{ - - float x, y, rx, ry; - - - public Ellipse(BaseObject parent, XMLElement properties) { - super(parent, properties); - this.x = properties.getFloatAttribute("cx"); - this.y = properties.getFloatAttribute("cy"); - this.rx = properties.getFloatAttribute("rx") * 2; - this.ry = properties.getFloatAttribute("ry") * 2; - } - - protected void drawShape() { - parent.ellipseMode(PConstants.CENTER); - parent.ellipse(x, y, rx, ry); - } - } - - - // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . - - - private class Rect extends BaseObject{ - - float x, y, w, h; - - public Rect(BaseObject parent, XMLElement properties) { - super(parent, properties); - this.x = properties.getFloatAttribute("x"); - this.y = properties.getFloatAttribute("y"); - this.w = properties.getFloatAttribute("width"); - this.h = properties.getFloatAttribute("height"); - } - - protected void drawShape() { - parent.rectMode(PConstants.CORNER); - parent.rect(x, y, w, h); - } - } - - - // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . - - - private class Poly extends BaseObject { - - float points[][] = null; - /** true if polygon, false if polyline */ - boolean closed; - - public Poly(BaseObject parent, XMLElement properties, boolean closed) { - super(parent, properties); - String pointsBuffer[] = null; - this.closed = closed; - - if (properties.hasAttribute("points")) { - pointsBuffer = PApplet.splitTokens(properties.getStringAttribute("points")); - } - - points = new float[pointsBuffer.length][2]; - for (int i = 0; i < points.length; i++) { - String pb[] = PApplet.split(pointsBuffer[i], ','); - points[i][0] = Float.valueOf(pb[0]).floatValue(); - points[i][1] = Float.valueOf(pb[1]).floatValue(); - } - } - - protected void drawShape() { - if (points != null) - if (points.length > 0) { - parent.beginShape(); - for (int i = 0; i < points.length; i++) { - parent.vertex(points[i][0], points[i][1]); - } - parent.endShape(closed ? PConstants.CLOSE : PConstants.OPEN); - } - } - } - - - // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . - - - public class Path extends BaseObject { - - public int count = 0; - public float[] x = new float[4]; - public float[] y = new float[4]; - - static public final int MOVETO = 0; - static public final int LINETO = 1; - static public final int CURVETO = 2; - static public final int QCURVETO = 3; - public int[] kind = new int[4]; - - public boolean closed = false; - - - public Path(BaseObject parent, XMLElement properties) { - super(parent, properties); - String pathDataBuffer = ""; - - if (!properties.hasAttribute("d")) - return; - - pathDataBuffer = properties.getStringAttribute("d"); - StringBuffer pathChars = new StringBuffer(); - - boolean lastSeparate = false; - - for (int i = 0; i < pathDataBuffer.length(); i++) { - char c = pathDataBuffer.charAt(i); - boolean separate = false; - - if (c == 'M' || c == 'm' || - c == 'L' || c == 'l' || - c == 'H' || c == 'h' || - c == 'V' || c == 'v' || - c == 'C' || c == 'c' || // beziers - c == 'S' || c == 's' || - c == 'Q' || c == 'q' || // quadratic beziers - c == 'T' || c == 't' || - c == 'Z' || c == 'z' || // closepath - c == ',') { - separate = true; - if (i != 0) { - pathChars.append("|"); - } - } - if (c == 'Z' || c == 'z') { - separate = false; - } - if (c == '-' && !lastSeparate) { - pathChars.append("|"); - } - if (c != ',') { - pathChars.append("" + pathDataBuffer.charAt(i)); - } - if (separate && c != ',' && c != '-') { - pathChars.append("|"); - } - lastSeparate = separate; - } - - pathDataBuffer = pathChars.toString(); - - //String pathDataKeys[] = PApplet.split(pathDataBuffer, '|'); - // use whitespace constant to get rid of extra spaces and CR or LF - String pathDataKeys[] = - PApplet.splitTokens(pathDataBuffer, "|" + PConstants.WHITESPACE); - //for (int j = 0; j < pathDataKeys.length; j++) { - // PApplet.println(j + "\t" + pathDataKeys[j]); - //} - //PApplet.println(pathDataKeys); - //PApplet.println(); - - //float cp[] = {0, 0}; - float cx = 0; - float cy = 0; - - int i = 0; - //for (int i = 0; i < pathDataKeys.length; i++) { - while (i < pathDataKeys.length) { - char c = pathDataKeys[i].charAt(0); - switch (c) { - - //M - move to (absolute) - case 'M': - /* - cp[0] = PApplet.parseFloat(pathDataKeys[i + 1]); - cp[1] = PApplet.parseFloat(pathDataKeys[i + 2]); - float s[] = {cp[0], cp[1]}; - i += 2; - points.add(s); - */ - cx = PApplet.parseFloat(pathDataKeys[i + 1]); - cy = PApplet.parseFloat(pathDataKeys[i + 2]); - moveto(cx, cy); - i += 3; - break; - - - //m - move to (relative) - case 'm': - /* - cp[0] = cp[0] + PApplet.parseFloat(pathDataKeys[i + 1]); - cp[1] = cp[1] + PApplet.parseFloat(pathDataKeys[i + 2]); - float s[] = {cp[0], cp[1]}; - i += 2; - points.add(s); - */ - cx = cx + PApplet.parseFloat(pathDataKeys[i + 1]); - cy = cy + PApplet.parseFloat(pathDataKeys[i + 2]); - moveto(cx, cy); - i += 3; - break; - - - case 'L': - cx = PApplet.parseFloat(pathDataKeys[i + 1]); - cy = PApplet.parseFloat(pathDataKeys[i + 2]); - lineto(cx, cy); - i += 3; - break; - - - case 'l': - cx = cx + PApplet.parseFloat(pathDataKeys[i + 1]); - cy = cy + PApplet.parseFloat(pathDataKeys[i + 2]); - lineto(cx, cy); - i += 3; - break; - - - // horizontal lineto absolute - case 'H': - cx = PApplet.parseFloat(pathDataKeys[i + 1]); - lineto(cx, cy); - i += 2; - break; - - - // horizontal lineto relative - case 'h': - cx = cx + PApplet.parseFloat(pathDataKeys[i + 1]); - lineto(cx, cy); - i += 2; - break; - - - case 'V': - cy = PApplet.parseFloat(pathDataKeys[i + 1]); - lineto(cx, cy); - i += 2; - break; - - - case 'v': - cy = cy + PApplet.parseFloat(pathDataKeys[i + 1]); - lineto(cx, cy); - i += 2; - break; - - - // C - curve to (absolute) - case 'C': { - float ctrlX1 = PApplet.parseFloat(pathDataKeys[i + 1]); - float ctrlY1 = PApplet.parseFloat(pathDataKeys[i + 2]); - float ctrlX2 = PApplet.parseFloat(pathDataKeys[i + 3]); - float ctrlY2 = PApplet.parseFloat(pathDataKeys[i + 4]); - float endX = PApplet.parseFloat(pathDataKeys[i + 5]); - float endY = PApplet.parseFloat(pathDataKeys[i + 6]); - curveto(ctrlX1, ctrlY1, ctrlX2, ctrlY2, endX, endY); - cx = endX; - cy = endY; - i += 7; - } - break; - - // c - curve to (relative) - case 'c': { - float ctrlX1 = cx + PApplet.parseFloat(pathDataKeys[i + 1]); - float ctrlY1 = cy + PApplet.parseFloat(pathDataKeys[i + 2]); - float ctrlX2 = cx + PApplet.parseFloat(pathDataKeys[i + 3]); - float ctrlY2 = cy + PApplet.parseFloat(pathDataKeys[i + 4]); - float endX = cx + PApplet.parseFloat(pathDataKeys[i + 5]); - float endY = cy + PApplet.parseFloat(pathDataKeys[i + 6]); - curveto(ctrlX1, ctrlY1, ctrlX2, ctrlY2, endX, endY); - cx = endX; - cy = endY; - i += 7; - } - break; - - // S - curve to shorthand (absolute) - case 'S': { - float ppx = x[count-2]; - float ppy = y[count-2]; - float px = x[count-1]; - float py = y[count-1]; - float ctrlX1 = px + (px - ppx); - float ctrlY1 = py + (py - ppy); - float ctrlX2 = PApplet.parseFloat(pathDataKeys[i + 1]); - float ctrlY2 = PApplet.parseFloat(pathDataKeys[i + 2]); - float endX = PApplet.parseFloat(pathDataKeys[i + 3]); - float endY = PApplet.parseFloat(pathDataKeys[i + 4]); - curveto(ctrlX1, ctrlY1, ctrlX2, ctrlY2, endX, endY); - cx = endX; - cy = endY; - i += 5; - } - break; - - // s - curve to shorthand (relative) - case 's': { - float ppx = x[count-2]; - float ppy = y[count-2]; - float px = x[count-1]; - float py = y[count-1]; - float ctrlX1 = px + (px - ppx); - float ctrlY1 = py + (py - ppy); - float ctrlX2 = cx + PApplet.parseFloat(pathDataKeys[i + 1]); - float ctrlY2 = cy + PApplet.parseFloat(pathDataKeys[i + 2]); - float endX = cx + PApplet.parseFloat(pathDataKeys[i + 3]); - float endY = cy + PApplet.parseFloat(pathDataKeys[i + 4]); - curveto(ctrlX1, ctrlY1, ctrlX2, ctrlY2, endX, endY); - cx = endX; - cy = endY; - i += 5; - } - break; - - // Q - quadratic curve to (absolute) - case 'Q': { - float ctrlX = PApplet.parseFloat(pathDataKeys[i + 1]); - float ctrlY = PApplet.parseFloat(pathDataKeys[i + 2]); - float endX = PApplet.parseFloat(pathDataKeys[i + 3]); - float endY = PApplet.parseFloat(pathDataKeys[i + 4]); - curveto(ctrlX, ctrlY, endX, endY); - cx = endX; - cy = endY; - i += 5; - } - break; - - // q - quadratic curve to (relative) - case 'q': { - float ctrlX = cx + PApplet.parseFloat(pathDataKeys[i + 1]); - float ctrlY = cy + PApplet.parseFloat(pathDataKeys[i + 2]); - float endX = cx + PApplet.parseFloat(pathDataKeys[i + 3]); - float endY = cy + PApplet.parseFloat(pathDataKeys[i + 4]); - curveto(ctrlX, ctrlY, endX, endY); - cx = endX; - cy = endY; - i += 5; - } - break; - - // T - quadratic curve to shorthand (absolute) - // The control point is assumed to be the reflection of the - // control point on the previous command relative to the - // current point. (If there is no previous command or if the - // previous command was not a Q, q, T or t, assume the control - // point is coincident with the current point.) - case 'T': { - float ppx = x[count-2]; - float ppy = y[count-2]; - float px = x[count-1]; - float py = y[count-1]; - float ctrlX = px + (px - ppx); - float ctrlY = py + (py - ppy); - float endX = PApplet.parseFloat(pathDataKeys[i + 1]); - float endY = PApplet.parseFloat(pathDataKeys[i + 2]); - curveto(ctrlX, ctrlY, endX, endY); - cx = endX; - cy = endY; - i += 3; - } - break; - - // t - quadratic curve to shorthand (relative) - case 't': { - float ppx = x[count-2]; - float ppy = y[count-2]; - float px = x[count-1]; - float py = y[count-1]; - float ctrlX = px + (px - ppx); - float ctrlY = py + (py - ppy); - float endX = cx + PApplet.parseFloat(pathDataKeys[i + 1]); - float endY = cy + PApplet.parseFloat(pathDataKeys[i + 2]); - curveto(ctrlX, ctrlY, endX, endY); - cx = endX; - cy = endY; - i += 3; - } - break; - - case 'Z': - case 'z': - closed = true; - i++; - break; - - default: - String parsed = - PApplet.join(PApplet.subset(pathDataKeys, 0, i), ","); - String unparsed = - PApplet.join(PApplet.subset(pathDataKeys, i), ","); - System.err.println("parsed: " + parsed); - System.err.println("unparsed: " + unparsed); - throw new RuntimeException("shape command not handled: " + pathDataKeys[i]); - } - } - } - - - protected void moveto(float px, float py) { - if (count == x.length) { - x = PApplet.expand(x); - y = PApplet.expand(y); - kind = PApplet.expand(kind); - } - kind[count] = MOVETO; - x[count] = px; - y[count] = py; - count++; - } - - - protected void lineto(float px, float py) { - if (count == x.length) { - x = PApplet.expand(x); - y = PApplet.expand(y); - kind = PApplet.expand(kind); - } - kind[count] = LINETO; - x[count] = px; - y[count] = py; - count++; - } - - - /** Quadratic curveto command. */ - protected void curveto(float x1, float y1, float x2, float y2) { - if (count + 2 >= x.length) { - x = PApplet.expand(x); - y = PApplet.expand(y); - kind = PApplet.expand(kind); - } - kind[count] = QCURVETO; - x[count] = x1; - y[count] = y1; - count++; - x[count] = x2; - y[count] = y2; - count++; - } - - - /** Cubic curveto command. */ - protected void curveto(float x1, float y1, float x2, float y2, float x3, float y3) { - if (count + 2 >= x.length) { - x = PApplet.expand(x); - y = PApplet.expand(y); - kind = PApplet.expand(kind); - } - kind[count] = CURVETO; - x[count] = x1; - y[count] = y1; - count++; - x[count] = x2; - y[count] = y2; - count++; - x[count] = x3; - y[count] = y3; - count++; - } - - - protected void drawShape() { - parent.beginShape(); - - parent.vertex(x[0], y[0]); - int i = 1; // moveto has the first point - while (i < count) { - switch (kind[i]) { - case MOVETO: - parent.breakShape(); - parent.vertex(x[i], y[i]); - i++; - break; - - case LINETO: - parent.vertex(x[i], y[i]); - i++; - break; - - case QCURVETO: // doubles the control point - parent.bezierVertex(x[i], y[i], x[i+1], y[i+1], x[i+1], y[i+1]); - i += 2; - break; - - case CURVETO: - parent.bezierVertex(x[i], y[i], x[i+1], y[i+1], x[i+2], y[i+2]); - i += 3; - break; - } - } - parent.endShape(closed ? PConstants.CLOSE : PConstants.OPEN); - } - } +// public SVG get(String name) { +// BaseObject obj = table.get(name); +// if (obj == null) { +// // try with underscores instead of spaces +// obj = table.get(name.replace(' ', '_')); +// } +// if (obj != null) { +// return new SVG(parent, width, height, table, obj, ignoreStyles); +// } +// return null; +// } } diff --git a/core/src/processing/core/PConstants.java b/core/src/processing/core/PConstants.java index afe886e98..8d5f4a5b3 100644 --- a/core/src/processing/core/PConstants.java +++ b/core/src/processing/core/PConstants.java @@ -187,9 +187,11 @@ public interface PConstants { // the low four bits set the variety, // higher bits set the specific shape type - static final int POINTS = (1 << 4) | 0; + static final int GROUP = (1 << 2); + + static final int POINTS = (1 << 4); - static final int LINES = (1 << 5) | 0; + static final int LINES = (1 << 5); //static final int LINE_STRIP = (1 << 5) | 1; //static final int LINE_LOOP = (1 << 5) | 2; diff --git a/core/src/processing/core/PGraphics.java b/core/src/processing/core/PGraphics.java index 3ae1c955f..2befbb493 100644 --- a/core/src/processing/core/PGraphics.java +++ b/core/src/processing/core/PGraphics.java @@ -108,47 +108,11 @@ public abstract class PGraphics extends PImage implements PConstants { static public final int EG = 33; static public final int EB = 34; - //has this vertex been lit yet + // has this vertex been lit yet static public final int BEEN_LIT = 35; static final int VERTEX_FIELD_COUNT = 36; - // line & triangle fields (note how these overlap) - - static public final int INDEX = 0; // shape index - static public final int VERTEX1 = 1; - static public final int VERTEX2 = 2; - static public final int VERTEX3 = 3; // (triangles only) - static public final int TEXTURE_INDEX = 4; // (triangles only) - static public final int STROKE_MODE = 3; // (lines only) - static public final int STROKE_WEIGHT = 4; // (lines only) - - static public final int LINE_FIELD_COUNT = 5; - static public final int TRIANGLE_FIELD_COUNT = 5; - - static public final int TRI_DIFFUSE_R = 0; - static public final int TRI_DIFFUSE_G = 1; - static public final int TRI_DIFFUSE_B = 2; - static public final int TRI_DIFFUSE_A = 3; - static public final int TRI_SPECULAR_R = 4; - static public final int TRI_SPECULAR_G = 5; - static public final int TRI_SPECULAR_B = 6; - static public final int TRI_SPECULAR_A = 7; - - static public final int TRIANGLE_COLOR_COUNT = 8; - - - // normal modes for lighting, these have the uglier naming - // because the constants are never seen by users - - /// normal calculated per triangle - static public final int AUTO_NORMAL = 0; - /// one normal manually specified per shape - static public final int MANUAL_SHAPE_NORMAL = 1; - /// normals specified for each shape vertex - static public final int MANUAL_VERTEX_NORMAL = 2; - - /// width minus one (useful for many calculations) public int width1; @@ -982,24 +946,9 @@ public abstract class PGraphics extends PImage implements PConstants { * that's how things are implemented. */ abstract public void beginShape(int kind); - /* - shape = kind; - - // reset vertex, line and triangle information - // every shape is rendered at endShape(); - vertexCount = 0; - - splineVertexCount = 0; - //spline_vertices_flat = true; - - //strokeChanged = false; - //fillChanged = false; - //normalChanged = false; - */ public void normal(float nx, float ny, float nz) { - //depthError("normal"); } diff --git a/core/src/processing/core/PGraphics2D.java b/core/src/processing/core/PGraphics2D.java index 8288f04f2..5f523fa41 100644 --- a/core/src/processing/core/PGraphics2D.java +++ b/core/src/processing/core/PGraphics2D.java @@ -1792,9 +1792,6 @@ public class PGraphics2D extends PGraphics { */ protected void clear() { Arrays.fill(pixels, backgroundColor); -// for (int i = 0; i < pixelCount; i++) { -// pixels[i] = backgroundColor; -// } } diff --git a/core/src/processing/core/PGraphics3D.java b/core/src/processing/core/PGraphics3D.java index 3bfd174e0..948d55faf 100644 --- a/core/src/processing/core/PGraphics3D.java +++ b/core/src/processing/core/PGraphics3D.java @@ -41,6 +41,41 @@ import java.util.*; */ public class PGraphics3D extends PGraphics { + // line & triangle fields (note how these overlap) + + static public final int INDEX = 0; // shape index + static public final int VERTEX1 = 1; + static public final int VERTEX2 = 2; + static public final int VERTEX3 = 3; // (triangles only) + static public final int TEXTURE_INDEX = 4; // (triangles only) + static public final int STROKE_MODE = 3; // (lines only) + static public final int STROKE_WEIGHT = 4; // (lines only) + + static public final int LINE_FIELD_COUNT = 5; + static public final int TRIANGLE_FIELD_COUNT = 5; + + static public final int TRI_DIFFUSE_R = 0; + static public final int TRI_DIFFUSE_G = 1; + static public final int TRI_DIFFUSE_B = 2; + static public final int TRI_DIFFUSE_A = 3; + static public final int TRI_SPECULAR_R = 4; + static public final int TRI_SPECULAR_G = 5; + static public final int TRI_SPECULAR_B = 6; + static public final int TRI_SPECULAR_A = 7; + + static public final int TRIANGLE_COLOR_COUNT = 8; + + + // normal modes for lighting, these have the uglier naming + // because the constants are never seen by users + + /// normal calculated per triangle + static public final int AUTO_NORMAL = 0; + /// one normal manually specified per shape + static public final int MANUAL_SHAPE_NORMAL = 1; + /// normals specified for each shape vertex + static public final int MANUAL_VERTEX_NORMAL = 2; + // ........................................................ // Lighting-related variables @@ -251,7 +286,7 @@ public class PGraphics3D extends PGraphics { cameraFOV = 60 * DEG_TO_RAD; // at least for now cameraX = width / 2.0f; cameraY = height / 2.0f; - cameraZ = cameraY / ((float) tan(cameraFOV / 2.0f)); + cameraZ = cameraY / ((float) Math.tan(cameraFOV / 2.0f)); cameraNear = cameraZ / 10.0f; cameraFar = cameraZ * 10.0f; cameraAspect = (float)width / (float)height; @@ -502,12 +537,9 @@ public class PGraphics3D extends PGraphics { textureImage = image; if (texture_index == textures.length - 1) { - PImage temp[] = new PImage[texture_index<<1]; - System.arraycopy(textures, 0, temp, 0, texture_index); - textures = temp; - //message(CHATTER, "allocating more textures " + textures.length); + textures = (PImage[]) PApplet.expand(textures); } - if (textures[texture_index] != null) { + if (textures[texture_index] != null) { // ??? texture_index++; } textures[texture_index] = image; @@ -2038,13 +2070,6 @@ public class PGraphics3D extends PGraphics { } - private void crossProduct(float[] u, float[] v, float[] out) { - out[0] = u[1]*v[2] - u[2]*v[1]; - out[1] = u[2]*v[0] - u[0]*v[2]; - out[2] = u[0]*v[1] - u[1]*v[0]; - } - - private void light_triangle(int triIndex) { int vIndex; @@ -2109,7 +2134,7 @@ public class PGraphics3D extends PGraphics { dv2[2] = vertices[vIndex3][VZ] - vertices[vIndex][VZ]; //float[] norm = new float[3]; - crossProduct(dv1, dv2, norm); + cross(dv1, dv2, norm); float nMag = mag(norm[X], norm[Y], norm[Z]); if (nMag != 0 && nMag != 1) { norm[X] /= nMag; norm[Y] /= nMag; norm[Z] /= nMag; @@ -2177,7 +2202,7 @@ public class PGraphics3D extends PGraphics { dv2[2] = vertices[vIndex3][VZ] - vertices[vIndex][VZ]; //float[] norm = new float[3]; - crossProduct(dv1, dv2, norm); + cross(dv1, dv2, norm); float nMag = mag(norm[X], norm[Y], norm[Z]); if (nMag != 0 && nMag != 1) { norm[X] /= nMag; norm[Y] /= nMag; norm[Z] /= nMag; @@ -3116,7 +3141,7 @@ public class PGraphics3D extends PGraphics { */ public void perspective(float fov, float aspect, float zNear, float zFar) { //float ymax = zNear * tan(fovy * PI / 360.0f); - float ymax = zNear * tan(fov / 2.0f); + float ymax = zNear * (float) Math.tan(fov / 2); float ymin = -ymax; float xmin = ymin * aspect; @@ -3758,7 +3783,7 @@ public class PGraphics3D extends PGraphics { lightPosition(lightCount, x, y, z); lightDirection(lightCount, nx, ny, nz); lightSpotAngle[lightCount] = angle; - lightSpotAngleCos[lightCount] = max(0, cos(angle)); + lightSpotAngleCos[lightCount] = max(0, (float) Math.cos(angle)); lightSpotConcentration[lightCount] = concentration; lightCount++; @@ -3848,13 +3873,6 @@ public class PGraphics3D extends PGraphics { */ public void background(PImage image) { super.background(image); - - /* - for (int i = 0; i < pixelCount; i++) { - zbuffer[i] = Float.MAX_VALUE; - //stencil[i] = 0; - } - */ Arrays.fill(zbuffer, Float.MAX_VALUE); } @@ -3864,18 +3882,8 @@ public class PGraphics3D extends PGraphics { * Stencil buffer should also be cleared, but for now is ignored in P3D. */ protected void clear() { - //System.out.println("PGraphics3.clear(" + - // PApplet.hex(backgroundColor) + ")"); - /* - for (int i = 0; i < pixelCount; i++) { - pixels[i] = backgroundColor; - zbuffer[i] = Float.MAX_VALUE; - //stencil[i] = 0; - } - */ Arrays.fill(pixels, backgroundColor); Arrays.fill(zbuffer, Float.MAX_VALUE); - clearRaw(); } @@ -3928,18 +3936,12 @@ public class PGraphics3D extends PGraphics { // MATH (internal use only) - /* - private final float mag(float a, float b) { - return (float)Math.sqrt(a*a + b*b); - } - */ - private final float mag(float a, float b, float c) { - return (float)Math.sqrt(a*a + b*b + c*c); + return (float) Math.sqrt(a*a + b*b + c*c); } private final float mag(float abc[]) { - return (float)Math.sqrt(abc[0]*abc[0] + abc[1]*abc[1] + abc[2]*abc[2]); + return (float) Math.sqrt(abc[0]*abc[0] + abc[1]*abc[1] + abc[2]*abc[2]); } private final float min(float a, float b) { @@ -3950,41 +3952,23 @@ public class PGraphics3D extends PGraphics { return (a > b) ? a : b; } - /* - private final float max(float a, float b, float c) { - return Math.max(a, Math.max(b, c)); - } - - private final float sq(float a) { - return a*a; - } - */ - private final float pow(float a, float b) { - return (float)Math.pow(a, b); + return (float) Math.pow(a, b); } private final float abs(float a) { return (a < 0) ? -a : a; } - /* - private final float sin(float angle) { - return (float)Math.sin(angle); - } - */ - - private final float cos(float angle) { - return (float)Math.cos(angle); - } - - private final float tan(float angle) { - return (float)Math.tan(angle); - } - private float dot(float ax, float ay, float az, float bx, float by, float bz) { return ax * bx + ay * by + az * bz; } + + private final void cross(float[] a, float[] b, float[] out) { + out[0] = a[1]*b[2] - a[2]*b[1]; + out[1] = a[2]*b[0] - a[0]*b[2]; + out[2] = a[0]*b[1] - a[1]*b[0]; + } } diff --git a/core/src/processing/core/PMatrix2D.java b/core/src/processing/core/PMatrix2D.java new file mode 100644 index 000000000..91a1bcc89 --- /dev/null +++ b/core/src/processing/core/PMatrix2D.java @@ -0,0 +1,321 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2005-08 Ben Fry and Casey Reas + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +package processing.core; + + +/** + * 4x4 matrix implementation. + */ +public final class PMatrix2D implements PConstants { + + public float m00, m01, m02; + public float m10, m11, m12; + + + public PMatrix2D() { + reset(); + } + + + public PMatrix2D(float m00, float m01, float m02, + float m10, float m11, float m12) { + set(m00, m01, m02, + m10, m11, m12); + } + + + public void reset() { + set(1, 0, 0, + 0, 1, 0); + } + + + /** + * Returns a copy of this PMatrix. + */ + public PMatrix2D get() { + PMatrix2D outgoing = new PMatrix2D(); + outgoing.set(this); + return outgoing; + } + + + /** + * Copies the matrix contents into a 16 entry float array. + * If target is null (or not the correct size), a new array will be created. + */ + public float[] get(float[] target) { + if ((target == null) || (target.length != 6)) { + target = new float[6]; + } + target[0] = m00; + target[1] = m01; + target[2] = m02; + + target[3] = m10; + target[4] = m11; + target[5] = m12; + + return target; + } + + + public void set(PMatrix2D src) { + set(src.m00, src.m01, src.m02, + src.m10, src.m11, src.m12); + } + + + public void set(float[] source) { + m00 = source[0]; + m01 = source[1]; + m02 = source[2]; + + m10 = source[3]; + m11 = source[4]; + m12 = source[5]; + } + + + public void set(float m00, float m01, float m02, + float m10, float m11, float m12) { + this.m00 = m00; this.m01 = m01; this.m02 = m02; + this.m10 = m10; this.m11 = m11; this.m12 = m12; + } + + + public void translate(float tx, float ty) { + m02 = tx*m00 + ty*m01 + m02; + m12 = tx*m10 + ty*m11 + m12; + } + + + public void rotate(float angle) { + // TODO fixme + } + + + public void rotateX(float angle) { + } + + + public void rotateY(float angle) { + } + + + public void rotateZ(float angle) { + } + + + public void rotate(float angle, float v0, float v1, float v2) { + } + + + public void scale(float s) { + scale(s, s); + } + + + public void scale(float sx, float sy) { + m00 *= sx; m01 *= sy; + m10 *= sx; m11 *= sy; + } + + + public void scale(float x, float y, float z) { + } + + + /** + * Multiply this matrix by another. + */ + public void apply(PMatrix2D source) { + apply(source.m00, source.m01, source.m02, + source.m10, source.m11, source.m12); + } + + + public void apply(float n00, float n01, float n02, + float n10, float n11, float n12) { + float t0 = m00; + float t1 = m01; + m00 = n00 * t0 + n10 * t1; + m01 = n01 * t0 + n11 * t1; + m02 += n02 * t0 + n12 * t1; + + t0 = m10; + t1 = m11; + m10 = n00 * t0 + n10 * t1; + m11 = n01 * t0 + n11 * t1; + m12 += n02 * t0 + n12 * t1; + } + + + /** + * Apply another matrix to the left of this one. + */ + public void preApply(PMatrix2D left) { + preApply(left.m00, left.m01, left.m02, + left.m10, left.m11, left.m12); + } + + + public void preApply(float n00, float n01, float n02, + float n10, float n11, float n12) { + float t0 = m02; + float t1 = m12; + n02 += t0 * n00 + t1 * n01; + n12 += t0 * n10 + t1 * n11; + + m02 = n02; + m12 = n12; + + t0 = m00; + t1 = m10; + m00 = t0 * n00 + t1 * n01; + m10 = t0 * n10 + t1 * n11; + + t0 = m01; + t1 = m11; + m01 = t0 * n00 + t1 * n01; + m11 = t0 * n10 + t1 * n11; } + + + ////////////////////////////////////////////////////////////// + + + /** + * Multiply a four element vector against this matrix. + * If out is null or not length four, a new float array will be returned. + * The values for vec and out can be the same (though that's less efficient). + */ + public float[] multiply(float vec[], float out[]) { + if (out == null || out.length != 2) { + out = new float[2]; + } + + if (vec == out) { + float tx = m00*vec[0] + m01*vec[1] + m02; + float ty = m10*vec[0] + m11*vec[1] + m12; + + out[0] = tx; + out[1] = ty; + + } else { + out[0] = m00*vec[0] + m01*vec[1] + m02; + out[1] = m10*vec[0] + m11*vec[1] + m12; + } + + return out; + } + + + /** + * Transpose this matrix. + */ + public void transpose() { + } + + + /** + * Invert this matrix. Implementation stolen from OpenJDK. + * @return true if successful + */ + public boolean invert() { + float determinant = determinant(); + if (Math.abs(determinant) <= Float.MIN_VALUE) { + return false; + } + + float t00 = m00; + float t01 = m01; + float t02 = m02; + float t10 = m10; + float t11 = m11; + float t12 = m12; + + m00 = t11 / determinant; + m10 = -t10 / determinant; + m01 = -t01 / determinant; + m11 = t00 / determinant; + m02 = (t01 * t12 - t11 * t02) / determinant; + m12 = (t10 * t02 - t00 * t12) / determinant; + + return true; + } + + + /** + * @return the determinant of the matrix + */ + public float determinant() { + return m00 * m11 - m01 * m10; + } + + + ////////////////////////////////////////////////////////////// + + + public void print() { + int big = (int) abs(max(PApplet.max(abs(m00), abs(m01), abs(m02)), + PApplet.max(abs(m10), abs(m11), abs(m12)))); + + int digits = 1; + if (Float.isNaN(big) || Float.isInfinite(big)) { // avoid infinite loop + digits = 5; + } else { + while ((big /= 10) != 0) digits++; // cheap log() + } + + System.out.println(PApplet.nfs(m00, digits, 4) + " " + + PApplet.nfs(m01, digits, 4) + " " + + PApplet.nfs(m02, digits, 4)); + + System.out.println(PApplet.nfs(m10, digits, 4) + " " + + PApplet.nfs(m11, digits, 4) + " " + + PApplet.nfs(m12, digits, 4)); + + System.out.println(); + } + + + ////////////////////////////////////////////////////////////// + + + private final float max(float a, float b) { + return (a > b) ? a : b; + } + + private final float abs(float a) { + return (a < 0) ? -a : a; + } + + private final float sin(float angle) { + return (float)Math.sin(angle); + } + + private final float cos(float angle) { + return (float)Math.cos(angle); + } +} diff --git a/core/src/processing/core/PMatrix3D.java b/core/src/processing/core/PMatrix3D.java index aed18dd6f..4a471a76a 100644 --- a/core/src/processing/core/PMatrix3D.java +++ b/core/src/processing/core/PMatrix3D.java @@ -57,7 +57,7 @@ public final class PMatrix3D implements PConstants { public PMatrix3D(float m00, float m01, float m02, - float m10, float m11, float m12) { + float m10, float m11, float m12) { set(m00, m01, m02, 0, m10, m11, m12, 0, 0, 0, 1, 0, @@ -67,9 +67,9 @@ public final class PMatrix3D implements PConstants { public PMatrix3D(float m00, float m01, float m02, float m03, - float m10, float m11, float m12, float m13, - float m20, float m21, float m22, float m23, - float m30, float m31, float m32, float m33) { + float m10, float m11, float m12, float m13, + float m20, float m21, float m22, float m23, + float m30, float m31, float m32, float m33) { set(m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, @@ -640,33 +640,32 @@ public final class PMatrix3D implements PConstants { max(max(abs(m30), abs(m31)), max(abs(m32), abs(m33)))))); - // avoid infinite loop - if (Float.isNaN(big) || Float.isInfinite(big)) { - big = 1000000; // set to something arbitrary + int digits = 1; + if (Float.isNaN(big) || Float.isInfinite(big)) { // avoid infinite loop + digits = 5; + } else { + while ((big /= 10) != 0) digits++; // cheap log() } - int d = 1; - while ((big /= 10) != 0) d++; // cheap log() + System.out.println(PApplet.nfs(m00, digits, 4) + " " + + PApplet.nfs(m01, digits, 4) + " " + + PApplet.nfs(m02, digits, 4) + " " + + PApplet.nfs(m03, digits, 4)); - System.out.println(PApplet.nfs(m00, d, 4) + " " + - PApplet.nfs(m01, d, 4) + " " + - PApplet.nfs(m02, d, 4) + " " + - PApplet.nfs(m03, d, 4)); + System.out.println(PApplet.nfs(m10, digits, 4) + " " + + PApplet.nfs(m11, digits, 4) + " " + + PApplet.nfs(m12, digits, 4) + " " + + PApplet.nfs(m13, digits, 4)); - System.out.println(PApplet.nfs(m10, d, 4) + " " + - PApplet.nfs(m11, d, 4) + " " + - PApplet.nfs(m12, d, 4) + " " + - PApplet.nfs(m13, d, 4)); + System.out.println(PApplet.nfs(m20, digits, 4) + " " + + PApplet.nfs(m21, digits, 4) + " " + + PApplet.nfs(m22, digits, 4) + " " + + PApplet.nfs(m23, digits, 4)); - System.out.println(PApplet.nfs(m20, d, 4) + " " + - PApplet.nfs(m21, d, 4) + " " + - PApplet.nfs(m22, d, 4) + " " + - PApplet.nfs(m23, d, 4)); - - System.out.println(PApplet.nfs(m30, d, 4) + " " + - PApplet.nfs(m31, d, 4) + " " + - PApplet.nfs(m32, d, 4) + " " + - PApplet.nfs(m33, d, 4)); + System.out.println(PApplet.nfs(m30, digits, 4) + " " + + PApplet.nfs(m31, digits, 4) + " " + + PApplet.nfs(m32, digits, 4) + " " + + PApplet.nfs(m33, digits, 4)); System.out.println(); } diff --git a/core/src/processing/core/PShape.java b/core/src/processing/core/PShape.java index 4aba94cc5..0cf3856c0 100644 --- a/core/src/processing/core/PShape.java +++ b/core/src/processing/core/PShape.java @@ -23,6 +23,8 @@ package processing.core; +import java.util.HashMap; + // take a look at the obj loader to see how this fits with things // PShape.line() PShape.ellipse()? @@ -30,57 +32,67 @@ package processing.core; // line() // endShape(s) -public class PShape { +abstract public class PShape implements PConstants { - public String name; + protected String name; + + protected int kind; + protected int drawMode; + protected PMatrix3D matrix; + + // setAxis -> .x and .y to move x and y coords of origin + protected float x; + protected float y; + protected float width; + protected float height; // set to false if the object is hidden in the layers palette - public boolean display; + protected boolean visible; - public boolean stroke; - public int strokeColor; - public float strokeWeight; // default is 1 - public int strokeCap; - public int strokeJoin; + protected boolean stroke; + protected int strokeColor; + protected float strokeWeight; // default is 1 + protected int strokeCap; + protected int strokeJoin; - public boolean fill; - public int fillColor; + protected boolean fill; + protected int fillColor; + protected boolean styles; + //public boolean hasTransform; - public float[] transformation; - - // - - int kind; - PMatrix3D matrix; + //protected float[] transformation; int[] opcode; int opcodeCount; // need to reorder vertex fields to make a VERTEX_SHORT_COUNT // that puts all the non-rendering fields into later indices + int dataCount; float[][] data; // second param is the VERTEX_FIELD_COUNT // should this be called vertices (consistent with PGraphics internals) // or does that hurt flexibility? - int childCount; - PShape[] children; + protected PShape parent; + protected int childCount; + protected PShape[] children; + protected HashMap table; // POINTS, LINES, xLINE_STRIP, xLINE_LOOP // TRIANGLES, TRIANGLE_STRIP, TRIANGLE_FAN // QUADS, QUAD_STRIP // xPOLYGON - static final int PATH = 1; // POLYGON, LINE_LOOP, LINE_STRIP - static final int GROUP = 2; +// static final int PATH = 1; // POLYGON, LINE_LOOP, LINE_STRIP +// static final int GROUP = 2; // how to handle rectmode/ellipsemode? // are they bitshifted into the constant? // CORNER, CORNERS, CENTER, (CENTER_RADIUS?) - static final int RECT = 3; // could just be QUAD, but would be x1/y1/x2/y2 - static final int ELLIPSE = 4; - - static final int VERTEX = 7; - static final int CURVE = 5; - static final int BEZIER = 6; +// static final int RECT = 3; // could just be QUAD, but would be x1/y1/x2/y2 +// static final int ELLIPSE = 4; +// +// static final int VERTEX = 7; +// static final int CURVE = 5; +// static final int BEZIER = 6; // fill and stroke functions will need a pointer to the parent @@ -92,37 +104,93 @@ public class PShape { // material parameters will be thrown out, // except those currently supported (kinds of lights) - // setAxis -> .x and .y to move x and y coords of origin - public float x; - public float y; - // pivot point for transformations - public float px; - public float py; +// public float px; +// public float py; public PShape() { + this.kind = GROUP; + } + + + public PShape(int kind) { + this.kind = kind; } - public PShape(float x, float y) { - this.x = x; - this.y = y; + public void setName(String name) { + this.name = name; + } + + + public String getName() { + return name; + } + + + public boolean isVisible() { + return visible; + } + + + public void setVisible(boolean visible) { + this.visible = visible; + } + + + // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + + + protected void checkBounds() { + if (width == 0 || height == 0) { + // calculate bounds here (also take kids into account) + width = 1; + height = 1; + } + } + + + public float getWidth() { + checkBounds(); + return width; + } + + + public float getHeight() { + checkBounds(); + return height; } // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . - + /** - * Called by the following (the shape() command adds the g) - * PShape s = loadShapes("blah.svg"); - * shape(s); + * Set the orientation for drawn objects, similar to PImage.imageMode(). + * @param which Either CORNER, CORNERS, or CENTER. */ - public void draw(PGraphics g) { - boolean flat = g instanceof PGraphics3D; + public void drawMode(int which) { + drawMode = which; + } + + boolean strokeSaved; + int strokeColorSaved; + float strokeWeightSaved; + int strokeCapSaved; + int strokeJoinSaved; + + boolean fillSaved; + int fillColorSaved; + + int ellipseModeSaved; + + + protected void pre(PGraphics g) { if (matrix != null) { + boolean flat = g instanceof PGraphics3D; + g.pushMatrix(); if (flat) { g.applyMatrix(matrix.m00, matrix.m01, matrix.m02, @@ -135,50 +203,217 @@ public class PShape { } } - // if g subclasses PGraphics2, ignore all lighting stuff and z coords - // otherwise if PGraphics3, need to call diffuse() etc + strokeSaved = g.stroke; + strokeColorSaved = g.strokeColor; + strokeWeightSaved = g.strokeWeight; + strokeCapSaved = g.strokeCap; + strokeJoinSaved = g.strokeJoin; - // unfortunately, also a problem with no way to encode stroke/fill - // being enabled/disabled.. this quickly gets into just having opcodes - // for the entire api, to deal with things like textures and images + fillSaved = g.fill; + fillColorSaved = g.fillColor; - switch (kind) { - case PATH: - for (int i = 0; i < opcodeCount; i++) { - switch (opcode[i]) { - case VERTEX: - break; - } - } - break; + ellipseModeSaved = g.ellipseMode; - case GROUP: - break; - - case RECT: - break; + if (styles) { + styles(g); } + } + + + protected void styles(PGraphics g) { + // should not be necessary because using only the int version of color + //parent.colorMode(PConstants.RGB, 255); + + if (stroke) { + g.stroke(strokeColor); + g.strokeWeight(strokeWeight); + g.strokeCap(strokeCap); + g.strokeJoin(strokeJoin); + } else { + g.noStroke(); + } + + if (fill) { + //System.out.println("filling " + PApplet.hex(fillColor)); + g.fill(fillColor); + } else { + g.noFill(); + } + } + + + public void post(PGraphics g) { + for (int i = 0; i < childCount; i++) { + children[i].draw(g); + } + + // TODO this is not sufficient, since not saving fillR et al. + g.stroke = strokeSaved; + g.strokeColor = strokeColorSaved; + g.strokeWeight = strokeWeightSaved; + g.strokeCap = strokeCapSaved; + g.strokeJoin = strokeJoinSaved; + + g.fill = fillSaved; + g.fillColor = fillColorSaved; + + g.ellipseMode = ellipseModeSaved; if (matrix != null) { g.popMatrix(); } } + + + /** + * Called by the following (the shape() command adds the g) + * PShape s = loadShapes("blah.svg"); + * shape(s); + */ + public void draw(PGraphics g) { + if (!visible) return; + + if (drawMode == PConstants.CENTER) { + g.pushMatrix(); + g.translate(-width/2, -height/2); + } + + pre(g); + drawImpl(g); + post(g); + + if (drawMode == PConstants.CENTER) { + g.popMatrix(); + } + } + + + /** + * Convenience method to draw at a particular location. + */ + public void draw(PGraphics g, float x, float y) { + if (!visible) return; + + g.pushMatrix(); + + if (drawMode == PConstants.CENTER) { + g.translate(x - width/2, y - height/2); + + } else if ((drawMode == PConstants.CORNER) || + (drawMode == PConstants.CORNERS)) { + g.translate(x, y); + } + pre(g); + drawImpl(g); + post(g); + + g.popMatrix(); + } + + + public void draw(PGraphics g, float x, float y, float c, float d) { + if (!visible) return; + + g.pushMatrix(); + + if (drawMode == PConstants.CENTER) { + // x and y are center, c and d refer to a diameter + g.translate(x - c/2f, y - d/2f); + g.scale(c / width, d / height); + + } else if (drawMode == PConstants.CORNER) { + g.translate(x, y); + g.scale(c / width, d / height); + + } else if (drawMode == PConstants.CORNERS) { + // c and d are x2/y2, make them into width/height + c -= x; + d -= y; + // then same as above + g.translate(x, y); + g.scale(c / width, d / height); + } + pre(g); + drawImpl(g); + post(g); + + g.popMatrix(); + } + + + /** + * Draws the SVG document. + */ + abstract public void drawImpl(PGraphics g); // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + + public int getChildCount() { + return childCount; + } + + + public PShape getChild(int index) { + return children[index]; + } + + + public PShape getChild(String name) { + if (table != null) { + for (String n : table.keySet()) { + if (n.equals(name)) { + return table.get(name); + } + } + } + for (int i = 0; i < childCount; i++) { + PShape found = children[i].getChild(name); + if (found != null) return found; + } + return null; + } + + + /** + * 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. + */ + public PShape findChild(String name) { + if (parent == null) { + return getChild(name); + + } else { + return parent.findChild(name); + } + } + // can't be 'add' because that suggests additive geometry public void addChild(PShape who) { + if (children == null) { + children = new PShape[2]; + } + if (childCount == children.length) { + children = (PShape[]) PApplet.expand(children); + } + children[childCount++] = who; + who.parent = this; + + if (table == null) { + table = new HashMap(); + } + table.put(who.getName(), who); } - public PShape createGroup() { - PShape group = new PShape(); - group.kind = GROUP; - addChild(group); - return group; - } +// public PShape createGroup() { +// PShape group = new PShape(); +// group.kind = GROUP; +// addChild(group); +// return group; +// } // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . @@ -200,9 +435,6 @@ public class PShape { } - // - - public void rotateX(float angle) { rotate(angle, 1, 0, 0); } @@ -232,10 +464,12 @@ public class PShape { scale(s, s, s); } + public void scale(float sx, float sy) { scale(sx, sy, 1); } + public void scale(float x, float y, float z) { checkMatrix(); matrix.scale(x, y, z); @@ -245,6 +479,10 @@ public class PShape { // + public void resetMatrix() { + } + + public void applyMatrix(float n00, float n01, float n02, float n10, float n11, float n12) { checkMatrix(); @@ -254,6 +492,7 @@ public class PShape { 0, 0, 0, 1); } + public void applyMatrix(float n00, float n01, float n02, float n03, float n10, float n11, float n12, float n13, float n20, float n21, float n22, float n23, @@ -287,17 +526,17 @@ public class PShape { * This will also need to flip the y axis (scale(1, -1)) in cases * like Adobe Illustrator where the coordinates start at the bottom. */ - public void center() { - } +// public void center() { +// } /** * Set the pivot point for all transformations. */ - public void pivot(float x, float y) { - px = x; - py = y; - } +// public void pivot(float x, float y) { +// px = x; +// py = y; +// } // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . diff --git a/todo.txt b/todo.txt index 5cfed0830..926b91432 100644 --- a/todo.txt +++ b/todo.txt @@ -16,14 +16,14 @@ X build it and then exit X notations have been added to the bug report that cover the plw changes X Toolmenu won't show until I compile Mangler X http://dev.processing.org/bugs/show_bug.cgi?id=892 - -_ transport error 208 -_ http://dev.processing.org/bugs/show_bug.cgi?id=895 -_ possible http://www.jetbrains.net/jira/browse/IDEADEV-4268 - X update quaqua to 4.4.7 on macosx (http://www.randelshofer.ch/quaqua/) X now supports 10.5 an 64 bit jnilib +_ video capture problems with opengl +_ http://dev.processing.org/bugs/show_bug.cgi?id=882 +_ serial.available() broken with video +_ http://dev.processing.org/bugs/show_bug.cgi?id=829 + _ add local variables to PdeRecognizer _ remove static methods in PdePreprocessor, instead pull things from recog @@ -59,6 +59,8 @@ _ but: open() is platform specific anyway, ppl can use exec() [ needs verification ] +_ transport error 202 +_ http://dev.processing.org/bugs/show_bug.cgi?id=895 _ check to see whether this bug is fixed once 0140 is released _ properly handle non-ascii chars in p5 folder name _ http://dev.processing.org/bugs/show_bug.cgi?id=49