# Bezier Curve move weight points

I have a java code which moves control points (Corner points) according to the mouse movement. I need to modify the code so that it moves the weight points (the points in between the control points) according to the mouse drag.

PGraphics pg;
float scale, dW;
float [][] pt;
float [][] wpt;
boolean pressed;
int selected;

// Bezier curve
int order;

void setup() {
// setup the screen
size(600,400);
pg = createGraphics(600,400);
// scale of the image
scale = 150;

// prepare the Bezier curve
order = 3;
// declare the control polygon
pt = new float[order + 1][3];
// delcare the weight points
wpt = new float[order][3];

// initialize the control polygon
float angle;
int i;
for(i = 0; i <= order; i += 1) {
angle = 1.25*PI-1.5*PI*i/order;
pt[i] = new float[] { 1, cos(angle), sin(angle) };
}

// set mouse to not pressed
pressed = false;

// initialize selected point: nothing = -1
selected = -1;

// initialize delta Weight
dW = 0.05;
}

// convert from world coordinate to image coordinates x
float convertX(float x) {
return 300 + scale*x;
}

// convert from image to world coordinates x
float convertXback(float x) {
return (x - 300)/scale;
}

// convert from world coordinate to image coordinates y
float convertY(float y) {
return 200 - scale*y;
}

// convert from image to world coordinate y
float convertYback(float y) {
return (200 - y)/scale;
}

// draw edge of polygon
void drawLine(float[] p0, float[] p1) {
pg.line(convertX(p0[1]/p0[0]), convertY(p0[2]/p0[0]), convertX(p1[1]/p1[0]), convertY(p1[2]/p1[0]));
}

// draw control polygon
void drawPolygon(float[][] pol, int n) {
for(int i = 0; i < n; i += 1) {
drawLine(pol[i], pol[i+1]);
}
}

// make nice circles around points
void drawPoints(float pol[][], int n) {
for(int i = 0; i <= n; i += 1) {
pg.ellipse(convertX(pol[i][1]/pol[i][0]), convertY(pol[i][2]/pol[i][0]), 8, 8);
}
}

// interpolate two points at value t
float[] interpolate(float[] p0, float[] p1) {
return new float[] { (p0[0]+p1[0])/2, (p0[1]+p1[1])/2, (p0[2]+p1[2])/2 };
}

// compute one step in the deCasteljau algorithm, i is the number of the step (order-i) is the total number of points
float[][] deCasteljauStep(float [][] B, int i) {
float[][] Bnext = new float[order-i][3];
for(int j = 0; j < order-i; j += 1) {
Bnext[j] = interpolate(B[j], B[j+1]);
}
return Bnext;
}

// full deCasteljau algorithm
void deCasteljau(float[][] pol, int level) {

// initialise left and right control polygon
float[][] lPol = new float[order+1][3];
float[][] rPol = new float[order+1][3];

// first and last point remain the same
lPol[0] = pol[0];
rPol[order] = pol[order];

for(int i = 1; i <= order; i += 1) {
pol = deCasteljauStep(pol, i-1);
lPol[i] = pol[0];
rPol[order-i] = pol[order-i];
}

if(level>0) {
deCasteljau(lPol, level-1);
deCasteljau(rPol, level-1);
} else {
drawPolygon(lPol, order);
drawPolygon(rPol, order);
}
}

// move control point
void moveControlPoint() {
if(mousePressed) {
// x and y of mouse in world coordinates
float mx = convertXback(mouseX);
float my = convertYback(mouseY);

// if just pressed go through all control points and select closest one
if(pressed == false) {
pressed = true;

// initialize selected point to be -1
selected = -1;

// variables to keep track of distance
float dist = 999999999;
float dist0;

for(int i=0; i <= order; i += 1) {
// use square of distance instead of distance to minimize computations
dist0 = sq(pt[i][1]/pt[i][0]-mx) + sq(pt[i][2]/pt[i][0]-my);
if(dist0 < dist) {
dist = dist0;
selected = i;
}
}
} // end if pressed

// change the selected control point to mouse location
float w = pt[selected][0];

// change weight
if(keyPressed) {
switch(keyCode) {
case UP:
w += dW;
break;
case DOWN:
w -= dW;
// make sure w is always positive (but not 0)
if(w<=0) {
w = 0.00001;
}
break;
}
}

pt[selected] = new float[] { w, w*mx, w*my };

} else { // end if pressed
pressed = false;
}
}

// calculate weight points
void calcWpt() {
for(int i = 0; i < order; i += 1) {
wpt[i][0] = pt[i][0]+pt[i+1][0];
wpt[i][1] = pt[i][1]+pt[i+1][1];
wpt[i][2] = pt[i][2]+pt[i+1][2];
}
}

void draw() {
// start drawing
pg.beginDraw();
pg.background(200);
pg.fill(200);

// draw control polygon
pg.strokeWeight(2);
drawPolygon(pt, order);
// draw Bezier curve
pg.strokeWeight(4);
deCasteljau(pt,8);
//draw control points
pg.strokeWeight(2);
drawPoints(pt, order);

// calculate the weight points
calcWpt();
pg.strokeWeight(1);
drawPoints(wpt, order-1);

// move selected control point
moveControlPoint();

// end drawing
pg.endDraw();
image(pg,0,0);
}

I believe that @Chrisir has written a number of graphical interfaces for 2D (and possibly 3D?) curve editing â€“ perhaps he can share some links and / or give you advice on your method.

See his gallery post:

Related past discussions:

I canâ€™t remember, but the ProScene library might also have some curve editing tools in it?

There is also an old (2009) tool called TimeLine which might be interesting to look at: its bezier curve interface is similar to one I used before in Quartz Composer.

http://www.drifkin.net/timeline/#timeline

For a simple Java-style curve editor that runs in processing.js (but no control points) see:

2 Likes

In addition to my post (that has been linked above) a small sketch.

Chrisir

ArrayList<SimplePoint> bList = new ArrayList();

boolean runPoints=false;
float  t;

// drag with mouse
SimplePoint holding;
boolean hold=false;

void setup() {
size(800, 800);
bList = new ArrayList();
background(0);
}

void draw() {
background(0);

// Run green ball
if (runPoints&&bList.size()==4) {
displayRunningBall();
}

// decoration and showing curve ---------
// show points
int i_show_points=0;
for (SimplePoint bz : bList) {
bz.display( i_show_points );
i_show_points++;
}

// show Bezier
if (bList.size()==4) {
showBezier();
}

// text
fill(255);
text("Enter 4 points with the mouse (0 and 3 are anchors, 1 and 2 control points). \n"
+"Then hit Enter to run a ball on the curve (Enter to stop). C to clear list.",
20, 20);

// drag mouse
if (hold) {
holding.x=mouseX;
holding.y=mouseY;
}//if
}//func

// ---------------------------------------------------------------------

void keyPressed() {
if (keyCode == ENTER||keyCode == RETURN) {
runPoints = !runPoints;
// reset
t=0;
} else if (key=='c') {
bList.clear();
}
}

void mousePressed() {
// Do we have 4 points already?
if (bList.size()<4) {
// No
SimplePoint myBezier = new SimplePoint(mouseX, mouseY);
runPoints=false;
} else
{
// Yes, drag with mouse
for (SimplePoint bz : bList) {
if (dist(mouseX, mouseY, bz.x, bz.y) < 10) {
hold=true;
holding=bz;
}
}
}//else
}//

void mouseReleased() {
hold    = false;
holding = null;
}

// -------------------------------------------------------------------

void displayRunningBall() {

int i=0;
SimplePoint a1Point=(SimplePoint)bList.get(i);
SimplePoint c1Point=(SimplePoint)bList.get(i+1);
SimplePoint c2Point=(SimplePoint)bList.get(i+2);
SimplePoint a2Point=(SimplePoint)bList.get(i+3);

float x = bezierPoint(a1Point.x, c1Point.x, c2Point.x, a2Point.x, t/10);
float y = bezierPoint(a1Point.y, c1Point.y, c2Point.y, a2Point.y, t/10);

noStroke();
fill(0, 255, 0);
ellipse(x, y, 10, 10);

strokeWeight (1);
t += 0.1;
if (t>10) {
t=0;
}
}// func

void showBezier() {
int i2=0;
SimplePoint a1Point=(SimplePoint)bList.get(i2);
SimplePoint c1Point=(SimplePoint)bList.get(i2+1);
SimplePoint c2Point=(SimplePoint)bList.get(i2+2);
SimplePoint a2Point=(SimplePoint)bList.get(i2+3);

stroke(255);
noFill();
bezier ( a1Point.x, a1Point.y,
c1Point.x, c1Point.y,
c2Point.x, c2Point.y,
a2Point.x, a2Point.y);
}//func

// ===================================================================

class SimplePoint {

float x;
float y;

SimplePoint(float _x, float _y ) {
x = _x;
y = _y;
}

void display( int i ) {
stroke(255);
point(x, y);
point(x+1, y+1);
fill(255);
text(i,
x+4, y+5);
}
}//class
//