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);  
    bList.add(myBezier); 
    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 
//