There are many ways to describe a curved path the most flexible way is to use parametric curves where the position is given by two functions x = f(t) and y = g(t). The parameter t can be any value but it is common to use the range 0\le\;t\;\le1.
Note: in 3D we would have a third parametric function z=h(t)
To get an object (e.g. aircraft) to align along the curved path we need to be able to calculate the tangent to the curve at the objects current position. This tangent will be represented as a vector where
x = f(t+\partial t)-f(t-\partial t) \\
y = g(t+\partial t)-g(t-\partial t)
where \partial t is a very small positive value \gt0
This sketch demonstrates this approach. If you have questions about the code ask here.
Path path;
Arrow[] arrows;
void setup() {
size (400, 400);
path = new Parametric01();
arrows = new Arrow[] {
new Arrow( random(1), path, color(255, 100, 100), 0.0001),
new Arrow( random(1), path, color(255, 255, 100), 0.0002),
new Arrow( random(1), path, color(100, 100, 255), 0.0003),
new Arrow( random(1), path, color(100, 255, 100), 0.00035),
new Arrow( random(1), path, color(255, 100, 255), 0.00015),
new Arrow( random(1), path, color(100, 255, 255), 0.00025),
};
}
void draw() {
background(0);
path.show();
for (int i = 0; i < arrows.length; i++) {
arrows[i].move();
arrows[i].show();
}
}
class Arrow {
PVector pos; // position
PVector dir; // direction of travel
Path path;
float t;
int col;
float speed;
Arrow(float startT, Path path, int col, float speed) {
this. t = startT;
this.path = path;
this.pos = this.path.getPos(t);
this.dir = this.path.getDir(t);
this.col = col;
this.speed = speed;
}
void move() {
if (speed != 0) {
t += speed;
pos = path.getPos(t);
dir = path.getDir(t);
}
}
void show() {
// The object should 'point' along the +X axis
float a = atan2(dir.y, dir.x);
push();
noStroke();
fill(col);
translate(pos.x, pos.y);
rotate(a);
triangle(10, 0, -8, -5, -8, 5);
pop();
}
}
interface Path {
PVector getPos(float t);
PVector getDir(float t);
void show();
}
class Parametric01 implements Path {
float cx = width / 2;
float cy = height / 2;
float scale = width / 4;
PVector getPos(float t) {
return new PVector(x(t), y(t));
}
PVector getDir(float t) {
// Effectively calculates the tangent at this point
float x = x(t + 0.001) - x(t - 0.001);
float y = y(t + 0.001) - y(t - 0.001);
return new PVector(x, y);
}
float x(float t) {
t = t * 2 * PI; // for full circuit
return scale * (sin(t) + sin(7 * t)/2 + cos(17 * t)/3) + cx;
}
float y(float t) {
t = t * 2 * PI; // for full circuit
return scale * (cos(t) + cos(7 * t)/2 + sin(17 * t)/3) + cy;
}
void show() {
int res = 1000;
float dt = 1f / res;
push();
stroke(160);
float prevX = x(0), prevY = y(0);
for (int n = 1; n <= res; n++) {
float currX = x(n * dt), currY = y(n * dt);
line(prevX, prevY, currX, currY);
prevX = currX;
prevY = currY;
}
pop();
}
}