// classic(300es)
precision highp float;
precision highp int;

uniform vec2 resolution;
uniform float time;
uniform sampler2D backbuffer;

out vec4 outColor;

// const
const float PI = 3.141592653589793;

// utilities
float atan2(float y, float x){
    return x == 0.0 ? 0.5*PI*sign(y) : atan(y, x);
}

vec3 hsv2rgb(float h, float s, float v){
    return ((clamp(abs(fract(h+vec3(0.0, 2.0, 1.0) / 3.0) * 6.0 - 3.0) - 1.0, 0.0, 1.0) - 1.0) * s + 1.0) * v;
}

mat2 rotate2D(float th){
    float c = cos(th);
    float s = sin(th);
    return mat2(c, -s, s, c);
}

float smootherstep(float e0, float e1, float x){
    x = clamp((x - e0) / (e1 - e0), 0.0, 1.0);
    return  x*x*x*(x*(x*6.0-15.0)+10.0);
}

// hash
const uint UINT_MAX = 0xffffffffu;
uvec3 k = uvec3(0x456789abu, 0x6789ab45u, 0x89ab4567u);
uvec3 u = uvec3(1u, 2u, 3u);

uint uhash11(uint n){
    n ^= n << u.x;
    n ^= n >> u.x;
    n *= k.x;
    n ^= n << u.x;
    return n * k.x;
}

uvec2 uhash22(uvec2 n){
    n ^= n.yx << u.xy;
    n ^= n.yx >> u.xy;
    n *= k.xy;
    n ^= n.yx << u.xy;
    return n * k.xy;
}

uvec3 uhash33(uvec3 n){
    n ^= n.yzx << u;
    n ^= n.yzx >> u;
    n *= k;
    n ^= n.yzx << u;
    return n * k;
}

float hash11(float p){
    uint n = floatBitsToUint(p);
    return float(uhash11(n)) / float(UINT_MAX);
}

vec2 hash22(vec2 p){
    uvec2 n = floatBitsToUint(p);
    return vec2(uhash22(n)) / vec2(UINT_MAX);
}

vec3 hash33(vec3 p){
    uvec3 n = floatBitsToUint(p);
    return vec3(uhash33(n)) / vec3(UINT_MAX);
}

float hash21(vec2 p){
    uvec2 n = floatBitsToUint(p);
    return float(uhash22(n).x) / float(UINT_MAX);
}

float hash31(vec3 p){
    uvec3 n = floatBitsToUint(p);
    return float(uhash33(n).x) / float(UINT_MAX);
}

// perlin noise
float gtable2(vec2 lattice, vec2 p){
    uvec2 n = floatBitsToUint(lattice);
    uint ind = uhash22(n).x >> 29;
    float u = 0.9238795325112867 * (ind < 4u ? p.x : p.y);  // cos(PI*0.125)
    float v = 0.3826834323650898 * (ind < 4u ? p.y : p.x);  // sin(PI*0.125)
    return ((ind & 1u) == 0u ? u : -u) + ((ind & 2u) == 0u? v : -v);
}

float pnoise21(vec2 p){
    vec2 n = floor(p);
    vec2 f = fract(p);
    float v00 = gtable2(n + vec2(0.0, 0.0), f - vec2(0.0, 0.0));
    float v10 = gtable2(n + vec2(1.0, 0.0), f - vec2(1.0, 0.0));
    float v01 = gtable2(n + vec2(0.0, 1.0), f - vec2(0.0, 1.0));
    float v11 = gtable2(n + vec2(1.0, 1.0), f - vec2(1.0, 1.0));

    f = f * f * f * (10.0 - 15.0 * f + 6.0 * f * f);
    return 0.5 + 0.5 * mix(mix(v00, v10, f.x), mix(v01, v11, f.x), f.y);
}

float gtable3(vec3 lattice, vec3 p){
    uvec3 n = floatBitsToUint(lattice);
    uint ind = uhash33(n).x >> 28;
    float u = ind < 8u ? p.x : p.y;
    float v = ind < 4u ? p.y : ind == 12u || ind == 14u ? p.x : p.z;
    return ((ind & 1u) == 0u? u: -u) + ((ind & 2u) == 0u? v : -v);
}

float pnoise31(vec3 p){
    vec3 n = floor(p);
    vec3 f = fract(p);
    float v000 = gtable3(n + vec3(0.0, 0.0, 0.0), f - vec3(0.0, 0.0, 0.0)) * 0.7071067811865475;  // 1.0 / sqrt(2.0)
    float v100 = gtable3(n + vec3(1.0, 0.0, 0.0), f - vec3(1.0, 0.0, 0.0)) * 0.7071067811865475;  // 1.0 / sqrt(2.0)
    float v010 = gtable3(n + vec3(0.0, 1.0, 0.0), f - vec3(0.0, 1.0, 0.0)) * 0.7071067811865475;  // 1.0 / sqrt(2.0)
    float v001 = gtable3(n + vec3(0.0, 0.0, 1.0), f - vec3(0.0, 0.0, 1.0)) * 0.7071067811865475;  // 1.0 / sqrt(2.0)
    float v011 = gtable3(n + vec3(0.0, 1.0, 1.0), f - vec3(0.0, 1.0, 1.0)) * 0.7071067811865475;  // 1.0 / sqrt(2.0)
    float v101 = gtable3(n + vec3(1.0, 0.0, 1.0), f - vec3(1.0, 0.0, 1.0)) * 0.7071067811865475;  // 1.0 / sqrt(2.0)
    float v110 = gtable3(n + vec3(1.0, 1.0, 0.0), f - vec3(1.0, 1.0, 0.0)) * 0.7071067811865475;  // 1.0 / sqrt(2.0)
    float v111 = gtable3(n + vec3(1.0, 1.0, 1.0), f - vec3(1.0, 1.0, 1.0)) * 0.7071067811865475;  // 1.0 / sqrt(2.0)

    f = f * f * f * (10.0 - 15.0 * f + 6.0 * f * f);

    float v00 = mix(v000, v100, f.x);
    float v10 = mix(v010, v110, f.x);
    float v01 = mix(v001, v101, f.x);
    float v11 = mix(v011, v111, f.x);

    float v0 = mix(v00, v10, f.y);
    float v1 = mix(v01, v11, f.y);

    return 0.5 + 0.5 * mix(v0, v1, f.z);
}

// domain warping
float warp21(vec2 p, float g){
    float val = 0.0;
    float th;
    for (int i=0; i<4; i++){
        th = 2.0 * PI * val;
        //val = fbm21(p + g * vec2(cos(th) ,sin(th)), 0.5);
        //val = fbm21(p + g * val, 0.5);
        //val = pnoise21(p + g * val);
        val = pnoise21(p + g * vec2(cos(th) ,sin(th)));
    }
    return val;
}

void main(){
    float t = time;
    vec2 r = resolution;
    vec4 FC = gl_FragCoord;
    vec4 o;

    vec2 p = (2.0*FC.xy - r) / min(r.x, r.y);
    p *= 4.0;

    // skybox
    vec2 pt = p;
    pt *= 2.0;
    pt = 2.0*fract(pt)-1.0;
    pt = abs(pt);
    vec3 color = pt.x + pt.y < 1.0 ? vec3(0.4) : vec3(0.8);

    // truchet
    float k = 8.0;
    //p *= rotate2D(2.0*PI*cos(0.055*PI*t));
    for (float i = 0.0; i < k; i++){
        pt = p;
        pt *= rotate2D(2.0*PI*cos(0.055*PI*(t+0.25*i)));
        pt.y += 8.0*sin(0.17*PI*t);
        pt.x += 8.0*cos(0.19*PI*t);
        float h = 5.0 * hash21(floor(pt));
        vec2 pf = 2.0*fract(pt) - 1.0;

        float d;
        float rs = 0.2;
        float rl = 0.5;
        if (h < 1.0){
            d = min(
                abs(length(pf-vec2(1.0, -1.0))-1.0)-rs,
                abs(length(pf-vec2(-1.0, 1.0))-1.0)-rs
            );
        }
        else if (h < 2.0){
            d = min(
                abs(length(pf-vec2(1.0, 1.0))-1.0)-rs,
                abs(length(pf-vec2(-1.0, -1.0))-1.0)-rs
            );
        }
        else if (h < 3.0){
            d = min(
                abs(pf.x)-rs,
                abs(pf.y)-rs
            );
        }
        else if (h < 4.0){
            pf = abs(pf);
            d = min(
                abs(pf.x)-rs,
                length(pf-vec2(1.0, 0.0))-rl
            );
        }
        else{
            pf = abs(pf);
            d = min(
                abs(pf.y)-rs,
                length(pf-vec2(0.0, 1.0))-rl
            );
        }

        if (d < 0.0){
            color = hsv2rgb(
                0.125*t - 0.125*i/k - 0.0625*length(vec2(2.0*pt.x, pt.y)),
                1.0 - i/k,
                1.0
            ) * warp21(vec2(pt.x, pt.y+4.0*t), 1.5+1.0*cos(1.0*(floor(t)+smootherstep(0.0, 1.0, fract(t)))*PI));
            break;
        }
        else{
            p *= 1.2;
        }
    }

    o.xyz = color;


    o.w = 1.0;
    outColor = o;
}
