Here's a very convoluted demonstration, but it uses draggable objects so you can see how it works better.
You can model your curve on a bezier path. The code below demonstrates a spring which sits on a bezier curve. A bezier curve is drawn by calculating the points on a path dependant on 4 co-ords. You use a value called "t" and modify that value between 0.0 and 1.0 to get all the points along the path to make your curve. The command bezierPoint() does all this for you.
Use the space bar to see the spring in action and use "p" if you want a print out of the curve you've made.
Code:
float M = 0.8; // Mass
float K = 0.21; // Spring constant
float D = 0.92; // Damping
float R = 0.5; // Rest position
// Spring simulation variables
float ps = 0.5; // Position
float vs = 0.0; // Velocity
float as = 0; // Acceleration
float f = 0; // Force
Draggable [] anchor = new Draggable[2];
Draggable [] control = new Draggable[2];
Draggable ball;
void setup(){
size(400,400,P3D);
anchor[0] = new Draggable (100,100,10,color(100,100,200),color(100,200,200),"rect");
anchor[1] = new Draggable (300,300,10,color(100,100,200),color(100,200,200),"rect");
control[0] = new Draggable (100,300,10,color(100,100,200),color(100,200,200),"ellipse");
control[1] = new Draggable (300,100,10,color(100,100,200),color(100,200,200),"ellipse");
float y = bezierPoint(anchor[0].y,control[0].y,control[1].y,anchor[1].y,0.5);
float x = bezierPoint(anchor[0].x,control[0].x,control[1].x,anchor[1].x,0.5);
ball = new Draggable (x,y,10,color(100,100,200),color(100,200,200),"ellipse");
ellipseMode(CENTER);
rectMode(CENTER);
}
void draw(){
background(144,130,140);
stroke(0);
bezier(
anchor[0].x,anchor[0].y,control[0].x,control[0].y,
control[1].x,control[1].y,anchor[1].x,anchor[1].y);
for (int i = 0; i < 2; i++){
anchor[i].draw();
control[i].draw();
anchor[i].update();
control[i].update();
ball.draw();
ball.updateSpring(
anchor[0].x,anchor[0].y,control[0].x,control[0].y,
control[1].x,control[1].y,anchor[1].x,anchor[1].y);
}
if(keyPressed){
switch(key){
case ' ':
ball.t = constrain(ball.t + 0.05, 0.0, 1.0);
break;
case 'p':
//print out co-ords of bezier path (rather than figure it out)
println("bezier("+
anchor[0].x+", "+anchor[0].y+", "+control[0].x+", "+control[0].y+", "+
control[1].x+", "+control[1].y+", "+anchor[1].x+", "+anchor[1].y+");");
}
}
}
class Draggable {
float x,y,t;
int size,c1,c2;
String mode;
boolean locked = false;
Draggable (float x, float y, int size, color c1, color c2, String mode) {
this.x = x;
this.y = y;
this.size = size;
this.c1 = c1;
this.c2 = c2;
this.mode = mode;
t = 0.5;
}
void draw(){
noStroke();
if (locked || over()){
fill(c1);
}
else{
fill(c2);
}
if (mode == "rect"){
stroke(c1);
rect(x,y,size*2,size*2);
}
if (mode == "ellipse"){
stroke(c1);
ellipse(x,y,size*2,size*2);
}
}
void updateSpring(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4){
// Update the spring position
if(!keyPressed) {
f = -K * (t - R); // f=-ky
as = f / M; // Set the acceleration, f=ma == a=f/m
vs = D * (vs + as); // Set the velocity
t = t + vs; // Updated position
}
if(abs(vs) < 0.1) {
vs = 0.0;
}
x = bezierPoint(x1, x2, x3, x4, t);
y = bezierPoint(y1, y2, y3, y4, t);
}
void update(){
if (over() && mousePressed){
locked = true;
}
if (locked){
x = mouseX;
y = mouseY;
}
if (!mousePressed){
locked = false;
}
}
boolean over(){
if (mouseX <= x+size && mouseX >= x-size && mouseY <= y+size && mouseY >= y-size){
return true;
}
else{
return false;
}
}
}
There's possibly another method, but I hope this gives you a kick start.