//-----------------SETTINGS-----------------

//#define TIMES_DETAILED (sin(time*32.0)+1.0)
#define TIMES_DETAILED (1.0+.1*sin(time*PI*1.0))
#define SPIRAL_BLUR_SCALAR (1.0+.1*sin(time*PI*1.0))

//-----------------USEFUL-----------------

#define MOUSE_X (iMouse.x/iResolution.x)
#define MOUSE_Y (iMouse.y/iResolution.y)

#define PI 3.14159265359
#define E 2.7182818284
#define GR 1.61803398875
#define EPS 10.0/max(iResolution.x, iResolution.y)

#define flux(x) (vec3(cos(x),cos(4.0*PI/3.0+x),cos(2.0*PI/3.0+x))*.5+.5)
#define time ((saw(float(__LINE__))+1.0)*(iTime+12345.12345)/PI/2.0)
#define sphereN(uv) (normalize(vec3((uv).xy, sqrt(clamp(1.0-length((uv)), 0.0, 1.0)))))
#define rotatePoint(p,n,theta) (p*cos(theta)+cross(n,p)*sin(theta)+n*dot(p,n) *(1.0-cos(theta)))
float seedling;
float cross( in vec2 a, in vec2 b ) { return a.x*b.y - a.y*b.x; }
#define circle(x) (vec2(cos(x),sin(x)))
float saw(float x)
{
    float f = mod(floor(abs(x)), 2.0);
    float m = mod(abs(x), 1.0);
    return f*(1.0-m)+(1.0-f)*m;
}
vec2 saw(vec2 x)
{
    return vec2(saw(x.x), saw(x.y));
}

vec3 saw(vec3 x)
{
    return vec3(saw(x.x), saw(x.y), saw(x.z));
}
//-----------------SIMPLEX-----------------

vec3 random3(vec3 c) {
    float j = 4096.0*sin(dot(c,vec3(17.0, 59.4, 15.0)));
    vec3 r;
    r.z = fract(512.0*j);
    j *= .125;
    r.x = fract(512.0*j);
    j *= .125;
    r.y = fract(512.0*j);
    return r-0.5;
}

float simplex3d(vec3 p) {
    const float F3 =  0.3333333;
    const float G3 =  0.1666667;
    
    vec3 s = floor(p + dot(p, vec3(F3)));
    vec3 x = p - s + dot(s, vec3(G3));
    
    vec3 e = step(vec3(0.0), x - x.yzx);
    vec3 i1 = e*(1.0 - e.zxy);
    vec3 i2 = 1.0 - e.zxy*(1.0 - e);
    
    vec3 x1 = x - i1 + G3;
    vec3 x2 = x - i2 + 2.0*G3;
    vec3 x3 = x - 1.0 + 3.0*G3;
    
    vec4 w, d;
    
    w.x = dot(x, x);
    w.y = dot(x1, x1);
    w.z = dot(x2, x2);
    w.w = dot(x3, x3);
    
    w = max(0.6 - w, 0.0);
    
    d.x = dot(random3(s), x);
    d.y = dot(random3(s + i1), x1);
    d.z = dot(random3(s + i2), x2);
    d.w = dot(random3(s + 1.0), x3);
    
    w *= w;
    w *= w;
    d *= w;
    
    return dot(d, vec4(52.0));
}

//-----------------IMAGINARY-----------------

vec2 cmul(vec2 v1, vec2 v2) {
	return vec2(v1.x * v2.x - v1.y * v2.y, v1.y * v2.x + v1.x * v2.y);
}

vec2 cdiv(vec2 v1, vec2 v2) {
	return vec2(v1.x * v2.x + v1.y * v2.y, v1.y * v2.x - v1.x * v2.y) / dot(v2, v2);
}

//-----------------RENDERING-----------------
float zoom;

vec2 invBilinear( in vec2 p, in vec2 a, in vec2 b, in vec2 c, in vec2 d )
{
    vec2 e = b-a;
    vec2 f = d-a;
    vec2 g = a-b+c-d;
    vec2 h = p-a;
        
    float k2 = cross( g, f );
    float k1 = cross( e, f ) + cross( h, g );
    float k0 = cross( h, e );
    
    float w = k1*k1 - 4.0*k0*k2;

    w = sqrt(abs( w ));
    
    float v1 = ((-k1 - w)/(2.0*k2));
    float v2 = ((-k1 + w)/(2.0*k2));
    float u1 = ((h.x - f.x*v1)/(e.x + g.x*v1));
    float u2 = ((h.x - f.x*v2)/(e.x + g.x*v2));
    bool  b1a = v1>0.0 && v1<1.0;
    bool  b1b = u1>0.0 && u1<1.0;
    bool  b2a = v2>0.0 && v2<1.0;
    bool  b2b = u2>0.0 && u2<1.0;
    

    vec2 res = vec2(min(abs(u1), abs(u2)), min(abs(v1), abs(v2)));
    
    return res;
}


vec2 SinCos( const in float x )
{
	return vec2(sin(x), cos(x));
}
vec3 RotateZ( const in vec3 vPos, const in vec2 vSinCos )
{
	return vec3( vSinCos.y * vPos.x + vSinCos.x * vPos.y, -vSinCos.x * vPos.x + vSinCos.y * vPos.y, vPos.z);
}
      
vec3 RotateZ( const in vec3 vPos, const in float fAngle )
{
	return RotateZ( vPos, SinCos(fAngle) );
}
vec2 RotateZ( const in vec2 vPos, const in float fAngle )
{
	return RotateZ( vec3(vPos, 0.0), SinCos(fAngle) ).xy;
}
mat4 RotateZ( const in mat4 vPos, const in float fAngle )
{
	return mat4(RotateZ( vec3(vPos[0].xy, 0.0), SinCos(fAngle) ).xy, 0.0, 0.0,
                RotateZ( vec3(vPos[1].xy, 0.0), SinCos(fAngle) ).xy, 0.0, 0.0,
                RotateZ( vec3(vPos[2].xy, 0.0), SinCos(fAngle) ).xy, 0.0, 0.0,
                RotateZ( vec3(vPos[3].xy, 0.0), SinCos(fAngle) ).xy, 0.0, 0.0);
}
mat4 translate( const in mat4 vPos, vec2 offset )
{
	return mat4(vPos[0].xy+offset, 0.0, 0.0,
                vPos[1].xy+offset, 0.0, 0.0,
                vPos[2].xy+offset, 0.0, 0.0,
                vPos[3].xy+offset, 0.0, 0.0);
} 
mat4 scale( const in mat4 vPos, vec2 factor )
{
	return mat4(vPos[0].xy*factor, 0.0, 0.0,
                vPos[1].xy*factor, 0.0, 0.0,
                vPos[2].xy*factor, 0.0, 0.0,
                vPos[3].xy*factor, 0.0, 0.0);
} 
float magnification;
vec2 tree(vec2 uv)
{
    float w1 = saw(time);
    
    mat4 square = mat4(EPS, EPS, 0.0, 0.0,
                       1.0-EPS, EPS, 0.0, 0.0,
                       1.0-EPS, 1.0-EPS, 0.0, 0.0,
                       0.0, 1.0-EPS, 0.0, 0.0);
    
    float size =  .5;
    
    square = scale(square, vec2(2.0));
    square = translate(square, vec2(-1.0));
    
    //square = scale(square, (1.0-saw(uv.xy))*vec2(saw(t1)+.5));
    
    square = RotateZ(square, time+seedling);
    
    float t1 = time;
    float t2 = time;
    square = scale(square, vec2(saw(uv.y*uv.x+t2+seedling)*.25+.75));
    
    square = scale(square, vec2(.5));
    square = translate(square, circle(uv.x*PI*2.0+time+seedling)/(6.0+5.0*saw(time)));
    square = translate(square, circle(uv.y*PI*2.0+time+seedling)/(6.0+5.0*saw(time)));
    
    
    float t = time;
    vec3 f = flux(time);
    
    
    vec2 dxdy = sqrt(4.0)/iResolution.xy;
    
    vec2 a = uv+vec2(0.0, 		0.0);
    vec2 b = uv+vec2(1.0, 	0.0);
    vec2 c = uv+vec2(0.0, 		1.0);
    
    vec2 a2 = invBilinear(a, square[0].xy, square[1].xy, square[2].xy, square[3].xy);
    vec2 b2 = invBilinear(b, square[0].xy, square[1].xy, square[2].xy, square[3].xy);
    vec2 c2 = invBilinear(c, square[0].xy, square[1].xy, square[2].xy, square[3].xy);
    
    magnification = abs(cross(square[3].xy-square[0].xy, square[1].xy-square[0].xy)/cross(b2-a2, c2-a2));
    
    return saw(a2);
}

float draw(vec2 uv)
{
    return 1.0-abs(abs(saw(uv.x*(1.5+sin(iTime*.654321))*PI+iTime*.7654321)*2.0-1.0)-abs(uv.y));
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    float aspect = iResolution.y/iResolution.x;
   
    vec2 uv = fragCoord.xy/iResolution.xy;
    
   	const int max_i = 5;
    float stretch = 1.0;
    float ifs = 1.0;
    float depth = 0.0;
    float zoom = 1.5+saw(time);
    
    uv = uv*2.0-1.0;
    uv *= zoom;
    uv = uv*.5+.5;
    for(int i = 0; i < max_i; i++)
    {
        seedling = float(i)/float(max_i);
        vec2 next = tree((uv));
        ifs /= magnification+1.0;
    	float weight = pow(ifs, 1.0/float(i+1)/2.0);
        depth += (length(uv*2.0-1.0))*weight;
        uv = saw(time+(next*2.0-1.0)*weight);//*weight+vec2(.5)*(1.0-weight);//*weight+uv*(1.0-weight);
    }
    
    
    fragColor = vec4(uv, 0.0, 1.0);
    
    depth /= float(max_i)/5.0;

    float weight = pow(ifs, 1.0/float(max_i)/2.0);

    float black = smoothstep(0.0, 1.0/5.0, saw(depth-time));
    float white = smoothstep(4.0/5.0, 1.0, saw(depth-time));
    
    fragColor = vec4(uv, 0.0, 1.0)*weight;
    fragColor = vec4(clamp(sqrt(weight), 0.0, 1.0)*(flux(time+depth)*black+white), 1.0);
    
}
