private PVector ballPos;
private PVector ballStart;
private PVector ballTarget;
private PVector ballDelta;
private PVector controlPoint;
private PVector controlPointFlux;
private PVector leftFloater;
private PVector rightFloater;
private PVector leftDelta;
private PVector rightDelta;
private PVector center;
private float slope;
private float clock;
private float elapsed;
private float duration;
private boolean debugLines;

public void setup() {
  debugLines = false;
  size(800, 600);
  ballPos = new PVector(50, 50);
  ballStart = new PVector();
  ballTarget = new PVector();
  ballDelta = new PVector();
  controlPointFlux = new PVector();
  controlPoint = new PVector();
  leftFloater = new PVector();
  rightFloater = new PVector();
  leftDelta = new PVector();
  rightDelta = new PVector();
  center = new PVector();
  clock = millis();
  duration = 5000;
  reset(750, 550);
}

private void reset(float x, float y) {
  ballStart.x = ballPos.x;
  ballStart.y = ballPos.y;
  ballTarget.x = x;
  ballTarget.y = y;
  ballDelta.x = ballTarget.x - ballStart.x;
  ballDelta.y = ballTarget.y - ballStart.y;
  controlPointFlux.x = ballDelta.x / 2;
  controlPointFlux.y = ballDelta.y / 2;
  controlPoint.x = ballStart.x + ballDelta.x / 2 + controlPointFlux.x;
  controlPoint.y = ballStart.y + ballDelta.y / 2 - controlPointFlux.y; 

  leftFloater.x = ballStart.x;
  leftFloater.y = ballStart.y;
  leftDelta.x = controlPoint.x - leftFloater.x;
  leftDelta.y = controlPoint.y - leftFloater.y;
  rightFloater.x = controlPoint.x;
  rightFloater.y = controlPoint.y;
  rightDelta.x = ballTarget.x - controlPoint.x;
  rightDelta.y = ballTarget.y - controlPoint.y;

  elapsed = 0;
}

private void update() {
  if (mousePressed) {
    clock = millis();
    reset(mouseX, mouseY);
    return;
  }

  if (elapsed >= duration) {
    ballPos.x = ballTarget.x;
    ballPos.y = ballTarget.y;
    return;
  }
  float newClock = millis();
  float delta = newClock - clock;
  elapsed += delta;
  clock = newClock;  
  float easeAmount = easeInOut(elapsed / duration);
  leftFloater.x = ballStart.x + leftDelta.x * easeAmount;
  leftFloater.y = ballStart.y + leftDelta.y * easeAmount;

  rightFloater.x = controlPoint.x + rightDelta.x * easeAmount;
  rightFloater.y = controlPoint.y + rightDelta.y * easeAmount;

  center.x = (leftFloater.x + rightFloater.x) / 2;
  center.y = (leftFloater.y + rightFloater.y) / 2;
  slope = (rightFloater.y - leftFloater.y / rightFloater.x - leftFloater.x);

  ballPos.x = leftFloater.x + (rightFloater.x - leftFloater.x) * easeAmount;
  ballPos.y = leftFloater.y + (rightFloater.y - leftFloater.y) * easeAmount;
}

public void draw() {
  update();

  background(0, 0, 0);
  fill(255, 255, 255);
  noStroke();
  ellipse(ballPos.x, ballPos.y, 20, 20);

  if (debugLines) {
    //points
    fill(0, 0, 0, 0);

    stroke(255, 0, 0);
    line(leftFloater.x, leftFloater.y, rightFloater.x, rightFloater.y);

    stroke(255, 255, 255);

    ellipse(center.x, center.y, 10, 10);

    stroke(255, 0, 0);
    ellipse(leftFloater.x, leftFloater.y, 10, 10);
    ellipse(rightFloater.x, rightFloater.y, 10, 10);

    stroke(0, 255, 0);
    line(ballStart.x, ballStart.y, controlPoint.x, controlPoint.y);
    line(controlPoint.x, controlPoint.y, ballTarget.x, ballTarget.y);
    ellipse(ballStart.x, ballStart.y, 10, 10);
    ellipse(ballTarget.x, ballTarget.y, 10, 10);
    ellipse(controlPoint.x, controlPoint.y, 10, 10);
  }
}

private float linear(float k) {
  return k;
}

private float easeOut(float k) {
  return -k * (k-2);
}

private float easeInOut(float k) {
  if ( ( k *= 2 ) < 1 ) return 0.5 * k * k;
  return - 0.5 * ( --k * ( k - 2 ) - 1 );
}

void keyPressed() {
  if (key == 'd') {
    debugLines = !debugLines;
  }
}


