970 lines
26 KiB
Java
970 lines
26 KiB
Java
/**
|
|
Copyright 2004-2008 Ricard Marxer <email@ricardmarxer.com>
|
|
|
|
This file is part of Geomerative.
|
|
|
|
Geomerative is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
Geomerative 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 General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with Geomerative. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
package geomerative;
|
|
|
|
import processing.core.*;
|
|
import processing.data.*;
|
|
|
|
/**
|
|
* @extended
|
|
*/
|
|
public class RSVG
|
|
{
|
|
public void draw(String filename, PGraphics g)
|
|
{
|
|
this.toGroup(filename).draw(g);
|
|
}
|
|
|
|
public void draw(String filename, PApplet p)
|
|
{
|
|
this.toGroup(filename).draw(p);
|
|
}
|
|
|
|
public void draw(String filename)
|
|
{
|
|
this.toGroup(filename).draw();
|
|
}
|
|
|
|
public void saveShape(String filename, RShape shp) {
|
|
String str = fromShape(shp);
|
|
String[] strs = PApplet.split(str, "\n");
|
|
RG.parent().saveStrings(filename, strs);
|
|
}
|
|
|
|
public String fromShape(RShape shape) {
|
|
String header = "<?xml version=\"1.0\" standalone=\"no\"?>\n<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n<svg width=\"100%\" height=\"100%\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\">\n";
|
|
|
|
return header + shapeToString(shape) + "</svg>";
|
|
}
|
|
|
|
public void saveGroup(String filename, RGroup grp) {
|
|
String str = fromGroup(grp);
|
|
String[] strs = PApplet.split(str, "\n");
|
|
RG.parent().saveStrings(filename, strs);
|
|
}
|
|
|
|
public String fromGroup(RGroup group) {
|
|
String header = "<?xml version=\"1.0\" standalone=\"no\"?>\n<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n<svg width=\"100%\" height=\"100%\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\">\n";
|
|
|
|
return header + groupToString(group) + "</svg>";
|
|
}
|
|
|
|
|
|
|
|
public RGroup toGroup(String filename)
|
|
{
|
|
XML svg = null;
|
|
try{
|
|
svg = RG.parent().loadXML(filename);
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
}
|
|
|
|
if (svg == null) return new RGroup();
|
|
|
|
if (!svg.getName().equals("svg")) {
|
|
throw new RuntimeException("root is not <svg>, it's <" + svg.getName() + ">");
|
|
}
|
|
|
|
return elemToGroup(svg);
|
|
}
|
|
|
|
public float unitsToPixels(String units, float originalPxSize) {
|
|
// TODO: check if it is possible to know the dpi of a given PGraphics or device
|
|
return unitsToPixels(units, originalPxSize, 72.0f/*Toolkit.getDefaultToolkit().getScreenResolution()*/);
|
|
}
|
|
|
|
public float unitsToPixels(String units, float originalPxSize, float dpi) {
|
|
int chars = 0;
|
|
float multiplier = 1.0f;
|
|
|
|
if (units.endsWith("em")) {
|
|
chars = 2;
|
|
multiplier = 1.0f;
|
|
} else if (units.endsWith("ex")) {
|
|
chars = 2;
|
|
multiplier = 1.0f;
|
|
} else if (units.endsWith("px")) {
|
|
chars = 2;
|
|
multiplier = 1.0f;
|
|
} else if (units.endsWith("pt")) {
|
|
chars = 2;
|
|
multiplier = 1.25f;
|
|
} else if (units.endsWith("pc")) {
|
|
chars = 2;
|
|
multiplier = 15f;
|
|
} else if (units.endsWith("cm")) {
|
|
chars = 2;
|
|
multiplier = 35.43307f / 90.0f * dpi;
|
|
} else if (units.endsWith("mm")) {
|
|
chars = 2;
|
|
multiplier = 3.543307f / 90.0f * dpi;
|
|
} else if (units.endsWith("in")) {
|
|
chars = 2;
|
|
multiplier = dpi;
|
|
} else if (units.endsWith("%")) {
|
|
chars = 1;
|
|
multiplier = originalPxSize / 100.0f;
|
|
} else {
|
|
chars = 0;
|
|
multiplier = 1.0f;
|
|
}
|
|
|
|
return Float.valueOf(units.substring(0, units.length()-chars)).floatValue() * multiplier;
|
|
}
|
|
|
|
public RShape toShape(String filename)
|
|
{
|
|
XML svg = null;
|
|
try{
|
|
svg = RG.parent().loadXML(filename);
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
}
|
|
|
|
if (svg == null) return new RShape();
|
|
|
|
if ( !svg.getName().equals("svg") )
|
|
{
|
|
throw new RuntimeException("root is not <svg>, it's <" + svg.getName() + ">");
|
|
}
|
|
|
|
RShape result = elemToCompositeShape(svg);
|
|
|
|
result.origWidth = result.getWidth();
|
|
result.origHeight = result.getHeight();
|
|
|
|
if (svg.hasAttribute("width") && svg.hasAttribute("height")) {
|
|
String widthStr = svg.getString("width").trim();
|
|
String heightStr = svg.getString("height").trim();
|
|
|
|
result.width = unitsToPixels(widthStr, result.origWidth);
|
|
result.height = unitsToPixels(heightStr, result.origHeight);
|
|
} else {
|
|
result.width = result.origWidth;
|
|
result.height = result.origHeight;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public RPolygon toPolygon(String filename)
|
|
{
|
|
return toGroup(filename).toPolygon();
|
|
}
|
|
|
|
public RMesh toMesh(String filename)
|
|
{
|
|
return toGroup(filename).toMesh();
|
|
}
|
|
|
|
public String groupToString(RGroup grp) {
|
|
String result = "";
|
|
result += "<g ";
|
|
result += styleToString(grp.getStyle());
|
|
result += ">\n";
|
|
|
|
for(int i=0;i<grp.countElements();i++) {
|
|
switch(grp.elements[i].getType()){
|
|
case RGeomElem.GROUP:
|
|
result += groupToString((RGroup)grp.elements[i]);
|
|
break;
|
|
|
|
case RGeomElem.POLYGON:
|
|
result += polygonToString((RPolygon)grp.elements[i]);
|
|
break;
|
|
|
|
case RGeomElem.SHAPE:
|
|
result += shapeToString((RShape)grp.elements[i]);
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
result += "</g>\n";
|
|
return result;
|
|
}
|
|
|
|
public String polygonToString(RPolygon poly) {
|
|
String result = "";
|
|
result += shapeToString(poly.toShape());
|
|
return result;
|
|
}
|
|
|
|
public String shapeToString(RShape shp) {
|
|
String result = "";
|
|
|
|
// If it has children it is a group
|
|
result += "<g ";
|
|
result += styleToString(shp.getStyle());
|
|
result += ">\n";
|
|
|
|
if (shp.countPaths() > 0) {
|
|
result += "<path ";
|
|
result += "d=\"";
|
|
|
|
for(int i=0; i<shp.countPaths(); i++) {
|
|
RPath sushp = shp.paths[i];
|
|
boolean init = true;
|
|
for ( int j = 0; j < sushp.countCommands(); j++ ) {
|
|
RCommand cmd = sushp.commands[j];
|
|
|
|
if (init) {
|
|
result += "M" + cmd.startPoint.x + " " + cmd.startPoint.y + " ";
|
|
init = false;
|
|
}
|
|
|
|
switch( cmd.getCommandType() )
|
|
{
|
|
case RCommand.LINETO:
|
|
result += "L" + cmd.endPoint.x + " " + cmd.endPoint.y + " ";
|
|
break;
|
|
|
|
case RCommand.QUADBEZIERTO:
|
|
result += "Q" + cmd.controlPoints[0].x + " " + cmd.controlPoints[0].y + cmd.endPoint.x + " " + cmd.endPoint.y + " ";
|
|
break;
|
|
|
|
case RCommand.CUBICBEZIERTO:
|
|
result += "C" + cmd.controlPoints[0].x + " " + cmd.controlPoints[0].y + " " + cmd.controlPoints[1].x + " " + cmd.controlPoints[1].y + " " + cmd.endPoint.x + " " + cmd.endPoint.y + " ";
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (sushp.closed) {
|
|
result += "Z ";
|
|
}
|
|
}
|
|
|
|
result += "\"/>\n";
|
|
}
|
|
|
|
for (int i=0; i<shp.countChildren(); i++) {
|
|
result+=shapeToString(shp.children[i]);
|
|
}
|
|
|
|
result += "</g>\n";
|
|
return result;
|
|
}
|
|
|
|
public String styleToString(RStyle style) {
|
|
String result = " style=\"";
|
|
|
|
if (style.fillDef) {
|
|
if (!style.fill) {
|
|
result += "fill:none;";
|
|
} else {
|
|
result += "fill:#" + PApplet.hex(style.fillColor, 6) + ";";
|
|
}
|
|
}
|
|
|
|
if (style.fillAlphaDef) {
|
|
result += "fill-opacity:" + style.fillAlpha/255.0f + ";";
|
|
}
|
|
|
|
if (style.strokeDef) {
|
|
if (!style.stroke) {
|
|
result += "stroke:none;";
|
|
} else {
|
|
result += "stroke:#" + PApplet.hex(style.strokeColor, 6) + ";";
|
|
}
|
|
}
|
|
|
|
if (style.strokeAlphaDef) {
|
|
result += "stroke-opacity:" + style.strokeAlpha/255.0f + ";";
|
|
}
|
|
|
|
if (style.strokeWeightDef) {
|
|
result += "stroke-width:" + style.strokeWeight + ";";
|
|
}
|
|
|
|
|
|
if(style.strokeCapDef) {
|
|
result += "stroke-linecap:";
|
|
|
|
switch (style.strokeCap) {
|
|
case RG.PROJECT:
|
|
result += "butt";
|
|
break;
|
|
case RG.ROUND:
|
|
result += "round";
|
|
break;
|
|
case RG.SQUARE:
|
|
result += "square";
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
result += ";";
|
|
}
|
|
|
|
if(style.strokeJoinDef) {
|
|
result += "stroke-linejoin:";
|
|
|
|
switch (style.strokeJoin) {
|
|
case RG.MITER:
|
|
result += "miter";
|
|
break;
|
|
case RG.ROUND:
|
|
result += "round";
|
|
break;
|
|
case RG.BEVEL:
|
|
result += "bevel";
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
result += ";";
|
|
}
|
|
|
|
result += "\" ";
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* @invisible
|
|
*/
|
|
public RGroup elemToGroup(XML elem)
|
|
{
|
|
RGroup grp = new RGroup();
|
|
|
|
// Set the defaults SVG styles for the root
|
|
if(elem.getName().toLowerCase().equals("svg")){
|
|
grp.setFill(0); // By default in SVG it's black
|
|
grp.setFillAlpha(255); // By default in SVG it's 1
|
|
grp.setStroke(false); // By default in SVG it's none
|
|
grp.setStrokeWeight(1F); // By default in SVG it's none
|
|
grp.setStrokeCap("butt"); // By default in SVG it's 'butt'
|
|
grp.setStrokeJoin("miter"); // By default in SVG it's 'miter'
|
|
grp.setStrokeAlpha(255); // By default in SVG it's 1
|
|
grp.setAlpha(255); // By default in SVG it's 1F
|
|
}
|
|
|
|
XML elems[] = elem.getChildren();
|
|
for (int i = 0; i < elems.length; i++) {
|
|
String name = elems[i].getName().toLowerCase();
|
|
XML element = elems[i];
|
|
|
|
// Parse and create the geometrical element
|
|
RGeomElem geomElem = null;
|
|
if(name.equals("g")){
|
|
geomElem = elemToGroup(element);
|
|
|
|
}else if (name.equals("path")) {
|
|
geomElem = elemToShape(element);
|
|
|
|
}else if(name.equals("polygon")){
|
|
geomElem = elemToPolygon(element);
|
|
|
|
}else if(name.equals("polyline")){
|
|
geomElem = elemToPolyline(element);
|
|
|
|
}else if(name.equals("circle")){
|
|
geomElem = elemToCircle(element);
|
|
|
|
}else if(name.equals("ellipse")){
|
|
geomElem = elemToEllipse(element);
|
|
|
|
}else if(name.equals("rect")){
|
|
geomElem = elemToRect(element);
|
|
|
|
}else if(name.equals("line")){
|
|
geomElem = elemToLine(element);
|
|
|
|
}else if(name.equals("defs")){
|
|
// Do nothing normally we should make a hashmap
|
|
// to apply everytime they are called in the actual objects
|
|
}else{
|
|
PApplet.println("Element '" + name + "' not know. Ignoring it.");
|
|
}
|
|
|
|
// If the geometrical element has been correctly created
|
|
if((geomElem != null)){
|
|
// Transform geometrical element
|
|
if(element.hasAttribute("transform")){
|
|
String transformString = element.getString("transform");
|
|
RMatrix transf = new RMatrix(transformString);
|
|
geomElem.transform(transf);
|
|
}
|
|
|
|
// Get the id for the geometrical element
|
|
if(element.hasAttribute("id")){
|
|
geomElem.name = element.getString("id");
|
|
}
|
|
|
|
// Get the style for the geometrical element
|
|
if(element.hasAttribute("style")){
|
|
geomElem.setStyle(element.getString("style"));
|
|
}
|
|
|
|
// Get the fill for the geometrical element
|
|
if(element.hasAttribute("fill")){
|
|
geomElem.setFill(element.getString("fill"));
|
|
}
|
|
|
|
// Get the fill-linejoin for the geometrical element
|
|
if(element.hasAttribute("fill-opacity")){
|
|
geomElem.setFillAlpha(element.getString("fill-opacity"));
|
|
}
|
|
|
|
// Get the stroke for the geometrical element
|
|
if(element.hasAttribute("stroke")){
|
|
geomElem.setStroke(element.getString("stroke"));
|
|
}
|
|
|
|
// Get the stroke-width for the geometrical element
|
|
if(element.hasAttribute("stroke-width")){
|
|
geomElem.setStrokeWeight(element.getString("stroke-width"));
|
|
}
|
|
|
|
// Get the stroke-linecap for the geometrical element
|
|
if(element.hasAttribute("stroke-linecap")){
|
|
geomElem.setStrokeCap(element.getString("stroke-linecap"));
|
|
}
|
|
|
|
// Get the stroke-linejoin for the geometrical element
|
|
if(element.hasAttribute("stroke-linejoin")){
|
|
geomElem.setStrokeJoin(element.getString("stroke-linejoin"));
|
|
}
|
|
|
|
// Get the stroke-linejoin for the geometrical element
|
|
if(element.hasAttribute("stroke-opacity")){
|
|
geomElem.setStrokeAlpha(element.getString("stroke-opacity"));
|
|
}
|
|
|
|
// Get the opacity for the geometrical element
|
|
if(element.hasAttribute("opacity")){
|
|
geomElem.setAlpha(element.getString("opacity"));
|
|
}
|
|
|
|
// Get the style for the geometrical element
|
|
grp.addElement(geomElem);
|
|
}
|
|
}
|
|
|
|
// Set the original width and height
|
|
grp.updateOrigParams();
|
|
|
|
return grp;
|
|
}
|
|
|
|
/**
|
|
* @invisible
|
|
*/
|
|
public RShape elemToCompositeShape( XML elem )
|
|
{
|
|
RShape shp = new RShape();
|
|
|
|
// Set the defaults SVG styles for the root
|
|
if (elem.getName().toLowerCase().equals("svg"))
|
|
{
|
|
shp.setFill(0); // By default in SVG it's black
|
|
shp.setFillAlpha(255); // By default in SVG it's 1
|
|
shp.setStroke(false); // By default in SVG it's none
|
|
shp.setStrokeWeight(1F); // By default in SVG it's none
|
|
shp.setStrokeCap("butt"); // By default in SVG it's 'butt'
|
|
shp.setStrokeJoin("miter"); // By default in SVG it's 'miter'
|
|
shp.setStrokeAlpha(255); // By default in SVG it's 1
|
|
shp.setAlpha(255); // By default in SVG it's 1F
|
|
}
|
|
|
|
XML elems[] = elem.getChildren();
|
|
|
|
for (int i = 0; i < elems.length; i++)
|
|
{
|
|
|
|
String name = elems[i].getName();
|
|
if ( name == null ) continue;
|
|
|
|
name = name.toLowerCase();
|
|
XML element = elems[i];
|
|
|
|
// Parse and create the geometrical element
|
|
RShape geomElem = null;
|
|
if(name.equals("g")){
|
|
geomElem = elemToCompositeShape(element);
|
|
|
|
}else if (name.equals("path")) {
|
|
geomElem = elemToShape(element);
|
|
|
|
}else if(name.equals("polygon")){
|
|
geomElem = elemToPolygon(element);
|
|
|
|
}else if(name.equals("polyline")){
|
|
geomElem = elemToPolyline(element);
|
|
|
|
}else if(name.equals("circle")){
|
|
geomElem = elemToCircle(element);
|
|
|
|
}else if(name.equals("ellipse")){
|
|
geomElem = elemToEllipse(element);
|
|
|
|
}else if(name.equals("rect")){
|
|
geomElem = elemToRect(element);
|
|
|
|
}else if(name.equals("line")){
|
|
geomElem = elemToLine(element);
|
|
|
|
}else if(name.equals("defs")){
|
|
// Do nothing normally we should make a hashmap
|
|
// to apply everytime they are called in the actual objects
|
|
}else{
|
|
PApplet.println("Element '" + name + "' not know. Ignoring it.");
|
|
}
|
|
|
|
// If the geometrical element has been correctly created
|
|
if((geomElem != null)){
|
|
// Transform geometrical element
|
|
if(element.hasAttribute("transform")){
|
|
String transformString = element.getString("transform");
|
|
RMatrix transf = new RMatrix(transformString);
|
|
geomElem.transform(transf);
|
|
}
|
|
|
|
// Get the id for the geometrical element
|
|
if(element.hasAttribute("id")){
|
|
geomElem.name = element.getString("id");
|
|
}
|
|
|
|
// Get the style for the geometrical element
|
|
if(element.hasAttribute("style")){
|
|
geomElem.setStyle(element.getString("style"));
|
|
}
|
|
|
|
// Get the fill for the geometrical element
|
|
if(element.hasAttribute("fill")){
|
|
geomElem.setFill(element.getString("fill"));
|
|
}
|
|
|
|
// Get the fill-linejoin for the geometrical element
|
|
if(element.hasAttribute("fill-opacity")){
|
|
geomElem.setFillAlpha(element.getString("fill-opacity"));
|
|
}
|
|
|
|
// Get the stroke for the geometrical element
|
|
if(element.hasAttribute("stroke")){
|
|
geomElem.setStroke(element.getString("stroke"));
|
|
}
|
|
|
|
// Get the stroke-width for the geometrical element
|
|
if(element.hasAttribute("stroke-width")){
|
|
geomElem.setStrokeWeight(element.getString("stroke-width"));
|
|
}
|
|
|
|
// Get the stroke-linecap for the geometrical element
|
|
if(element.hasAttribute("stroke-linecap")){
|
|
geomElem.setStrokeCap(element.getString("stroke-linecap"));
|
|
}
|
|
|
|
// Get the stroke-linejoin for the geometrical element
|
|
if(element.hasAttribute("stroke-linejoin")){
|
|
geomElem.setStrokeJoin(element.getString("stroke-linejoin"));
|
|
}
|
|
|
|
// Get the stroke-linejoin for the geometrical element
|
|
if(element.hasAttribute("stroke-opacity")){
|
|
geomElem.setStrokeAlpha(element.getString("stroke-opacity"));
|
|
}
|
|
|
|
// Get the opacity for the geometrical element
|
|
if(element.hasAttribute("opacity")){
|
|
geomElem.setAlpha(element.getString("opacity"));
|
|
}
|
|
|
|
// Get the style for the geometrical element
|
|
shp.addChild(geomElem);
|
|
}
|
|
}
|
|
|
|
shp.updateOrigParams();
|
|
|
|
return shp;
|
|
}
|
|
|
|
/**
|
|
* @invisible
|
|
*/
|
|
public RShape elemToPolyline(XML elem)
|
|
{
|
|
RShape shp = getPolyline(elem.getString("points").trim());
|
|
|
|
shp.updateOrigParams();
|
|
|
|
return shp;
|
|
}
|
|
|
|
/**
|
|
* @invisible
|
|
*/
|
|
public RShape elemToPolygon(XML elem)
|
|
{
|
|
RShape poly = elemToPolyline(elem);
|
|
|
|
poly.addClose();
|
|
|
|
poly.updateOrigParams();
|
|
|
|
return poly;
|
|
}
|
|
|
|
/**
|
|
* @invisible
|
|
*/
|
|
public RShape elemToRect(XML elem)
|
|
{
|
|
|
|
RShape shp = getRect(elem.getFloat("x"), elem.getFloat("y"), elem.getFloat("width"), elem.getFloat("height"));
|
|
|
|
shp.updateOrigParams();
|
|
|
|
return shp;
|
|
}
|
|
|
|
/**
|
|
* @invisible
|
|
*/
|
|
public RShape elemToLine(XML elem)
|
|
{
|
|
RShape shp = getLine(elem.getFloat("x1"), elem.getFloat("y1"), elem.getFloat("x2"), elem.getFloat("y2"));
|
|
|
|
shp.updateOrigParams();
|
|
|
|
return shp;
|
|
}
|
|
|
|
|
|
/**
|
|
* @invisible
|
|
*/
|
|
public RShape elemToEllipse(XML elem)
|
|
{
|
|
RShape shp = getEllipse(elem.getFloat("cx"), elem.getFloat("cy"), elem.getFloat("rx"), elem.getFloat("ry"));
|
|
|
|
shp.updateOrigParams();
|
|
|
|
return shp;
|
|
}
|
|
|
|
|
|
/**
|
|
* @invisible
|
|
*/
|
|
public RShape elemToCircle(XML elem)
|
|
{
|
|
float r = elem.getFloat("r");
|
|
RShape shp = getEllipse(elem.getFloat("cx"), elem.getFloat("cy"), r, r);
|
|
|
|
shp.updateOrigParams();
|
|
|
|
return shp;
|
|
}
|
|
|
|
/**
|
|
* @invisible
|
|
*/
|
|
public RShape elemToShape(XML elem)
|
|
{
|
|
RShape shp = getShape(elem.getString("d"));
|
|
|
|
shp.updateOrigParams();
|
|
|
|
return shp;
|
|
}
|
|
|
|
/**
|
|
* @invisible
|
|
*/
|
|
private RShape getRect(float x, float y, float w, float h)
|
|
{
|
|
RShape shp = RShape.createRectangle(x, y, w, h);
|
|
|
|
shp.updateOrigParams();
|
|
|
|
return shp;
|
|
}
|
|
|
|
/**
|
|
* @invisible
|
|
*/
|
|
private RShape getLine(float x1, float y1, float x2, float y2)
|
|
{
|
|
RShape shp = new RShape();
|
|
|
|
shp.addMoveTo(x1, y1);
|
|
shp.addLineTo(x2, y2);
|
|
|
|
return shp;
|
|
}
|
|
|
|
|
|
/**
|
|
* @invisible
|
|
*/
|
|
private RShape getEllipse(float cx, float cy, float rx, float ry)
|
|
{
|
|
// RShape createEllipse takes as input the width and height of the ellipses
|
|
return RShape.createEllipse(cx, cy, rx*2F, ry*2F);
|
|
}
|
|
|
|
/**
|
|
* @invisible
|
|
*/
|
|
private RShape getPolyline(String s)
|
|
{
|
|
RShape poly = new RShape();
|
|
boolean first = true;
|
|
|
|
//format string to usable format
|
|
char charline[]=s.toCharArray();
|
|
for(int i=0;i<charline.length;i++)
|
|
{
|
|
switch(charline[i])
|
|
{
|
|
case '-':
|
|
if(charline[i-1] != 'e' && charline[i-1] != 'E'){
|
|
charline=PApplet.splice(charline,' ',i);
|
|
i++;
|
|
}
|
|
break;
|
|
case ',':
|
|
case '\n':
|
|
case '\r':
|
|
case '\t':
|
|
charline[i]=' ';
|
|
break;
|
|
}
|
|
}
|
|
String formatted=new String(charline);
|
|
String tags[]=PApplet.splitTokens(formatted,", ");
|
|
for(int i=0;i<tags.length;i++){
|
|
float x = PApplet.parseFloat(tags[i]);
|
|
float y = PApplet.parseFloat(tags[i+1]);
|
|
i++;
|
|
if(first){
|
|
poly.addMoveTo(x,y);
|
|
first = false;
|
|
}else{
|
|
poly.addLineTo(x,y);
|
|
}
|
|
}
|
|
return poly;
|
|
}
|
|
|
|
/**
|
|
* @invisible
|
|
*/
|
|
private RShape getShape(String s)
|
|
{
|
|
RShape shp = new RShape();
|
|
|
|
if(s == null){
|
|
return shp;
|
|
}
|
|
|
|
//format string to usable format
|
|
char charline[] = s.toCharArray();
|
|
for( int i = 0 ; i < charline.length ; i++)
|
|
{
|
|
switch(charline[i])
|
|
{
|
|
case 'M':
|
|
case 'm':
|
|
case 'Z':
|
|
case 'z':
|
|
case 'C':
|
|
case 'c':
|
|
case 'S':
|
|
case 's':
|
|
case 'L':
|
|
case 'l':
|
|
case 'H':
|
|
case 'h':
|
|
case 'V':
|
|
case 'v':
|
|
charline = PApplet.splice(charline,' ',i);
|
|
i ++;
|
|
charline = PApplet.splice(charline,' ',i+1);
|
|
i ++;
|
|
break;
|
|
|
|
case '-':
|
|
if(i>0 && charline[i-1] != 'e' && charline[i-1] != 'E'){
|
|
charline=PApplet.splice(charline,' ',i);
|
|
i++;
|
|
}
|
|
break;
|
|
case ',':
|
|
case '\n':
|
|
case '\r':
|
|
case '\t':
|
|
charline[i] = ' ';
|
|
break;
|
|
|
|
}
|
|
}
|
|
String formatted = new String(charline);
|
|
String[] tags = PApplet.splitTokens(formatted);
|
|
|
|
//PApplet.println("formatted: " + formatted);
|
|
//PApplet.println("tags: ");
|
|
//PApplet.println(tags);
|
|
|
|
//build points
|
|
RPoint curp = new RPoint();
|
|
RPoint relp = new RPoint();
|
|
RPoint refp = new RPoint();
|
|
RPoint strp = new RPoint();
|
|
|
|
char command = 'a';
|
|
|
|
for (int i=0;i<tags.length;i++)
|
|
{
|
|
char nextChar = tags[i].charAt(0);
|
|
switch(nextChar)
|
|
{
|
|
case 'm':
|
|
case 'M':
|
|
case 'c':
|
|
case 'C':
|
|
case 's':
|
|
case 'S':
|
|
case 'l':
|
|
case 'L':
|
|
case 'h':
|
|
case 'H':
|
|
case 'v':
|
|
case 'V':
|
|
i += 1;
|
|
case 'z':
|
|
case 'Z':
|
|
command = nextChar;
|
|
break;
|
|
default:
|
|
if (command == 'm') {
|
|
command = 'l';
|
|
} else if (command == 'M') {
|
|
command = 'L';
|
|
}
|
|
}
|
|
|
|
relp.setLocation(0F, 0F);
|
|
|
|
switch(command)
|
|
{
|
|
case 'm':
|
|
relp.setLocation(curp.x, curp.y);
|
|
case 'M':
|
|
i = move(shp, curp, relp, refp, strp, tags, i);
|
|
break;
|
|
|
|
case 'z':
|
|
relp.setLocation(curp.x, curp.y);
|
|
case 'Z':
|
|
shp.addClose();
|
|
break;
|
|
|
|
case 'c':
|
|
relp.setLocation(curp.x, curp.y);
|
|
case 'C':
|
|
i = curve(shp, curp, relp, refp, strp, tags, i);
|
|
break;
|
|
|
|
case 's':
|
|
relp.setLocation(curp.x, curp.y);
|
|
case 'S':
|
|
i = smooth(shp, curp, relp, refp, strp, tags, i);
|
|
break;
|
|
|
|
case 'l':
|
|
relp.setLocation(curp.x, curp.y);
|
|
case 'L':
|
|
i = line(shp, curp, relp, refp, strp, tags, i);
|
|
break;
|
|
|
|
case 'h':
|
|
relp.setLocation(curp.x, curp.y);
|
|
case 'H':
|
|
i = horizontal(shp, curp, relp, refp, strp, tags, i);
|
|
break;
|
|
|
|
case 'v':
|
|
relp.setLocation(curp.x, curp.y);
|
|
case 'V':
|
|
i = vertical(shp, curp, relp, refp, strp, tags, i);
|
|
break;
|
|
}
|
|
}
|
|
return shp;
|
|
}
|
|
|
|
private int move(RShape shp, RPoint curp, RPoint relp, RPoint refp, RPoint strp, String[] tags, int i){
|
|
shp.addMoveTo(PApplet.parseFloat(tags[i])+relp.x, PApplet.parseFloat(tags[i+1])+relp.y);
|
|
|
|
curp.setLocation(PApplet.parseFloat(tags[i])+relp.x, PApplet.parseFloat(tags[i+1])+relp.y);
|
|
refp.setLocation(curp.x,curp.y);
|
|
strp.setLocation(curp.x,curp.y);
|
|
//relp.setLocation(0F, 0F);
|
|
return i + 1;
|
|
}
|
|
|
|
private int curve(RShape shp, RPoint curp, RPoint relp, RPoint refp, RPoint strp, String[] tags, int i){
|
|
shp.addBezierTo(PApplet.parseFloat(tags[i])+relp.x, PApplet.parseFloat(tags[i+1])+relp.y, PApplet.parseFloat(tags[i+2])+relp.x, PApplet.parseFloat(tags[i+3])+relp.y, PApplet.parseFloat(tags[i+4])+relp.x, PApplet.parseFloat(tags[i+5])+relp.y);
|
|
|
|
curp.setLocation(PApplet.parseFloat(tags[i+4])+relp.x, PApplet.parseFloat(tags[i+5])+relp.y);
|
|
refp.setLocation(2.0f*curp.x-(PApplet.parseFloat(tags[i+2])+relp.x), 2.0f*curp.y-(PApplet.parseFloat(tags[i+3])+relp.y));
|
|
return i + 5;
|
|
}
|
|
|
|
private int smooth(RShape shp, RPoint curp, RPoint relp, RPoint refp, RPoint strp, String[] tags, int i){
|
|
shp.addBezierTo(refp.x, refp.y, PApplet.parseFloat(tags[i])+relp.x, PApplet.parseFloat(tags[i+1])+relp.y, PApplet.parseFloat(tags[i+2])+relp.x, PApplet.parseFloat(tags[i+3])+relp.y);
|
|
|
|
curp.setLocation(PApplet.parseFloat(tags[i+2])+relp.x, PApplet.parseFloat(tags[i+3])+relp.y);
|
|
refp.setLocation(2.0f*curp.x-(PApplet.parseFloat(tags[i])+relp.x), 2.0f*curp.y-(PApplet.parseFloat(tags[i+1])+relp.y));
|
|
return i + 3;
|
|
}
|
|
|
|
private int line(RShape shp, RPoint curp, RPoint relp, RPoint refp, RPoint strp, String[] tags, int i){
|
|
shp.addLineTo(PApplet.parseFloat(tags[i])+relp.x, PApplet.parseFloat(tags[i+1])+relp.y);
|
|
|
|
curp.setLocation(PApplet.parseFloat(tags[i])+relp.x, PApplet.parseFloat(tags[i+1])+relp.y);
|
|
refp.setLocation(curp.x, curp.y);
|
|
return i + 1;
|
|
}
|
|
|
|
private int horizontal(RShape shp, RPoint curp, RPoint relp, RPoint refp, RPoint strp, String[] tags, int i){
|
|
shp.addLineTo(PApplet.parseFloat(tags[i])+relp.x, curp.y);
|
|
|
|
curp.setLocation(PApplet.parseFloat(tags[i])+relp.x, curp.y);
|
|
refp.setLocation(curp.x, curp.y);
|
|
return i;
|
|
}
|
|
|
|
private int vertical(RShape shp, RPoint curp, RPoint relp, RPoint refp, RPoint strp, String[] tags, int i){
|
|
shp.addLineTo(curp.x, PApplet.parseFloat(tags[i])+relp.y);
|
|
|
|
curp.setLocation(curp.x, PApplet.parseFloat(tags[i])+relp.y);
|
|
refp.setLocation(curp.x, curp.y);
|
|
return i;
|
|
}
|
|
}
|