// CC0: Late night truchet hacking
//  Continued a bit on the braided truchets
//  Tinkered around and liked the result
//  Code is dirty and it is getting late

#define TIME        iTime
#define RESOLUTION  iResolution
#define PI          3.141592654
#define TAU         (2.0*PI)
#define ROT(a)      mat2(cos(a), sin(a), -sin(a), cos(a))

// License: MIT OR CC-BY-NC-4.0, author: mercury, found: https://mercury.sexy/hg_sdf/
float modMirror1(inout float p, float size) {
  float halfsize = size*0.5;
  float c = floor((p + halfsize)/size);
  p = mod(p + halfsize,size) - halfsize;
  p *= mod(c, 2.0)*2.0 - 1.0;
  return c;
}

// License: MIT OR CC-BY-NC-4.0, author: mercury, found: https://mercury.sexy/hg_sdf/
float modPolar(inout vec2 p, float repetitions) {
  float angle = TAU/repetitions;
  float a = atan(p.y, p.x) + angle/2.;
  float r = length(p);
  float c = floor(a/angle);
  a = mod(a,angle) - angle/2.;
  p = vec2(cos(a), sin(a))*r;
  // For an odd number of repetitions, fix cell index of the cell in -x direction
  // (cell index would be e.g. -5 and 5 in the two halves of the cell):
  if (abs(c) >= (repetitions/2.0)) c = abs(c);
  return c;
}

// License: Unknown, author: Unknown, found: don't remember
float hash(float co) {
  return fract(sin(co*12.9898) * 13758.5453);
}

// License: Unknown, author: Unknown, found: don't remember
float hash(vec2 co) {
  return fract(sin(dot(co.xy ,vec2(12.9898,58.233))) * 13758.5453);
}

// License: MIT, author: Inigo Quilez, found: https://www.iquilezles.org/www/articles/smin/smin.htm
float pmin(float a, float b, float k) {
  float h = clamp(0.5+0.5*(b-a)/k, 0.0, 1.0);
  return mix(b, a, h) - k*h*(1.0-h);
}

float pabs(float a, float k) {
  return -pmin(a, -a, k);
}

// License: MIT, author: Inigo Quilez, found: https://iquilezles.org/www/articles/distfunctions2d/distfunctions2d.htm
float octogon(vec2 p, float r ) {
    const vec3 k = vec3(-0.9238795325, 0.3826834323, 0.4142135623 );
    p = abs(p);
    p -= 2.0*min(dot(vec2( k.x,k.y),p),0.0)*vec2( k.x,k.y);
    p -= 2.0*min(dot(vec2(-k.x,k.y),p),0.0)*vec2(-k.x,k.y);
    p -= vec2(clamp(p.x, -k.z*r, k.z*r), r);
    return length(p)*sign(p.y);
}

// License: MIT, author: Inigo Quilez, found: https://iquilezles.org/www/articles/distfunctions2d/distfunctions2d.htm
float segment(vec2 p, vec2 a, vec2 b) {
    vec2 pa = p-a, ba = b-a;
    float h = clamp( dot(pa,ba)/dot(ba,ba), 0.0, 1.0 );
    return length( pa - ba*h );
}

vec2 toPolar(vec2 p) {
  return vec2(length(p), atan(p.y, p.x));
}

vec2 toRect(vec2 p) {
  return p.x*vec2(cos(p.y), sin(p.y));
}

// License: CC0, author: Mårten Rånge, found: https://github.com/mrange/glsl-snippets
float smoothKaleidoscope(inout vec2 p, float sm, float rep) {
  vec2 hp = p;

  vec2 hpp = toPolar(hp);
  float rn = modMirror1(hpp.y, TAU/rep);

  float sa = PI/rep - pabs(PI/rep - abs(hpp.y), sm);
  hpp.y = sign(hpp.y)*(sa);

  hp = toRect(hpp);

  p = hp;

  return rn;
}

float ref(inout vec2 p, vec2 r) {
  float d = dot(p, r);
  p -= r*min(0.0, d)*2.0;
  return d < 0.0 ? 0.0 : 1.0;
}

vec3 braid(vec3 col, vec2 p, vec2 cp, float z, float aa) {
  const float reps  = 32.0;
  const float a     = TAU/reps;
  const float bsz0  = a*0.3;
  const mat2 rot2   = ROT(a*0.5);
  const float sa    = a*-0.12;
  const mat2 rot22   = ROT(sa);
  const float bw    = 0.04;
  const vec2 bp0 = -vec2(-0.5, 0.0);
  const vec2 p00 = bp0*transpose(rot2);
  const vec2 p01 = bp0*rot2;
  const vec2 bp1 = -vec2(-0.5/cos(0.5*a+sa), 0.0);
  const vec2 p10 = bp1*rot22;
  const vec2 p11 = bp1*transpose(rot22);

  float cd = length(cp)-0.5;
  vec2 pp = cp;

  pp *= rot2;
  float n = modPolar(pp, reps);

  float bd0 = segment(pp, p00, p10)-bw;
  float bd1 = segment(pp, p01, p11)-bw;
  bd0 = abs(bd0)-0.25*bw;
  bd1 = abs(bd1)-0.25*bw;

  float h = hash(n + 123.4);
  if (sign(h-0.5)*cd > 0.) {
    float tmp = bd0;
    bd0 = bd1;
    bd1 = tmp;
  }

  const float shd = 175.0;
  col *= 1.0-exp(-shd*max(bd0, 0.0));
  col = mix(col, vec3(1.0), smoothstep(0.0, -aa, bd0));
  col *= 1.0-exp(-shd*max(bd1, 0.0));
  col = mix(col, vec3(1.0), smoothstep(0.0, -aa, bd1));
  return col;
}

vec3 effect(vec2 p, vec2 pp) {
  float l = length(p);
  const float pr = 10.0;
  const float z = 1.0-0.25;
  p /= z;

  p *= ROT(0.05*TIME);
  float n = smoothKaleidoscope(p, 0.075, 64.0);
  p *= ROT(-0.05*TIME);
  p += pr*sin(0.125*vec2(1.0, sqrt(0.5))*TIME/pr);

  float aa = 4.0/(z*RESOLUTION.y);


  vec2 cp = p;
  vec2 np = round(cp);
  float nh0 = hash(np+123.4);
  cp -= np;

  if (nh0 > 0.5) cp = cp*ROT(PI/2.0);

  vec3 col = vec3(0.1)*smoothstep(2.0, 0.0, l);
  vec2 op = cp;
  op = abs(op);
  op -= 0.5;
  float od1 = octogon(op, 1.0/6.0);
  od1 = abs(od1)-1./16.0;
  od1 = abs(od1)-1./32.0;
  od1 = abs(od1)-1./96.0;
  float od0 = length(op)-1.0/3.0;

  const float shd = 25.0;
  col *= 1.0-exp(-shd*0.5*max(od0, 0.0));
  col = mix(col, vec3(1.0), smoothstep(0.0, -aa, od0));
  col *= 1.0-exp(-shd*max(od1, 0.0));
  col = mix(col, vec3(1.0), smoothstep(0.0, -aa, od1));
  col = mix(col, vec3(1.0), smoothstep(0.0, -aa, abs(od0)-0.011));
  vec2 bp = cp;
  float bn = ref(bp, normalize(vec2(1.0)));
  bp -= 0.5;
  col = braid(col, p, bp, z, aa);
  col *= smoothstep(1.75, 0.5, length(pp));
  col = sqrt(col);

  return col;
}

void mainImage( out vec4 fragColor, in vec2 fragCoord ) {
  vec2 q = fragCoord/RESOLUTION.xy;
  vec2 p = -1. + 2. * q;
  vec2 pp = p;
  p.x *= RESOLUTION.x/RESOLUTION.y;
  vec3 col = effect(p, pp);

  fragColor = vec4(col, 1.0);
}
