#define key(code) (texelFetch(iChannel3, ivec2((code),2),0).x != 0.0)
#define store(i,j) (texelFetch(iChannel2, ivec2((i),(j)),0))
#define keycount(key) (int(store(0,(key)).x))

const int KEY_PAGE_UP = 33;
const int KEY_PAGE_DOWN = 34;
const int KEY_LEFT = 37;
const int KEY_RIGHT = 39;
const int KEY_UP = 38;
const int KEY_DOWN = 40;

const int CHAR_0 = 48;

const int CHAR_A = 65;
const int CHAR_B = 66;
const int CHAR_C = 67;
const int CHAR_D = 68;
const int CHAR_E = 69;
const int CHAR_F = 70;
const int CHAR_G = 71;
const int CHAR_H = 72;
const int CHAR_I = 73;
const int CHAR_J = 74;
const int CHAR_K = 75;
const int CHAR_L = 76;
const int CHAR_M = 77;
const int CHAR_N = 78;
const int CHAR_O = 79;
const int CHAR_P = 80;
const int CHAR_Q = 81;
const int CHAR_R = 82;
const int CHAR_S = 83;
const int CHAR_T = 84;
const int CHAR_U = 85;
const int CHAR_V = 86;
const int CHAR_W = 87;
const int CHAR_X = 88;
const int CHAR_Y = 89;
const int CHAR_Z = 90;

vec3 hsv2rgb(float h, float s, float v) {
  vec3 rgb = clamp( abs(mod(h*6.0+vec3(0.0,4.0,2.0),6.0)-3.0)-1.0, 0.0, 1.0 );
  //rgb = rgb*rgb*(3.0-2.0*rgb); // cubic smoothing       
  return v * mix( vec3(1.0), rgb, s);
}

// From Chris Wellons: https://nullprogram.com/blog/2018/07/31/
uint ihash(uint x) {
    x ^= x >> 16;
    x *= 0x7feb352dU;
    x ^= x >> 15;
    x *= 0x846ca68bU;
    x ^= x >> 16;
    return x;
}

uint ihash(int x) {
  return ihash(uint(x));
}

// Sensible sign function
vec2 ssign(vec2 p) {
  return vec2(p.x < 0.0 ? -1 : 1, p.y < 0.0 ? -1 : 1);
}

float circle(vec2 p, vec3 c) {
  vec2 centre = c.xy;
  float radius = c.z;
  return abs(length(p-centre)-radius);
}

uint ihash(ivec2 index) {
  return ihash(index.x ^ int(ihash(index.y)));
}
float X = sqrt(3.0);

float pwidth;

vec3 docircle(vec3 col, vec2 p, vec2 index, vec2 s, vec2 cindex) {
  index += s*cindex;
  uint h = ihash(ivec2(index));
  vec3 ccol = hsv2rgb(float(h)/pow(2.0,32.0),1.0,1.0);
  vec2 ccentre = 0.5*vec2(1,X)*cindex;
  col = mix(ccol,col,0.2+0.8*smoothstep(0.015,0.025+pwidth,circle(p,vec3(ccentre,1))));
  if (!key(CHAR_D)) {
    float t = iTime;
    if (mod(index.x+index.y,4.0) < 2.0) t = -t;
    vec2 dotpos = ccentre + s*vec2(sin(t),cos(t));
    float dotdist = distance(p,dotpos);
    col = mix(vec3(1),col,smoothstep(0.015,0.025+pwidth,dotdist));
  }
  return col;
}

bool checksegment(vec2 index, vec2 s, float r, vec2 offset) {
  if (key(CHAR_C)) return true;
  vec2 p = 0.5*vec2(1,X)*abs(index+s*offset);
  return dot(p,p) < r*r;
}

vec3 diagram(vec2 p) {
  float scale = 3.0;
  p *= scale;
  vec2 p0 = p;
  pwidth = fwidth(p0.x);
  p.y /= X;
  vec2 index = round(p); // Coords of centre
  p -= index;
  index *= 2.0;
  p.y *= X;
  vec3 col = mod(index.x+index.y,4.0)< 2.0 ? vec3(1) : vec3(0.8);
  vec2 s = ssign(p);
  p = abs(p);
  vec3 ccol = vec3(0);
  float r = mod(iTime,6.0);
  if (checksegment(index,s,r,vec2(1,1))) {
    if (checksegment(index,s,r,vec2(-1,1))) {
      col = docircle(col,p,index,s,vec2(0,2));
    }
    if (checksegment(index,s,r,vec2(2,0))) {
      col = docircle(col,p,index,s,vec2(3,1));
    }
  }
  if (checksegment(index,s,r,vec2(0,0))) {
    if (checksegment(index,s,r,vec2(1,1))) {
      col = docircle(col,p,index,s,vec2(2,0));
      col = docircle(col,p,index,s,vec2(-1,1));
    }
    if (checksegment(index,s,r,vec2(-1,1))) {
      col = docircle(col,p,index,s,vec2(-2,0));
    }
    if (checksegment(index,s,r,vec2(2,0))) {
      col = docircle(col,p,index,s,vec2(1,-1));
    }
  }
  if (!key(CHAR_C)) col = mix(vec3(0),col,smoothstep(0.0,pwidth,abs(length(p0)-r)));
  return col;
}

void mainImage(out vec4 fragColor, vec2 fragCoord ) {
  vec2 p = (2.0*fragCoord-iResolution.xy)/iResolution.y;
  vec3 col = diagram(p);
  col = pow(col,vec3(0.4545));
  fragColor = vec4(col,1);
}
