
float edge(vec2 p, vec2 k) {
  p.y = abs(p.y);
  p -= k;
  float sgn = sign(p.x)+sign(p.y);
  return sgn == 2. ? length(p) : max(p.x,p.y);
}

#define FK(k) floatBitsToInt(cos(k))^floatBitsToInt(k);
float hash(vec2 p) {
  int x = FK(p.x); int y = FK(p.y);
  return float((x*x-y)*(y*y+x)-x)/2.14e9;
}

vec3 erot(vec3 p, vec3 ax, float ro) {
  return mix(dot(p,ax)*ax,p,cos(ro)) + sin(ro)*cross(ax,p);
}

float component(vec3 p, vec3 ro) {
  float phi = acos(ro.x);
  float theta = ro.y*3.1415;
  vec3 ax = vec3(cos(phi), sin(phi)*cos(theta), sin(phi)*sin(theta));
  p = erot(p, ax, ro.z*3.1415);
  
  
  vec3 sgn = mod(floor(p),2.)*2.-1.;
  p = (fract(p)-0.5);
  p*=sgn;
  return (p.x+p.y+p.z)/sqrt(3.);
}

float hash1(float rnd) {
  return hash(vec2(rnd,rnd*rnd+1.));
}

vec3 hash3(float rnd) {
  return vec3(hash1(rnd+1.), hash1(hash1(rnd+2.)), hash1(hash1(rnd*2.+2.)*3.));
}

float noise(vec3 p, int iters) {
  float d = 0.;
  float acc =1.;
  for (int i = 0; i < iters; i++) {
    acc+=1.;
    d += component(p, hash3(float(i)));
  }
  
  return d/sqrt(acc*1.5)-0.2;
}

vec2 levl(vec3 p, vec3 off, float s) {

  vec3 cls = normalize(p) * s; 
  return vec2(length(noise(cls+off, 1)), distance(p, cls));
}

float bpm = 136.;

vec2 mmin(vec2 a, vec2 b) {
  if (a.x < b.x) return a;
  return b;
}

float linedist(vec2 p, vec2 a, vec2 b) {
  return distance(p, mix(a,b,clamp(dot(p-a,b-a)/dot(b-a,b-a),0.,1.)));
}

float bgcol = 0.;

vec2 scene(vec3 p) {
  vec2 dst = vec2(100000.,-1.);
  
  float bar = floor(iTime/60.*bpm);
  float iter = mod(bar, 4.)+1.;
  
  if (iter == 4.) iter = 2.;
  float k = 0.;
  for (int i = 0; i < int(iter); i++) {
    vec2 mp = levl(p, vec3(iTime/2.), 1.+(float(i)+1.)*0.2);
    if (i==0) k = -mp.x + 0.35;
    float lev = edge(mp, vec2(0.3 - float(i)*0.15, 0.05))-0.05;
    vec2 col = vec2(lev, float(i)+1.);
    dst = mmin(dst, col);
    bgcol = float(i)+1.;
  }
  
  vec3 nrz = normalize(p)*1.2;
  float krt=0.1;
  float dthrg = length((fract(nrz/krt + iTime/2.)-0.5)*krt);
  vec2 rtir = vec2(dthrg, distance(p, nrz));
  float rz = length(p)-1.2;
  
  float anger = pow(fract(iTime/60.*bpm),0.5) * (length(sin(nrz*8.+iTime))/sqrt(3.)*0.5+0.5);
  if (mod(bar, 4.) != 3.) anger = 0.;
  rz = linedist(rtir, vec2(0), vec2(0,anger))-0.02;
  rz = -min(-rz,-k);
  rz = min(rz, length(p)-1.2);
  
  return mmin(vec2(rz,0),dst);
}

#define AP(f,k) vec3(f(k[0]).x,f(k[1]).x,f(k[2]).x)
mat3 ep = mat3(0.01);
vec3 norm(vec3 p) {
  mat3 k = mat3(p,p,p)-ep;
  return normalize(scene(p).x - AP(scene,k));
}



vec3 colr(float m) {
  vec3 col = vec3(0);
  if (m == 0.) col= vec3(153,70,70);
  if (m == 1.) col=vec3(173, 173, 173);
  if (m == 2.) col=vec3(100, 222, 207) ;
  if (m == 3.) col=vec3(255, 255, 255);
  return col / 255.;
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{

  vec2 uv = vec2(fragCoord.x / iResolution.x, fragCoord.y / iResolution.y);
  uv -= 0.5;
  uv /= vec2(iResolution.y / iResolution.x, 1);
  //uv*=4;
  float pound = fract(iTime/60.*bpm);
  vec3 cam = normalize(vec3(1.+pound*0.3,uv));
  vec3 init = vec3(-5.,0.,0.);
  
  float ro = iTime/2.;
  cam = erot(erot(cam,vec3(0.,1.,0.), ro/3.), vec3(0.,0.,1.), ro);
  init = erot(erot(init,vec3(0.,1.,0.), ro/3.), vec3(0.,0.,1.), ro);
  
  vec3 p = init;
  bool hit = false;
  vec2 dist;
  for(int i = 0; i < 60; i++) {
    dist = scene(p);
    if (abs(dist.x)<0.001) { hit = true; break; }
    if (distance(p, init) > 100.) break;
    p+=cam*dist.x;
  }
  
  vec3 n = norm(p);
  vec3 c = colr(hit ? dist.y : bgcol);
  //float edg = noise(vec3(uv,iTime/10));
  vec3 colc = sqrt(c*c*length(sin(n*2.)*0.5+0.5)/sqrt(3.));
  fragColor.xyz = erot(colc*2.-1., normalize(vec3(1.,1,2)), iTime)*0.5+0.5;
  
  fragColor.xyz += vec3(hash(uv*(iTime+0.1))*0.05);
  if(hit) return;
  
  
  vec3 p2 = init;
  float dist2;
  float k =1.;
  for(int i = 0; i < 20; i++) {
    dist2 = noise(p2, 4)-0.2;
    if(i==0)k=sign(dist2);
    dist2*=k;
    if (abs(dist2)<0.001) { break; }
    if (distance(p2, init) > 100.) break;
    p2+=cam*dist2;
  }
  float col = length(sin(p2)*0.5+0.5)/sqrt(3.);
  fragColor.xyz*=mix(col, 1.,0.4);
}
