User Controlled Curve Points

Making an interactive curve with Processing.js

Click and drag points to control curve

Controlling points

While I was building the mouse driven graphic equaliser, I was experimenting with the way Processing.js drew curves on the canvas and thought it would be helpful to build a curve with interactive points. Even though it is not object oriented code that you can plug into anything, I liked the results enough to think it was worth sharing.

void setup(){
  size(320, 240);
  strokeWeight(2);
  stroke(255);
  noFill();    
}

// Control Point Data
int[][] control = new int[4][2];
int[] ctrlSz = new int[4];
int controlSize = 10;
int n, thisN = -1, pushN;
    
for (n = 0; n < control.length; n ++){
  control[n][0]=((width/2)/(control.length-1)*n)+(width/4);
  ctrlSz[n] = controlSize;
}

control[0][1]=height/2; control[1][1]=50;
control[2][1]=50; control[3][1]=height/2;
    
// Control Points Colors
int[][] cCol = new int[4][4];
cCol[0]=[255,0,0,200]; cCol[1]=[255,255,0,200];
cCol[2]=[0,255,0,200]; cCol[3]=[0,100,255,200];  

// Mouse Vars
int mouseDown = 0, dragging = -1, lastDragging = 0;

void draw(){
  background(100);
  
  noFill();
  curveTightness(0.2);
  beginShape();
    curveVertex(-50,height/2);
    curveVertex(0,height/2);        
    for (n = 0; n < control.length; n ++){ 
    curveVertex(control[n][0], control[n][1]) ;}          
    curveVertex(width,height/2);
    curveVertex(width+50,height/2);      
  endShape();          
  
  for (n = 0; n < control.length; n ++){
    fill(cCol[n][0], cCol[n][1], cCol[n][2], cCol[n][3]);    
    rect(control[n][0] - ctrlSz[n] /2, control[n][1] - ctrlSz[n] /2, 
      ctrlSz[n], ctrlSz[n]);
  }
}

void hoverDetect(){   
   pushN=-1;
   for (n = 0; n < control.length; n ++){
    if (mouseX > control[n][0] - controlSize *2
        && mouseY > control[n][1] - controlSize *2
        && mouseX < control[n][0] + controlSize *2
        && mouseY < control[n][1] + controlSize *2){
          pushN=n;
          ctrlSz[n]= controlSize *2;                           
    } else { ctrlSz[n]= controlSize; }
  }
  return pushN;
}

void mouseMoved(){ hoverDetect() }

void mousePressed(){       
  if (dragging==-1){
    thisN = hoverDetect();
    dragging=thisN;
  }
}

void mouseReleased() { dragging = -1 }

void mouseDragged(){  
  if (dragging>-1){
    control[dragging][0] = mouseX;
    control[dragging][1] = mouseY;
  }
}


Code for the example above.

The Source

The code basically uses a multi-dimensional array to store the X & Y of the control points. When the control points are hovered over they expand so the user has a visual cue that they are interacting with the curve.

I had two main obstacles when writing this script. Firstly... if you dragged one point across another, it would pick up the second point for moving and dropped the first one. This meant I had to let the script know which point I started dragging, and would not let any other point get picked up until the last point had been dropped.

The second obstacle was that I still wanted the unfocused points to have a hover state when I dragged the current point over them. To achieve this I was forced to loop through every point to see if it was being hovered over and return the variable pushN afterwards to the mouseDragged() function.

This created flexibility in the hoverDetect() routine, allowing all the points to be in their hover state at once. If you want to test this, try bunching three control points up together and then dragging the forth point over the cluster. You will see that all four points hover.