7

I have two (x,y) points start and end. I want to animate from start to end but instead of going a linear way I want to create a curved path.

I am quite sure I'm not looking for an easing because I don't want to affect the animation speed, I just want to calculate a curved path.

I figured out I needed some sort of control point, like shown in this image:

enter image description here

But I have no idea how to implement it. I would love to create a function that took the following parameters

function calculateXY(start, end, controlpoint, percentage);

Where percentage would be a number from 0 - 100% and where 0 would return the start position and 100% the end position.

The solution doesn't need to be in Objective-C, it could be in any programming language. I just can't get my head around the math :)

olha
  • 2,132
  • 1
  • 18
  • 39
Mads Lee Jensen
  • 4,570
  • 5
  • 36
  • 53

3 Answers3

0

The solution doesn't need to be in Objective-C, it could be in any programming language. I just can't get my head around the math

I'm not in a position to give code but if you're after an understanding of the math involved I can explain how that works for the Quad curve.

First understand what the control point mathematically impacts. The control point and the two defined points define the gradients of the drawn line at the current point and the endpoint. You can calculate the gradient of both lines using m = (y - y1)/(x - x1).

Mathematically what your trying to solve next is this set of equations for a, b, c:

ax^2 + bx + c contains both start and end points

2ax + b equals the corresponding gradients at the corresponding x value.

At that point you have quadratic that can be used for drawing the line.

Krish
  • 1,044
  • 9
  • 20
0

I saw this at work and wanted to take a shot at it at home. After looking at this example from Wikipedia for some time I think I understood, what to do and below you will find an example, which I will explain now.

I will use a time interval between 0 and 1. Any number in between is the time fraction of the animation. What you want is to get the location of you "point of interest" at a given fraction of time. The first step is, that you have three points A,B,C connected by two lines (g => [AB], h => [BC]). For each of these lines you will have to calulate points, that are wandering between the startPoint and the weightPoint P(g) respectively between the weightPoint and the endPoint P(h) at a given fraction of time.

Between these two calculated points (P(g) and P(h)) you draw a third line (let's call it y). Somewhere on that line is your "point of interest". But where? Again you have to calculate the position of a point on the line y (called P(y)) travelling from P(g) to P(h).

The position of your P(y) is what you're looking for.

function setup() {
  createCanvas(400, 400);
  fraction = 0;
  drawnPoints = [];
}

function draw() {
  background(100);
  let start = new Point(30, 50, 5);
  let end = new Point(300, 170, 5);
  let weight = new Point(200, 300, 5);

  let lineStartWeight = new Line(start, weight);
  let lineStartWeightPoint = lineStartWeight.getPointAt(fraction);
  let lineWeightEnd = new Line(weight, end);
  let lineWeightEndPoint = lineWeightEnd.getPointAt(fraction);

  let drawingLine = new Line(lineStartWeightPoint, lineWeightEndPoint);

  start.draw('red');
  end.draw('blue');
  weight.draw('#0f0');
  lineStartWeight.draw('#ff0');

  lineWeightEnd.draw('#0ff');

  lineStartWeightPoint.draw('#000');
  lineWeightEndPoint.draw('#fff')

  drawingLine.draw('#f66');

  drawnPoints.push(drawingLine.getPointAt(fraction));

  drawnPoints.forEach(p => p.draw(''));

  fraction += 0.01

  if (fraction > 1) {
    fraction = 0;
    drawnPoints = [];
  }

}

class Point {
  constructor(x, y, size = 1) {
    this.x = x;
    this.y = y;
    this.size = size;
  }

  draw(color) {
    fill(color);
    noStroke();
    ellipse(this.x, this.y, this.size, this.size);
  }
}

class Line {
  constructor(pointStart, pointEnd) {
    this.pointStart = pointStart;
    this.pointEnd = pointEnd;
  }

  draw(color) {
    stroke(color);
    line(this.pointStart.x, this.pointStart.y, this.pointEnd.x, this.pointEnd.y);
  }

  getPointAt(fraction) {
    let xCoord = (this.pointEnd.x - this.pointStart.x) * fraction + this.pointStart.x;
    let yCoord = (this.pointEnd.y - this.pointStart.y) * fraction + this.pointStart.y;
    return new Point(xCoord, yCoord, 5);
  }
}
html, body {
  margin: 0;
  padding: 0;
}
<html>
  <head>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.1/p5.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.1/addons/p5.dom.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.1/addons/p5.sound.min.js"></script>
    <link rel="stylesheet" type="text/css" href="style.css">
  </head>
  <body>
    <script src="sketch.js"></script>
  </body>
</html>

EDIT

It boils down to one simple function. I will only illustrate it for the x values, but y works analogous.

start(x1|y1), end(x2|y2) , controlpoint(x3|y3), f = fraction of time of the animation

To get the x value at a point in time f you have:

x = (((x2-x3)*f+x3)-((x3-x1)*f+x1))*f+((x3-x1)*f+x1)

After a few simplifications you come out with:

x = f2(x1+x2-2x3) +2f(x3-x1)+x1

Community
  • 1
  • 1
Argee
  • 1,216
  • 1
  • 12
  • 22
0

Look at Cocoa's bezier paths: (NSBezierPath).

It looks like it may not support quadratic bezier curves, so you'll need to convert to cubic.

Community
  • 1
  • 1
Michael Brewer-Davis
  • 14,018
  • 5
  • 37
  • 49