{
  "name": "Tilt (Rotate In)",
  "type": "fixed-intro",
  "duration": 0.6,
  "passes": [
    {
      "name": "rotate in",
      "inputs": [
        "default"
      ],
      "glsl": "precision highp float;

               varying vec2 sourceTextureCoordinate;

               uniform vec2 outputSize;
               uniform float progress;

               const vec2 K0 = vec2(502.0 / 720.0, 646.0 / 1280.0);
               const vec2 K1 = vec2(390.1 / 720.0, 631.7 / 1280.0);
               const vec2 K2 = vec2(361.1 / 720.0, 632.7 / 1280.0);
               const vec2 K3 = vec2(360.0 / 720.0, 640.0 / 1280.0);

               const float T0 = 0.0;
               const float T1 = 3.0 / 90.0;
               const float T2 = 5.0 / 90.0;
               const float T3 = 10.0 / 90.0;
               const float T4 = 18.0 / 90.0;
               const float T5 = 1.0;

               const float S0 = 0.71;
               const float S1 = 0.951;
               const float S2 = 1.0;

               const float R0 = 17.0;
               const float R1 = 0.0;
               const float R2 = -8.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 return K3;
               }

               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);
               }

               float catmullRom(float p0, float p1, float p2, float 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);
               }

               void main() {

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

                 // speeding things up 5x because the curves were originally timed for 3 seconds
                 // but the animation only runs for .6s
                 // now we have direct control of animation timing
                 float p = progress * 0.2;

                 float scaleVal;
                 float rotDeg;
                 vec2 targetUV;

                 if (p < T1){
                   float u = (p - T0) / (T1 - T0);
                   targetUV = catmullRom(K0, K0, K1, K2, u);
                 } else if (p < T2){
                   float u = (p - T1) / (T2 - T1);

                   targetUV = catmullRom(K0, K1, K2, K3, u);
                 } else if (p < T4){
                   float u = (p - T2) / (T4 - T2);
                   targetUV = catmullRom(K1, K2, K3, K3, u);
                 } else {
                   targetUV = K3;
                 }

                 if (p < T1){
                   float t = (p - T0) / (T1 - T0);
                   scaleVal = catmullRom(S0, S0, S1, S1, t);
                   rotDeg = catmullRom(R0, R0, R1, R2, t);
                 } else if (p < T3){
                   float t = (p - T1) / (T3 - T1);
                   scaleVal = catmullRom(S0, S1, S1, S2, t);
                   rotDeg = catmullRom(R0, R1, R2, R1, t);
                 } else if (p < T4){
                   float t = (p - T3) / (T4 - T3);
                   scaleVal = catmullRom(S1, S1, S2, S2, t);
                   rotDeg = catmullRom(R1, R2, R1, R1, t);
                 } else {
                   scaleVal = S2;
                   rotDeg = R1;
                 }

                 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);

                 //motion blur
                 vec2 dir = uv - vec2(1.0,0.75);
                 float blurAmt = 0.025 * clamp(1.0 - progress/ 0.15, 0.0, 1.0);
                 vec4 c0 = sampleInput(uv);
                 vec4 c1 = sampleInput(uv - dir * (blurAmt * 0.33));
                 vec4 c2 = sampleInput(uv - dir * (blurAmt * 0.66));

                 vec4 color = (c0 + c1 + c2) / 3.0;
                 gl_FragColor = toOutputFormat(color);
               }
",
      "metal": "using namespace metal;
struct fragmentIn { float2 sourceTextureCoordinate [[user(locn1)]]; };
struct fragmentOut { float4 _gl_FragColor [[color(0)]]; };
typedef struct {
  float4x4 content_transform;
  float4x4 texture_transform;
  float progress;
  float2 output_size;
 } Uniforms;

    float mod(float x, float y) { return x - y * floor(x / y); }
    float2 mod(float2 x, float2 y) { return x - y * floor(x / y); }
    float3 mod(float3 x, float3 y) { return x - y * floor(x / y); }
    float4 mod(float4 x, float4 y) { return x - y * floor(x / y); }
constant float2 K0 = float2(502.0 / 720.0, 646.0 / 1280.0);
constant float2 K1 = float2(390.1 / 720.0, 631.7 / 1280.0);
constant float2 K2 = float2(361.1 / 720.0, 632.7 / 1280.0);
constant float2 K3 = float2(360.0 / 720.0, 640.0 / 1280.0);

constant float T0 = 0.0;
constant float T1 = 3.0 / 90.0;
constant float T2 = 5.0 / 90.0;
constant float T3 = 10.0 / 90.0;
constant float T4 = 18.0 / 90.0;
constant float T5 = 1.0;

constant float S0 = 0.71;
constant float S1 = 0.951;
constant float S2 = 1.0;

constant float R0 = 17.0;
constant float R1 = 0.0;
constant float R2 = -8.0;

float2 mirrorUv(float2 v) {
  float2 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;
}

float2 pickKey(int i){
  if ( i < 0) return K0;
  else if (i == 1) return K1;
  else if (i == 2) return K2;
  else return K3;
}

float2 catmullRom(float2 p0, float2 p1, float2 p2, float2 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);
}

float catmullRom(float p0, float p1, float p2, float 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);
}

fragment fragmentOut fragmentShader(fragmentIn in [[stage_in]],
 constant Uniforms & uniforms [[buffer(1)]],
 DefaultInputs defaultInputs) {

  float2 uv = in.sourceTextureCoordinate;
  float2 ratio = float2(uniforms.output_size.xy / uniforms.output_size.y);

  float p = uniforms.progress * 0.2;

  float scaleVal;
  float rotDeg;
  float2 targetUV;

  if (p < T1){
    float u = (p - T0) / (T1 - T0);
    targetUV = catmullRom(K0, K0, K1, K2, u);
  } else if (p < T2){
    float u = (p - T1) / (T2 - T1);

    targetUV = catmullRom(K0, K1, K2, K3, u);
  } else if (p < T4){
    float u = (p - T2) / (T4 - T2);
    targetUV = catmullRom(K1, K2, K3, K3, u);
  } else {
    targetUV = K3;
  }

  if (p < T1){
    float t = (p - T0) / (T1 - T0);
    scaleVal = catmullRom(S0, S0, S1, S1, t);
    rotDeg = catmullRom(R0, R0, R1, R2, t);
  } else if (p < T3){
    float t = (p - T1) / (T3 - T1);
    scaleVal = catmullRom(S0, S1, S1, S2, t);
    rotDeg = catmullRom(R0, R1, R2, R1, t);
  } else if (p < T4){
    float t = (p - T3) / (T4 - T3);
    scaleVal = catmullRom(S1, S1, S2, S2, t);
    rotDeg = catmullRom(R1, R2, R1, R1, t);
  } else {
    scaleVal = S2;
    rotDeg = R1;
  }

  float2 offset = targetUV - float2(0.5);

  float a = (rotDeg * M_PI_F / 180.0);
  float c = cos(a);
  float ss = sin(a);
  float2x2 rot2d = float2x2(c, -ss, ss, c);

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

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

  uv = mirrorUv(uv);

  //motion blur
  float2 dir = uv - float2(1.0,0.75);
  float blurAmt = 0.025 * clamp(1.0 - uniforms.progress/ 0.15, 0.0, 1.0);
  float4 c0 = sampleInput(defaultInputs, uv);
  float4 c1 = sampleInput(defaultInputs, uv - dir * (blurAmt * 0.33));
  float4 c2 = sampleInput(defaultInputs, uv - dir * (blurAmt * 0.66));

  float4 color = (c0 + c1 + c2) / 3.0;
  return {toOutputFormat(color)};
}
",
      "uniforms": {}
    }
  ]
}
