/* bezier_mc_multiple_fix Interactive Bezier Drawing Program Michael Chladil 2008 02 05 Description: Clicking on four new points in the stage will generate a Bezier curve using the specified control points. Clicking and dragging any existing control point will move it. References: Interactive Selection http://www.genarts.com/karl/papers/siggraph91.html Daniel Shiffman Drawing Machines Bezier Curve http://itp.nyu.edu/~dn525/DrawingMachines/Class2.zip ArrayList Documentation http://java.sun.com/j2se/1.4.2/docs/api/java/util/ArrayList.html Point Documentation http://java.sun.com/j2se/1.4.2/docs/api/java/awt/Point.html */ final int SEL_RAD = 2; // selection box radius class Button { Rectangle r; //button's rectangle // String txt; //button's text boolean clicked; //did i click on it? boolean rollover; //did i rollover it? boolean dragging; Button(int x, int y, int w, int h) { r = new Rectangle(x,y,w,h); dragging = false; // txt = s; } void render() { //draw rectangle and text based on whether rollover or clicked rectMode(CORNER); stroke(0.75); noFill(); if (rollover) fill(0.5); if (clicked) fill(1.0); rect(r.x,r.y,r.width,r.height); float b = 0.0; if (clicked) b = 0.25f; else if (rollover) b = 0.75f; else b = 0.5; // stroke(b); fill(b); // textAlign(LEFT); // text(txt,r.x+10,r.y+14); } void move (int mx, int my) { r.x = mx; r.y = my; } //methods to check rollover, clicked, or released (must be called from appropriate //places in draw, mousePressed, mouseReleased boolean rollover(int mx, int my) { if (r.contains(mx,my)) rollover = true; else rollover = false; return rollover; } boolean clicked(int mx, int my) { if (r.contains(mx,my)) clicked = true; return clicked; } void released() { clicked = false; dragging = false; } } class InteractiveBezier { Point ControlPoints[]; Button ControlPointHandles[]; InteractiveBezier (int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) { ControlPoints = new Point[4]; ControlPoints[0] = new Point (x1, y1); ControlPoints[1] = new Point (x2, y2); ControlPoints[2] = new Point (x3, y3); ControlPoints[3] = new Point (x4, y4); ControlPointHandles = new Button[4]; ControlPointHandles[0] = new Button (x1 - SEL_RAD, y1 - SEL_RAD, SEL_RAD * 2, SEL_RAD * 2); ControlPointHandles[1] = new Button (x2 - SEL_RAD, y2 - SEL_RAD, SEL_RAD * 2, SEL_RAD * 2); ControlPointHandles[2] = new Button (x3 - SEL_RAD, y3 - SEL_RAD, SEL_RAD * 2, SEL_RAD * 2); ControlPointHandles[3] = new Button (x4 - SEL_RAD, y4 - SEL_RAD, SEL_RAD * 2, SEL_RAD * 2); } void render() { for (int i = 0; i < 4; i++) ControlPointHandles[i].render(); DMBezier (ControlPoints[0].x, ControlPoints[0].y, ControlPoints[1].x, ControlPoints[1].y, ControlPoints[2].x, ControlPoints[2].y, ControlPoints[3].x, ControlPoints[3].y); // illustrate the control points stroke(255, 100, 0); line(ControlPoints[0].x, ControlPoints[0].y, ControlPoints[1].x, ControlPoints[1].y); line(ControlPoints[2].x, ControlPoints[2].y, ControlPoints[3].x, ControlPoints[3].y); } boolean rollover (int mx, int my) { for (int i = 0; i < 4; i++) if (ControlPointHandles[i].rollover (mx, my)) return true; return false; } void drag (int mx, int my) { for (int i = 0; i < 4; i++) if (ControlPointHandles[i].dragging) { ControlPointHandles[i].move (mx, my); ControlPoints[i].setLocation (mx, my); } } boolean clicked (int mx, int my) { for (int i = 0; i < 4; i++) if (ControlPointHandles[i].clicked (mx, my)) { ControlPointHandles[i].dragging = true; return true; } return false; } void released () { for (int i = 0; i < 4; i++) ControlPointHandles[i].released (); } // if you've generalized the bresenham algorithm why not use it here? // this is a quadratic bezier curve private void DMBezier(float p0x, float p0y, float cp0x, float cp0y, float cp1x, float cp1y, float p1x, float p1y) { stroke (0); // B(t) = ((1-t)^3 * P0) + (3t(1-t)^2 * P1) + (3(t^2)(1-t) * P3) + (t^3 * P4) float delta = 0.02; // the starting point float lx = p0x, ly = p0y; float cx, cy; for(float t = 0; t <= 1.0; t += delta) { // calc the next point cx = (pow(1-t,3)*p0x) + (3*t*pow(1-t,2)*cp0x) + (3*pow(t,2)*(1-t)*cp1x) + (pow(t,3)*p1x); cy = (pow(1-t,3)*p0y) + (3*t*pow(1-t,2)*cp0y) + (3*pow(t,2)*(1-t)*cp1y) + (pow(t,3)*p1y); // draw a segment line(lx, ly, cx, cy); // store the last points lx = cx; ly = cy; } } } ArrayList Curves; ArrayList TempPoints; void setup() { size(320, 100); frameRate(15); smooth(); Curves = new ArrayList(); TempPoints = new ArrayList(); // Curves.add (new InteractiveBezier (50, 50, 50, 150, 150, 50, 150, 150)); } void draw() { fill(255, 10); rect (0, 0, width, height); // background(255, 255, 255, 100); int tmpX = mouseX; int tmpY = mouseY; for (int i = 0; i < Curves.size(); i++) { InteractiveBezier iB = (InteractiveBezier) Curves.get (i); iB.render(); iB.rollover (tmpX, tmpY); iB.drag (tmpX, tmpY); } stroke (218, 100, 52); ellipseMode (CENTER); for (int j = 0; j < TempPoints.size (); j++) { Point tP = (Point) TempPoints.get (j); ellipse (tP.x, tP.y, (SEL_RAD * 3), (SEL_RAD * 3)); } } void mousePressed () { int tmpX = mouseX; int tmpY = mouseY; boolean CurveClicked = false; for (int i = 0; i < Curves.size(); i++) { InteractiveBezier iB = (InteractiveBezier) Curves.get (i); if (iB.clicked (tmpX, tmpY)) { CurveClicked = true; } } if (CurveClicked == false) { println ("temppoint.size()=" + TempPoints.size ()); // create a new bezier curve based on the next four points that are created. if (TempPoints.size() < 4) TempPoints.add (new Point (tmpX, tmpY)); if (TempPoints.size () == 4) { Point tmpPoints[] = new Point[4]; // retrieve points from array list and put into temporary array to pass to Bezier Constructor for (int i = 0; i < TempPoints.size (); i++) tmpPoints[i] = (Point) TempPoints.get (i); Curves.add (new InteractiveBezier ((int)tmpPoints[0].x, (int)tmpPoints[0].y, (int)tmpPoints[1].x, (int)tmpPoints[1].y, (int)tmpPoints[2].x, (int)tmpPoints[2].y, (int)tmpPoints[3].x, (int)tmpPoints[3].y)); // a happy accident here... because the click algorithm is tracking the number of created points, the final click produces a new point that // starts at the end of the last curve. TempPoints.clear (); } } } void mouseReleased () { for (int i = 0; i < Curves.size(); i++) { InteractiveBezier iB = (InteractiveBezier) Curves.get (i); iB.released (); } }