{
  "name": "Bounce (Bounce in)",
  "type": "static",
  "passes": [
    {
      "name": "bounce",
      "inputs": [
        "default"
      ],
      "glsl": "precision highp float;

varying vec2 sourceTextureCoordinate;

// default uniforms for all filters
uniform float time_sec;

uniform float loopDuration;


const float T0 = 0.0/90.0;
const float T1 = 5.0/90.0;
const float T2 = 10.0/90.0;
const float T3 = 16.0/90.0;
const float T4 = 20.0/90.0;
const float T5 = 25.0/90.0;
const float T6 = 29.0/90.0;
const float T7 = 45.0/90.0;
const float T8 = 50.0/90.0;
const float T9 = 55.0/90.0;
const float T10 = 61.0/90.0;
const float T11 = 65.0/90.0;
const float T12 = 70.0/90.0;
const float T13 = 74.0/90.0;
const float T14 = 1.0;

const float K0 = 1.0;
const float K1 = 0.893;
const float K2 = 1.265;
const float K3 = 0.95;
const float K4 = 1.058;
const float K5 = 0.982;
const float K6 = 1.0;
const float K7 = 1.0;
const float K8 = 0.893;
const float K9 = 1.265;
const float K10 = 0.95;
const float K11 = 1.058;
const float K12 = 0.982;
const float K13 = 1.0;
const float K14 = 1.0;

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

  float getScale(float p){
    p = fract(p);
    float f;

    if(p < T1){
      f = (p - T0) / (T1 - T0);
      return mix(K0, K1, easeInOutCubic(f));

    } else if(p < T2){
      f = (p - T1) / (T2 - T1);
      return mix(K1, K2, easeInOutCubic(f));

    } else if(p < T3){
      f = (p - T2) / (T3 - T2);
      return mix(K2, K3, easeInOutCubic(f));

    } else if(p < T4){
      f = (p - T3) / (T4 - T3);
      return mix(K3, K4, easeInOutCubic(f));

    } else if(p < T5){
      f = (p - T4) / (T5 - T4);
      return mix(K4, K5, easeInOutCubic(f));

    } else if(p < T6){
      f = (p - T5) / (T6 - T5);
      return mix(K5, K6, easeInOutCubic(f));

    } else if(p < T7){
      f = (p - T6) / (T7 - T6);
      return mix(K6, K7, easeInOutCubic(f));

    } else if(p < T8){
      f = (p - T7) / (T8 - T7);
      return mix(K7, K8, easeInOutCubic(f));

    } else if(p < T9){
      f = (p - T8) / (T9 - T8);
      return mix(K8, K9, easeInOutCubic(f));

    } else if(p < T10){
      f = (p - T9) / (T10 - T9);
      return mix(K9, K10, easeInOutCubic(f));

    } else if(p < T11){
      f = (p - T10) / (T11 - T10);
      return mix(K10, K11, easeInOutCubic(f));

    } else if(p < T12){
      f = (p - T11) / (T12 - T11);
      return mix(K11, K12, easeInOutCubic(f));

    } else if(p < T13){
      f = (p - T12) / (T13 - T12);
      return mix(K12, K13, easeInOutCubic(f));

    } else if(p < T14){
      f = (p - T13) / (T14 - T13);
      return mix(K13, K14, easeInOutCubic(f));

    } else  {
      return K14;
    }
  }

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


// effect uniforms
void main() {

  float p = fract(time_sec / loopDuration);
  vec2 uv = sourceTextureCoordinate;
  float scale = getScale(p);
  vec2 center = vec2(0.5);
  uv = (uv - center) / scale + center;
  uv = mirrorUv(uv);

  vec4 color = sampleInput(uv);
  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 strength;
        float progress;
        float time_sec;
        float duration_sec;
        float clip_duration_sec;
        float2 inputSize;
        float2 outputSize;
        float2 tex0Size;
        float loopDuration;
    } Uniforms;

    constant float T0 = 0.0/90.0;
    constant float T1 = 5.0/90.0;
    constant float T2 = 10.0/90.0;
    constant float T3 = 16.0/90.0;
    constant float T4 = 20.0/90.0;
    constant float T5 = 25.0/90.0;
    constant float T6 = 29.0/90.0;
    constant float T7 = 45.0/90.0;
    constant float T8 = 50.0/90.0;
    constant float T9 = 55.0/90.0;
    constant float T10 = 61.0/90.0;
    constant float T11 = 65.0/90.0;
    constant float T12 = 70.0/90.0;
    constant float T13 = 74.0/90.0;
    constant float T14 = 1.0;

    constant float K0 = 1.0;
    constant float K1 = 0.893;
    constant float K2 = 1.265;
    constant float K3 = 0.95;
    constant float K4 = 1.058;
    constant float K5 = 0.982;
    constant float K6 = 1.0;
    constant float K7 = 1.0;
    constant float K8 = 0.893;
    constant float K9 = 1.265;
    constant float K10 = 0.95;
    constant float K11 = 1.058;
    constant float K12 = 0.982;
    constant float K13 = 1.0;
    constant float K14 = 1.0;

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

    float getScale(float p) {
        p = fract(p);
        float f;

        if(p < T1) {
            f = (p - T0) / (T1 - T0);
            return mix(K0, K1, easeInOutCubic(f));
        } else if(p < T2) {
            f = (p - T1) / (T2 - T1);
            return mix(K1, K2, easeInOutCubic(f));
        } else if(p < T3) {
            f = (p - T2) / (T3 - T2);
            return mix(K2, K3, easeInOutCubic(f));
        } else if(p < T4) {
            f = (p - T3) / (T4 - T3);
            return mix(K3, K4, easeInOutCubic(f));
        } else if(p < T5) {
            f = (p - T4) / (T5 - T4);
            return mix(K4, K5, easeInOutCubic(f));
        } else if(p < T6) {
            f = (p - T5) / (T6 - T5);
            return mix(K5, K6, easeInOutCubic(f));
        } else if(p < T7) {
            f = (p - T6) / (T7 - T6);
            return mix(K6, K7, easeInOutCubic(f));
        } else if(p < T8) {
            f = (p - T7) / (T8 - T7);
            return mix(K7, K8, easeInOutCubic(f));
        } else if(p < T9) {
            f = (p - T8) / (T9 - T8);
            return mix(K8, K9, easeInOutCubic(f));
        } else if(p < T10) {
            f = (p - T9) / (T10 - T9);
            return mix(K9, K10, easeInOutCubic(f));
        } else if(p < T11) {
            f = (p - T10) / (T11 - T10);
            return mix(K10, K11, easeInOutCubic(f));
        } else if(p < T12) {
            f = (p - T11) / (T12 - T11);
            return mix(K11, K12, easeInOutCubic(f));
        } else if(p < T13) {
            f = (p - T12) / (T13 - T12);
            return mix(K12, K13, easeInOutCubic(f));
        } else if(p < T14) {
            f = (p - T13) / (T14 - T13);
            return mix(K13, K14, easeInOutCubic(f));
        } else {
            return K14;
        }
    }

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

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

        fragmentOut out = {};

        float p = fract(uniforms.time_sec / uniforms.loopDuration);
        float2 uv = in.sourceTextureCoordinate;
        float scale = getScale(p);
        float2 center = float2(0.5);
        uv = (uv - center) / scale + center;
        uv = mirrorUv(uv);

        float4 color = sampleInput(defaultInputs, uv);
        out._gl_FragColor = color;

        return out;
    }
  ",
      "uniforms": {
        "loopDuration": {
          "value": 3.0
        }
      }
    }
  ]
}
