You already described all the process! You have done the hard part!
Here is an example with a bezier curve. You can play around with the handles.:
PVector[] originalPoints;
float[][] param;
int xMin, xMax;
float deltaX, deltaY;
Point[] p;
void setup() {
size(800, 600);
noFill();
background(20);
stroke(230);
initialyzeoriginalPoints();
initializeparam();
initializeBezierCurve();
beginShape();
for (int i = 0; i < originalPoints.length; i++) {
vertex(originalPoints[i].x, originalPoints[i].y);
}
endShape(CLOSE);
}
void draw() {
background(20);
// Draw the deformed shape
stroke(200, 25, 25);
strokeWeight(2);
noFill();
drawShape();
// Draw the control lines
stroke(50);
strokeWeight(2);
line(p[0].pos().x, p[0].pos().y, p[1].pos().x, p[1].pos().y);
line(p[2].pos().x, p[2].pos().y, p[3].pos().x, p[3].pos().y);
// Draw the bezier curves
noFill();
stroke(200);
strokeWeight(2);
bezier(p[0].pos().x, p[0].pos().y, p[1].pos().x, p[1].pos().y, p[2].pos().x, p[2].pos().y, p[3].pos().x, p[3].pos().y);
// Draw the points
for (int i = 0; i < 4; i++) {
p[i].show();
}
}
void mousePressed() {
// Check if mouse is over a point
for (int i = 0; i < 4; i++) {
if (p[i].isHit(mouseX, mouseY)) {
p[i].lock();
deltaX = mouseX - p[i].pos().x;
deltaY = mouseY - p[i].pos().y;
return;
}
}
}
void mouseReleased() {
for (int i = 0; i < 4; i++) {
p[i].unlock();
}
}
void mouseDragged() {
for (int i = 0; i < 4; i++) {
if (p[i].isLocked()) {
p[i].move(mouseX - deltaX, mouseY - deltaY);
}
}
}
void initialyzeoriginalPoints() {
originalPoints = new PVector[7];
originalPoints[0] = new PVector(100, 320);
originalPoints[1] = new PVector(340, 210);
originalPoints[2] = new PVector(530, 240);
originalPoints[3] = new PVector(700, 420);
originalPoints[4] = new PVector(420, 330);
originalPoints[5] = new PVector(340, 370);
originalPoints[6] = new PVector(210, 400);
xMin = 100;
xMax = 700;
}
void initializeparam() {
param = new float[7][2];
for (int i = 0; i < originalPoints.length; i++) {
param[i][0] = (originalPoints[i].x - xMin) / (float)(xMax - xMin);
param[i][1] = (float)((height / 2.0) - originalPoints[i].y);
}
}
void initializeBezierCurve() {
p = new Point[4];
p[0] = new Point(xMin, (height / 2.0), color(200, 200, 10), 10);
p[1] = new Point((xMin + xMax) / 2.0, (height / 2.0), color(200, 200, 10), 10);
p[2] = new Point((xMin + xMax) / 2.0, (height / 2.0), color(200, 200, 10), 10);
p[3] = new Point(xMax, (height / 2.0), color(200, 200, 10), 10);
}
void drawShape() {
beginShape();
for (int i = 0; i < param.length; i++) {
float bezierX = bezierPoint(p[0].pos().x, p[1].pos().x, p[2].pos().x, p[3].pos().x, param[i][0]);
float bezierY = bezierPoint(p[0].pos().y, p[1].pos().y, p[2].pos().y, p[3].pos().y, param[i][0]);
PVector dir = getTan(param[i][0]);
if (i == 3) {
println(dir.y);
}
dir.rotate(HALF_PI);
dir.normalize();
dir.mult(param[i][1]);
vertex(bezierX + dir.x, bezierY + dir.y);
}
endShape(CLOSE);
}
PVector getTan(float t) {
float resultX, resultY;
resultX = evaluateDerivativeX(t);
resultY = evaluateDerivativeY(t);
return new PVector(resultX, resultY);
}
float evaluateDerivativeX(float t) {
float temp1, temp2, temp3, temp4, temp5, temp6;
temp1 = -3 * p[0].pos().x * (1 - t) * (1 - t);
temp2 = 3 * p[1].pos().x * (1 - t) * (1 - t);
temp3 = -6 * p[1].pos().x * t * (1 - t);
temp4 = -3 * p[2].pos().x * t * t;
temp5 = 6 * p[2].pos().x * t * (1 - t);
temp6 = 3 * p[3].pos().x * t * t;
return temp1 + temp2 + temp3 + temp4 + temp5 + temp6;
}
float evaluateDerivativeY(float t) {
float temp1, temp2, temp3, temp4, temp5, temp6;
temp1 = -3 * p[0].pos().y * (1 - t) * (1 - t);
temp2 = 3 * p[1].pos().y * (1 - t) * (1 - t);
temp3 = -6 * p[1].pos().y * t * (1 - t);
temp4 = -3 * p[2].pos().y * t * t;
temp5 = 6 * p[2].pos().y * t * (1 - t);
temp6 = 3 * p[3].pos().y * t * t;
return temp1 + temp2 + temp3 + temp4 + temp5 + temp6;
}
class Point {
private PVector pos;
private color col;
private int size;
private boolean locked;
Point(float x, float y, color p_col, int p_size) {
pos = new PVector(x, y);
col = p_col;
size = p_size;
locked = false;
}
void move(float x, float y) {
pos.x = x;
pos.y = y;
}
void show() {
fill(col);
noStroke();
ellipse(pos.x, pos.y, size, size);
}
PVector pos() {
return pos;
}
void lock() {
locked = true;
}
void unlock() {
locked = false;
}
boolean isLocked() {
return locked;
}
boolean isHit(float x, float y) {
return (pos.x - x) * (pos.x - x) + (pos.y - y) * (pos.y - y) < (size * size) / 4.0;
}
}