// © 2010 Mike "Pomax" Kamermans - projects.nihongoresources.com /** * Simple coordinate class */ class Coord { int x,y; Coord(int x, int y) { this.x=x; this.y=y; }} /** * Simple point curve Point class * - plain points only have "main" set * - bezier points have "left" and "right" set as control points * A bezier curve from p1 to p2 will consist of {p1, p1.right, p2.left, p2} */ class Point { Coord left, main, right; boolean isplain = true; Point(int x, int y) { main = new Coord(x,y); } void setLeft(int x, int y) { isplain=false; left = new Coord(x,y); } void setRight(int x, int y) { isplain=false; right = new Coord(x,y); } boolean isPlain() { return isplain; } Coord getPoint() { return main; } Coord getLeft() { return isplain ? main : left==null? main : left; } Coord getRight() { return isplain ? main : right==null? main : right; } } /** * A shape defined in terms of curve points */ class PointCurveShape { ArrayList points; PointCurveShape() { points = new ArrayList(); } Point current() { return (Point) points.get(points.size()-1); } void addPoint(int x, int y) { points.add(new Point(x,y)); } void setLeft(int x, int y) { current().setLeft(x,y); } void setRight(int x, int y) { current().setRight(x,y); } /** * Convert the point curve to an SVG path string * (bidirectional conversion? You better believe it). * This code is very similar to the draw method, * since it effectively does the same thing. */ String toSVG() { // first vertex Coord first = ((Point)points.get(0)).getPoint(); int x = first.x; int y = first.y; String svg = "M" + x + (y<0? y : " " + y); // rest of the shape for(int p=1; p0) { prev = (Point) points.get(p-1); } else { prev = (Point) points.get(points.size()-1); } Point curr = (Point) points.get(p); // if both are plain, LineTo. Otherwise, Cubic bezier. if(curr.isPlain() && prev.isPlain()) { int lx = curr.getPoint().x; int ly = curr.getPoint().y; svg += "L" + lx + (ly<0? ly : " " + ly); } else { int cx1 = prev.getRight().x; int cy1 = prev.getRight().y; int cx2 = curr.getLeft().x; int cy2 = curr.getLeft().y; int x2 = curr.getPoint().x; int y2 = curr.getPoint().y; svg += "C" + cx1 + (cy1<0? cy1 : " " + cy1) + (cx2<0? cx2 : " " + cx2) + (cy2<0? cy2 : " " + cy2) + (x2<0? x2 : " " + x2) + (y2<0? y2 : " " + y2); }} return svg + "Z"; } /** * Drawing methods */ int marknum = 1; // show on-curve coordinates (purely cosmetic) void mark(int x, int y) { ellipse(x, y, 10, 10); line(x, y-5, x, y+5); line(x-5, y, x+5, y); // font units run "upside down" so the image is flipped. // that means any text needs "reflipping" scale(1,-1); text(""+marknum++,x+5,-y); scale(1,-1); } // show off-curve coordinates (purely cosmetic) void bline(int x1, int y1, int x2, int y2) { if(x1!=x2 || y1!=y2) { stroke(0,0,255); line(x1,y1,x2,y2); ellipse(x2,y2,4,4); stroke(0); }} // draw the shape void draw() { fill(random(255),random(255),random(255), 120); beginShape(); // place first vertex Coord first = ((Point)points.get(0)).getPoint(); vertex(first.x, first.y); mark(first.x, first.y); // then place the rest of the shape for(int p=1; p0) { prev = (Point) points.get(p-1); } else { prev = (Point) points.get(points.size()-1); } Point curr = (Point) points.get(p); // if both are plain, vertex. Otherwise, bezier vertex if(curr.isPlain() && prev.isPlain()) { vertex(curr.getPoint().x, curr.getPoint().y); } else { bezierVertex(prev.getRight().x,prev.getRight().y, curr.getLeft().x, curr.getLeft().y, curr.getPoint().x, curr.getPoint().y); } // and some (essentially irrelevant) cosmetics mark(curr.getPoint().x, curr.getPoint().y); bline(prev.getPoint().x, prev.getPoint().y, prev.getRight().x, prev.getRight().y); bline(curr.getPoint().x, curr.getPoint().y, curr.getLeft().x, curr.getLeft().y); } // not unimportant: close the shape at the end of the construction procedure endShape(CLOSE); } }