#define PI 3.14159265

mat2 rot(float a) {
   float s=sin(a), c=cos(a);
   return mat2(c,s,-s,c);
}

float hash21(vec2 p) {
    p = fract(p * vec2(123.21, 552.53));
    p += dot(p, p+34.21);
    return fract(p.x * p.y);
}

vec2 hash22(vec2 p) {
   vec3 p3 = fract(vec3(p.xyx) * vec3(231.23,423.24, 521.23));
   p3 += dot(p3, p3.yzx + 23.42);
   return fract((p3.xx + p3.zy) * p3.zy);
}

float fadeCircle(vec2 gv, float spread) {
    vec2 p = gv * spread;
    return 1.0/dot(p,p);
}

float circle(vec2 gv, float spread) {
    return step(length(gv), spread);
}

/*
       .P
      /|
   pa/ |(pa - ba*h)
    /  |
   A---X-----------B
   |---|   ba
     h 
     
   The dot product (pa,ba) projects pa onto ba and normalizes with the length of ba.
*/
float distanceFromLine( in vec2 a, in vec2 b, in vec2 p) {
    vec2 pa = p - a, ba = b - a;
	float h = clamp(dot(pa,ba) / dot(ba,ba), 0., 1.);
    
	return length(pa - ba * h);
}

float distanceFromWave( in vec2 a, in vec2 b, in vec2 p) {
    vec2 pa = p - a, ba = b - a;
	float h = clamp(dot(pa,ba) / dot(ba,ba), 0., 1.);
    float invH = sign(dot(vec2(-pa.y, pa.x), ba));
	return length(pa - ba * h) + invH * 0.1 * sin(PI * h * 1.0) * sin(PI * h * 16.0);
}

vec2 getPos(vec2 id, vec2 gv) {
    vec2 o = hash22(id) * .2;
    return gv - (o + o * vec2(sin(iTime), cos(iTime)) * .8);
}

vec2 getPos(vec2 id) {
    vec2 o = hash22(id) * .2;
    return id + o + o * vec2(sin(iTime), cos(iTime)) * .8;
}

float layer(vec2 uv, float scale, float angularSpeed) {
    uv += vec2(sin(iTime * 0.1), cos(iTime * 0.2));
    
    uv *= rot(iTime * angularSpeed);
    uv *= scale;
    
    /*
    float ripples = 0.03 * sin(iTime * 0.2);
    vec2 distort = uv * scale - scale/2.0;
    float v = sin(sqrt(distort.x*distort.x + distort.y * distort.y) + iTime + 1.0);
    distort = vec2(sin(PI*v*ripples),cos(PI*v*ripples));
    uv += distort;
    */
    
    //local uv within the grid cell (gv)
    vec2 gv = fract(uv) -.5;
    //id (row/col) of current cell
    vec2 id = floor(uv);
    //the circle
    //float d = circle(getPos(id, gv), 0.05);
    float d = fadeCircle(getPos(id, gv), 30.0);
    //lines from position of circle in cell id to cell id+(x,y)
    d += smoothstep(.05, .02, distanceFromWave(getPos(id), getPos(id+vec2(1.0, 0.0)),  id + gv));
    d += smoothstep(.05, .02, distanceFromWave(getPos(id), getPos(id+vec2(-1.0, 0.0)),  id + gv));
    d += smoothstep(.05, .02, distanceFromWave(getPos(id), getPos(id+vec2(0.0, 1.0)),  id + gv));
    d += smoothstep(.05, .02, distanceFromWave(getPos(id), getPos(id+vec2(0.0, -1.0)),  id + gv));
    
    return d;
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    // Normalized pixel coordinates (from 0 to 1)
    vec2 uv = (fragCoord-.5 * iResolution.xy)/iResolution.y;
    
    float d = layer(uv, 5.0 + sin(iTime * 0.3), 0.1);
    
    //second layer with different opacity...
    d+= layer(uv, 5.0 + sin(iTime * 0.4), 0.05) * 0.5;
     
    vec3 col = vec3(d * (0.5 + sin(iTime * 0.5) * 0.5),
                    d * (0.5 + cos(iTime * 0.3) * 0.5),
                    d * (0.5 + sin(1.5 + iTime * 0.4) * 0.5));
    // Output to screen
    fragColor = vec4(col,1.0);
}
