// 'Cinq variations sur un même thème':
//   Inspired by from Max Bill: Quinze variations sur un même thème, 1935-1938, with animations.
// wungasaurus, Revision 2023, animated gif

float noise(in vec2 uv) {
    return fract(sin(dot(uv.xy ,vec2(123.18692, 895.2234))) * 19395.1791);
}

bool isBitSet( float x,  int bit) {
    return (uint(round(x)) & (1u << bit)) != 0u;
}

// https://graphics.stanford.edu/%7Eseander/bithacks.html#CountBitsSetParallel
int popcount(in float f)
{
    int i = int(f);
    i = i - ((i >> 1) & 0x55555555);
    i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
    i = (i + (i >> 4)) & 0x0F0F0F0F;
    return (i * 0x01010101) >> 24;
}

float gain(float x, float k)
{
    float a = 0.5*pow(2.0*((x<0.5)?x:1.0-x), k);
    return (x<0.5)?a:1.0-a;
}

// All SDFs from/based on https://iquilezles.org/articles/distfunctions2d/
float sdCircle( vec2 p, float r )
{
    return length(p) - r;
}

float sdRing(vec2 p, float r1, float r2) {
    return abs(sdCircle(p, r1)) - r2;
}
float sdRingRing(vec2 p, float r1, float r2, float r3) {
    return abs(sdRing(p, r1,  r2)) - r3;
}

float nGon(in int n, in vec2 p, in float r) {
    // these 2 lines can be precomputed
    float an = 6.2831853 / float(n);
    float he = r * tan(0.5 * an);

    // rotate to first sector
    p = -p.yx; // if you want the corner to be up
    float bn = an * floor((atan(p.y, p.x) + 0.5 * an) / an);
    vec2 cs = vec2(cos(bn), sin(bn));
    p = mat2(cs.x, -cs.y, cs.y, cs.x) * p;

    // side of polygon
    return length(p - vec2(r, clamp(p.y, -he, he))) * sign(p.x-r);
}

float ngon_ring(in int n, in vec2 p, in float r1, float r2) {
    return abs(nGon(n, p, r1)) - r2;
}
float ngon_ringring(in int n, in vec2 p, in float r1, float r2, float r3) {
    return abs(ngon_ring(n, p, r1, r2)) - r3;
}

// Classic 2D rotate.
vec2 rotate( in vec2 p, float t )
{
    float co = cos(t);
    float si = sin(t);
    return mat2(co,-si,si,co)*p;
}

float shapecomb(float res, in int fillbit, float m, float d0, float d1, float d2, float d3, float d4, float d5) {
  float d = 0.;

  // ok
  // x 0 1 2 3 4 5
  // 0   / + + + -    rund ringe
  // 1     + + - -    eck linien
  // 2       + - +    eck füll
  // 3         / /    rund füll
  // 4           -    rund ring ring
  // 5.               eck ring ring

  float ds[5] = float[](d0, d1, d2, d3, d4);

    for (int i = 0; i < ds.length(); ++i) {
        if (m <= float(i + 1)) {
            d = mix(ds[i], ds[(i + 1) % ds.length()], m - float(i));
            break;
        }
    }

   if (d < 0.01) {
      if(!isBitSet(res, fillbit))
      res += float(1u<<fillbit);
   }
   return res;
}

float shape_1(in float res, in int fillbit, in vec2 uv, in float r1, in float r2, in float m) {
  float d0 = sdRing(uv, r1+r2, r2);
  float d3 = sdCircle(uv, r1+r2+.04);
  float d2 = nGon(8, uv, r1);
  float d1 = 10.*ngon_ring(8, uv, r1, 0.001);
  float d4 = 20.*sdRingRing(uv, r1+r2, r2, 0.001);
  float d5 = 20.*ngon_ringring(8, uv, r1, r2, 0.001);
  return shapecomb(res, fillbit, m, d0, d1, d2, d3, d4, d5);
}

float shape_2(in float res, in int fillbit, in vec2 uv, in float r1, in float r2, in float m) {
  float d0 = sdRing(uv, r1+r2, r2);
  float d3 = sdCircle(uv, r1+r2+.04);
  float d2 = nGon(7, uv, r1);
  float d1 = 10.*ngon_ring(7, uv, r1, 0.001);
  float d4 = 20.*sdRingRing(uv, r1+r2, r2, 0.001);
  float d5 = 20.*ngon_ringring(7, uv, r1, r2, 0.001);
  return shapecomb(res, fillbit, m, d0, d1, d2, d3, d4, d5);
}

float shape_3(in float res, in int fillbit, in vec2 uv, in float r1, in float r2, in float m) {
  float d0 = sdRing(uv, r1+r2, r2);
  float d3 = sdCircle(uv, r1+r2+.04);
  float d2 = nGon(6, uv, r1);
  float d1 = 10.*ngon_ring(6, uv, r1, 0.001);
  float d4 = 20.*sdRingRing(uv, r1+r2, r2, 0.001);
  float d5 = 20.*ngon_ringring(6, uv, r1, r2, 0.001);
  return shapecomb(res, fillbit, m, d0, d1, d2, d3, d4, d5);
}

float shape_4(in float res, in int fillbit, in vec2 uv, in float r1, in float r2, in float m) {
  float d0 = sdRing(uv, r1+r2, r2);
  float d3 = sdCircle(uv, r1+r2+.04);
  float d2 = nGon(5, uv, r1);
  float d1 = 10.*ngon_ring(5, uv, r1, 0.001);
  float d4 = 20.*sdRingRing(uv, r1+r2, r2, 0.001);
  float d5 = 20.*ngon_ringring(5, uv, r1, r2, 0.001);
  return shapecomb(res, fillbit, m, d0, d1, d2, d3, d4, d5);
}

float shape_5(in float res, in int fillbit, in vec2 uv, in float r1, in float r2, in float m) {
  float d0 = sdRing(uv, r1+r2, r2);
  float d3 = sdCircle(uv, r1+r2+.04);
  float d2 = nGon(4, uv, r1);
  float d1 = 10.*ngon_ring(4, uv, r1, 0.001);
  float d4 = 20.*sdRingRing(uv, r1+r2, r2, 0.001);
  float d5 = 20.*ngon_ringring(4, uv, r1, r2, 0.001);
  return shapecomb(res, fillbit, m, d0, d1, d2, d3, d4, d5);
}

float shape_6(in float res, in int fillbit, in vec2 uv, in float r1, in float r2, in float m) {
  float d0 = sdRing(uv, r1+r2, r2);
  float d3 = sdCircle(uv, r1+r2+.04);
  float d2 = nGon(3, uv, r1);
  float d1 = 10.*ngon_ring(3, uv, r1, 0.001);
  float d4 = 20.*sdRingRing(uv, r1+r2, r2, 0.001);
  float d5 = 20.*ngon_ringring(3, uv, r1, r2*.5, 0.001);
  return shapecomb(res, fillbit, m, d0, d1, d2, d3, d4, d5);
}

vec3 col_1 = vec3(109.,50.,116.)/vec3(255.);
vec3 col_2 = vec3(179.,74.,43.)/vec3(255.);
vec3 col_4 = vec3(66.,110.,58.)/vec3(255.);
vec3 col_3 = vec3(55.,49.,93.)/vec3(255.);
vec3 col_5 = vec3(161.,49.,41.)/vec3(255.);
vec3 col_6 = vec3(252.,190.,60.)/vec3(255.);
vec3 col_bg = vec3(244.,239.,224.)/vec3(255.);
vec3 col_bg_dark = vec3(192.,191.,173.)/vec3(255.);
vec3 col_intersect = vec3(37.,33.,30.)/vec3(255.);

vec4  fC ( in vec2 fragCoord )
{
    vec2 p = (2.0*fragCoord-iResolution.xy)/iResolution.y;
    p/=1.4;
    //p+=vec2(.0, -.15);
    p += vec2(0., -.03);

//p/=4.;
//p+=vec2(-.4,-.2);

    vec3 col = col_bg;

    float m = 0.;

    float q =  mod(iTime/2., 5.);


         if (q <= 1.) q = 0. + gain(q - 0., 6.);
    else if (q <= 2.) q = 1. + gain(q - 1., 4.);
    else if (q <= 3.) q = 2. + gain(q - 2., 5.);
    else if (q <= 4.) q = 3. + gain(q - 3., 6.);
    else if (q <= 5.) q = 4. + gain(q - 4., 5.);
    else if (q <= 6.) q = 5. + gain(q - 5., 6.);

    m = shape_1(m, 0, rotate(p + vec2(.0020, .0300), 0.000), 0.600, 0.014, q);
    m = shape_2(m, 1, rotate(p + vec2(.0580, .0870), 0.110), 0.520, 0.015, q);
    m = shape_3(m, 2, rotate(p + vec2(.1040, .0130), 0.560), 0.432, 0.014, q);
    m = shape_4(m, 3, rotate(p + vec2(.0160, .0100), 0.350), 0.344, 0.020, q);
    m = shape_5(m, 4, rotate(p + vec2(.0910, .0680), 0.665), 0.248, 0.024, q);
    m = shape_6(m, 5, rotate(p + vec2(.0055, .0055), 2.235), 0.144, 0.050, q);

    vec4 col_shapes = vec4(0.);
    if (isBitSet(m, 0)) col_shapes = vec4(col_1, 1.);
    if (isBitSet(m, 1)) col_shapes = vec4(col_2, 1.);
    if (isBitSet(m, 2)) col_shapes = vec4(col_3, 1.);
    if (isBitSet(m, 3)) col_shapes = vec4(col_4, 1.);
    if (isBitSet(m, 4)) col_shapes = vec4(col_5, 1.);
    if (isBitSet(m, 5)) col_shapes = vec4(col_6, 1.);

    vec4 col_intersect_any = vec4(0.);
    if (popcount(m) > 1)
        col_intersect_any = vec4(col_intersect, 1.);

    vec4 col_xor = vec4(0.);

    if (isBitSet(m, 5) && !isBitSet(m, 4))
        col_xor = vec4(col_bg, 1.);
    if (isBitSet(m, 4) && !isBitSet(m, 3))
        col_xor = vec4(col_bg, 1.);
    if (isBitSet(m, 3) && !isBitSet(m, 2))
        col_xor = vec4(col_bg, 1.);
    if (isBitSet(m, 2) && !isBitSet(m, 1))
        col_xor = vec4(col_bg, 1.);
    if (isBitSet(m, 1) && !isBitSet(m, 0))
        col_xor = vec4(col_bg, 1.);

         if (q <= 1.) col_xor = vec4(0.);
    else if (q <= 2.) col_xor = vec4(0.);
    else if (q <= 3.) col_xor = vec4(col_xor.xyz, smoothstep(0., col_xor.w, q - 2.));
    else if (q <= 4.) col_xor = vec4(col_xor.xyz, smoothstep(col_xor.w, 0., q - 3.));
    else if (q <= 5.) col_xor = vec4(0.);
    else if (q <= 6.) col_xor = vec4(0.);

    vec3 bgcol[7] = vec3[](col_bg, col_bg, col_bg_dark, col_bg_dark, col_bg, col_bg, col_bg);

    if (col_shapes.w > 0.) col = col_shapes.xyz;
    else
    {
       for (int i = 0; i < bgcol.length(); ++i) {
            if (q <= float(i + 1)) {
                col = mix(bgcol[i], bgcol[(i + 1) % bgcol.length()], q - float(i)).xyz;
                break;
            }
       }
    }

         if (q <= 1.) col = mix(col, col_intersect_any.xyz, col_intersect_any.w > 0. ? (1.-(q-0.)) : 0.);
    else if (q <= 2.) col = mix(col, col_intersect_any.xyz, 0.);
    else if (q <= 3.) col = mix(col, col_intersect_any.xyz, 0.);
    else if (q <= 4.) col = mix(col, col_intersect_any.xyz, col_intersect_any.w > 0. ? (   (q-3.)) : 0.);
    else if (q <= 5.) col = mix(col, col_intersect_any.xyz, col_intersect_any.w);
    else if (q <= 6.) col = mix(col, col_intersect_any.xyz, col_intersect_any.w);

    //col = mix(col, col_xor.xyz, col_xor.w);
    //col += col_xor.xyz * col_xor.w;

    return vec4(col, 1.);
}

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

    // Antialiasing from https://www.shadertoy.com/view/wtjfRV
    float A = 8., s = 1./A, x, y;

    for (x=-.5; x<.5; x+=s) for (y=-.5; y<.5; y+=s) fragColor += min ( fC(vec2(x,y)+fragCoord), 1.0);

    fragColor /= A*A;
    vec2 p = (2.0*fragCoord-iResolution.xy)/iResolution.y;
    fragColor = vec4(fragColor.xyz + noise(p)/18.,1.0);
}
