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