{
  "name": "Swerve (S Move)",
  "type": "fixed",
  "duration": 3,
  "passes": [
    {
      "name": "s move",
      "inputs": [
        "default"
      ],
      "glsl": "precision highp float;

varying vec2 sourceTextureCoordinate;

uniform vec2 outputSize; // size of the output fbo
uniform float progress; // normalized 0-1

const vec2 K0 = vec2(916.0 / 720.0, 1628.0 / 1280.0);
const vec2 K1 = vec2(334.6 / 720.0, 1317.8 / 1280.0);
const vec2 K2 = vec2(61.5 / 720.0, 1037.6 / 1280.0);
const vec2 K3 = vec2(64.1 / 720.0, 902.0 / 1280.0);
const vec2 K4 = vec2(286.7 / 720.0, 671.8 / 1280.0);
const vec2 K5 = vec2(360.0 / 720.0, 640.0 / 1280.0);
const vec2 K6 = vec2(360.0 / 720.0, 640.0 / 1280.0);

const float T0 = 0.0;
const float T1 = 3.0 / 90.0;
const float T2 = 7.0 / 90.0;
const float T3 = 10.0 / 90.0;
const float T4 = 16.0 / 90.0;
const float T5 = 20.0 / 90.0;
const float T6 = 1.0;

const float TS0 = 0.0;
const float TS1 = 7.0 / 90.0;
const float TS2 = 20.0 / 90.0;

const float S0 = 2.86;
const float S1 = 2.018;
const float S2 = 1.0;

const float R0 = -4.0;
const float R1 = 13.6;
const float R2 = 0.0;

vec2 mirrorUv(vec2 v) {
  vec2 m = mod(v, 2.0);
  return mix(m, 2.0 - m, step(1.0, m));
}

float easeInOutCubic(float t){
  return t < 0.5
  ? 4.0 * t * t * t
  : 1.0 - pow(-2.0 * t + 2.0, 3.0) / 2.0;
}


vec2 pickKey(int i){
  if ( i < 0) return K0;
  else if (i == 1) return K1;
  else if (i == 2) return K2;
  else if (i == 3) return K3;
  else if (i == 4) return K4;
  else if (i == 5) return K5;
  else return K6;
}

vec2 catmullRom(vec2 p0, vec2 p1, vec2 p2, vec2 p3, float t){
  float t2 = t * t, t3 = t2 * t;
  return 0.5 * ((2.0 * p1)
  + (-p0 + p2) * t
  + (2.0 * p0 - 5.0 * p1 + 4.0 * p2 - p3) * t2
  + (-p0 + 3.0 * p1 - 3.0 * p2 + p3) * t3);
}

// effect uniforms
void main() {

  vec2 uv = sourceTextureCoordinate;
  vec2 ratio = vec2(outputSize.xy / outputSize.y);
  float p = progress;

  float scaleVal;
  float rotDeg;

  int idx;
  float localT;

  vec2 targetUV;

  if (p < T1) {idx = 0; localT = (p -T0) / (T1 - T0);}
  else if (p < T2) {idx = 1; localT = (p -T1) / (T2 - T1);}
  else if (p < T3) {idx = 2; localT = (p -T2) / (T3 - T2);}
  else if (p < T4) {idx = 3; localT = (p -T3) / (T4 - T3);}
  else if (p < T5) {idx = 4; localT = (p -T4) / (T5 - T4);}
  else {idx = 5; localT = (p -T5) / (T6 - T5);}


  if (idx == 0){
   targetUV = mix(K0, K1, localT);
  } else {
    int i0 = (idx > 0) ? idx - 1 : 0;
    int i1 = idx;
    int i2 = idx + 1;
    int i3 = (idx + 2 <= 6) ? idx + 2 : 6;

    vec2 P0 = pickKey(i0);
    vec2 P1 = pickKey(i1);
    vec2 P2 = pickKey(i2);
    vec2 P3 = pickKey(i3);

    targetUV = catmullRom(P0, P1, P2, P3, localT);
  }

  if (p < TS1){
    float t = smoothstep(0.0,(TS1 - TS0),(p - TS0));
    rotDeg = mix(R0, R1, t);
  } else if (p < TS2){
    float t = easeInOutCubic((p - TS1) / (TS2 - TS1));
    rotDeg = mix(R1, R2, t);
  } else {
    scaleVal = S2;
    rotDeg = R2;
  }

  if (p < TS2){
    float t = smoothstep(0.0,TS2,p);
    scaleVal = mix(S0, S2, t);
  }

  vec2 offset = targetUV - vec2(0.5);

  float a = radians(rotDeg);
  float c = cos(a);
  float ss = sin(a);
  mat2 rot2d = mat2(c, -ss, ss, c);


  uv = uv - vec2(0.5);
  uv *= ratio;

  uv = rot2d * uv;
  uv/= ratio;
  uv -= offset;
  uv /= scaleVal;
  uv += vec2(0.5);

  uv = mirrorUv(uv);


  vec4 color = sampleInput(uv);
  gl_FragColor = toOutputFormat(color);
}
",
      "uniforms": {}
    }
  ]
}
