1// Compatibility #ifdefs needed for parameters
2#ifdef GL_ES
3#define COMPAT_PRECISION mediump
4#else
5#define COMPAT_PRECISION
6#endif
7
8// Parameter lines go here:
9#pragma parameter RETRO_PIXEL_SIZE "Retro Pixel Size" 0.84 0.0 1.0 0.01
10#ifdef PARAMETER_UNIFORM
11// All parameter floats need to have COMPAT_PRECISION in front of them
12uniform COMPAT_PRECISION float RETRO_PIXEL_SIZE;
13#else
14#define RETRO_PIXEL_SIZE 0.84
15#endif
16
17#if defined(VERTEX)
18
19#if __VERSION__ >= 130
20#define COMPAT_VARYING out
21#define COMPAT_ATTRIBUTE in
22#define COMPAT_TEXTURE texture
23#else
24#define COMPAT_VARYING varying
25#define COMPAT_ATTRIBUTE attribute
26#define COMPAT_TEXTURE texture2D
27#endif
28
29#ifdef GL_ES
30#define COMPAT_PRECISION mediump
31#else
32#define COMPAT_PRECISION
33#endif
34
35COMPAT_ATTRIBUTE vec4 VertexCoord;
36COMPAT_ATTRIBUTE vec4 COLOR;
37COMPAT_ATTRIBUTE vec4 TexCoord;
38COMPAT_VARYING vec4 COL0;
39COMPAT_VARYING vec4 TEX0;
40// out variables go here as COMPAT_VARYING whatever
41
42vec4 _oPosition1;
43uniform mat4 MVPMatrix;
44uniform COMPAT_PRECISION int FrameDirection;
45uniform COMPAT_PRECISION int FrameCount;
46uniform COMPAT_PRECISION vec2 OutputSize;
47uniform COMPAT_PRECISION vec2 TextureSize;
48uniform COMPAT_PRECISION vec2 InputSize;
49
50// compatibility #defines
51#define vTexCoord TEX0.xy
52#define SourceSize vec4(TextureSize, 1.0 / TextureSize) //either TextureSize or InputSize
53#define OutSize vec4(OutputSize, 1.0 / OutputSize)
54
55void main()
56{
57    gl_Position = MVPMatrix * VertexCoord;
58    TEX0.xy = VertexCoord.xy;
59// Paste vertex contents here:
60}
61
62#elif defined(FRAGMENT)
63
64#if __VERSION__ >= 130
65#define COMPAT_VARYING in
66#define COMPAT_TEXTURE texture
67out vec4 FragColor;
68#else
69#define COMPAT_VARYING varying
70#define FragColor gl_FragColor
71#define COMPAT_TEXTURE texture2D
72#endif
73
74#ifdef GL_ES
75#ifdef GL_FRAGMENT_PRECISION_HIGH
76precision highp float;
77#else
78precision mediump float;
79#endif
80#define COMPAT_PRECISION mediump
81#else
82#define COMPAT_PRECISION
83#endif
84
85uniform COMPAT_PRECISION int FrameDirection;
86uniform COMPAT_PRECISION int FrameCount;
87uniform COMPAT_PRECISION vec2 OutputSize;
88uniform COMPAT_PRECISION vec2 TextureSize;
89uniform COMPAT_PRECISION vec2 InputSize;
90uniform sampler2D Texture;
91COMPAT_VARYING vec4 TEX0;
92// in variables go here as COMPAT_VARYING whatever
93
94// compatibility #defines
95#define Source Texture
96#define vTexCoord TEX0.xy
97
98#define SourceSize vec4(TextureSize, 1.0 / TextureSize) //either TextureSize or InputSize
99#define OutSize vec4(OutputSize, 1.0 / OutputSize)
100
101// delete all 'params.' or 'registers.' or whatever in the fragment
102float iGlobalTime = float(FrameCount)*0.025;
103vec2 iResolution = OutputSize.xy;
104
105// ============================================================================================
106// Base-10 digit extraction of Pi using a lovely formula I discovered which
107// has a nice base-10 feel to it!
108//
109// pi/4 = 7/10 + 22/200 - 52/3000 - 312/40000 + 2/5000000 + 2852/6000000 + ...
110//
111// The nth term is given by ImaginaryPart[3(3i+1)^n - (2i-1)^n] / n10^n
112
113// The code represents each part of the imaginary number in the spiral terms
114// using a BIGNUM encoded in a vec4 - i.e. each float uses numbers less than
115// 2^24. This works in C++ with IEEE! But it depends on the vagaries of your
116// GPU and WebGL translation if you see the correct digits of pi. Sorry!
117
118// Thanks to Fabrice for spotting that the 56th digit was wrong. Fixed now!
119
120// Derivation of the formula:
121//
122// Using the logarithmic form of ArcTan(x), find the Taylor series of ArcTan(x) about 1/a
123//
124// ArcTan(x) = ArcTan(1/a) + SumOverN of [Q(n) * a^n * (x-1/a)^n / n(1+a^2)]
125//
126// The 1+a^2 on the bottom is interesting! It suggests values of a of 2, 3 or 7.
127//
128// Q(n) = i * ((-ia - 1)^n - (ia-1)^n)/2
129//
130// Which, for our purposes, we can simplify to just ImaginaryPart[ (ia - 1)^n ]
131//
132// This gives us the final formula.
133//
134// ArcTan(x) - ArcTan(1/a) = 1/n(1+a^2) * (x-1/a)^n * a^n * ImaginaryPart[ (ia - 1)^n ]
135//
136// For judicious choices of x and a we can get useful formulae. For instance:
137//
138// ArcTan(3/4) - ArcTan(1/2) = SumOverN of 1/n10^n * ImaginaryPart[( 2i-1)^n]
139// ArcTan(2/3) - ArcTan(1/3) = SumOverN of 1/n10^n * ImaginaryPart[( 3i-1)^n]
140// ArcTan(0/3) - ArcTan(1/3) = SumOverN of 1/n10^n * ImaginaryPart[(-3i+1)^n]
141//
142// Using the well known ArcTan addition formula
143//
144// ArcTan(3/4) - ArcTan(1/2) =  ArcTan(2/11)
145// ArcTan(2/3) - ArcTan(1/3) =  ArcTan(3/11)
146// ArcTan(0/3) - ArcTan(1/3) = -ArcTan(1/3)	... this is easy :)
147//
148// Then all we need to get pi is the Machin type formula, pi/4 = 3 ArcTan[1/3] - ArcTan[2/11]
149//
150// QED
151//
152// https://www.wolframalpha.com/input/?i=Sum+for+n+%3D+1+to+100+of+4+Im%5B3(3i%2B1)%5En-(2i-1)%5En%5D%2F(n+10%5En)
153//
154
155// Feeling brave? Try using ivecs for an extra 20 digits. Only works if you
156// have full 32-precision ints... and your WebGL implementation may crash!
157#define USE_INTEGERS 1
158
159#if USE_INTEGERS
160
161const int POW10_PER_COMPONENT	= 7;
162const int BASE_FOR_NUMBER		= 10000000;
163const int MAX_DIGIT				= 60;
164const int MAX_1OVER3_TERMS		= 122;
165const int MAX_2OVER11_TERMS		= 93;
166
167bool IsZero(ivec4 lo)
168{
169	return lo.x == 0 && lo.y == 0 && lo.z == 0 && lo.w == 0;
170}
171
172// Returns +1(a>b), 0, -1(a<b)
173int CompareAbsValues(ivec4 a, ivec4 b)
174{
175	if (a.w > b.w) {return +1;}
176	if (a.w < b.w) {return -1;}
177
178	if (a.z > b.z) {return +1;}
179	if (a.z < b.z) {return -1;}
180
181	if (a.y > b.y) {return +1;}
182	if (a.y < b.y) {return -1;}
183
184	if (a.x > b.x) {return +1;}
185	if (a.x < b.x) {return -1;}
186
187	return 0;
188}
189
190void DivMod(int a, int b, out int out_div, out int out_mod)
191{
192	if (a == 0)
193	{
194		out_div = 0;
195		out_mod = 0;
196
197		return;
198	}
199
200	out_div = a / b;
201	out_mod = a - out_div * b;
202}
203
204int Mod(int a, int b)
205{
206	int div = a / b;
207	int mod = a - b * div;
208
209	return mod;
210}
211
212ivec4 Div(ivec4 a, int divisor, out int out_mod)
213{
214	ivec4 ans = a;
215
216	if (ans.w != 0)
217	{
218		int div_w;
219		int mod_w;
220
221		DivMod(ans.w, divisor, div_w, mod_w);
222
223		ans.w  = div_w;
224		ans.z += mod_w * BASE_FOR_NUMBER;
225	}
226
227	if (ans.z != 0)
228	{
229		int div_z;
230		int mod_z;
231
232		DivMod(ans.z, divisor, div_z, mod_z);
233
234		ans.z  = div_z;
235		ans.y += mod_z * BASE_FOR_NUMBER;
236	}
237
238	if (ans.y != 0)
239	{
240		int div_y;
241		int mod_y;
242
243		DivMod(ans.y, divisor, div_y, mod_y);
244
245		ans.y  = div_y;
246		ans.x += mod_y * BASE_FOR_NUMBER;
247	}
248
249	if (ans.x != 0)
250	{
251		int div_x;
252		int mod_x;
253
254		DivMod(ans.x, divisor, div_x, mod_x);
255
256		ans.x = div_x;
257
258		out_mod =  mod_x;
259	}
260	else
261	{
262		out_mod = 0;
263	}
264
265	return ans;
266}
267
268ivec4 Double(ivec4 a)
269{
270	ivec4 ans = a + a;
271
272	if (ans.x >= BASE_FOR_NUMBER)
273	{
274		ans.x -= BASE_FOR_NUMBER;
275		ans.y += 1;
276	}
277
278	if (ans.y >= BASE_FOR_NUMBER)
279	{
280		ans.y -= BASE_FOR_NUMBER;
281		ans.z += 1;
282	}
283
284	if (ans.z >= BASE_FOR_NUMBER)
285	{
286		ans.z -= BASE_FOR_NUMBER;
287		ans.w += 1;
288	}
289
290	return ans;
291}
292
293ivec4 Treble(ivec4 a)
294{
295	ivec4 ans = a + a + a;
296
297	if (ans.x >= BASE_FOR_NUMBER)
298	{
299		ans.x -= BASE_FOR_NUMBER;
300		ans.y += 1;
301
302		if (ans.x >= BASE_FOR_NUMBER)
303		{
304			ans.x -= BASE_FOR_NUMBER;
305			ans.y += 1;
306		}
307	}
308
309	if (ans.y >= BASE_FOR_NUMBER)
310	{
311		ans.y -= BASE_FOR_NUMBER;
312		ans.z += 1;
313
314		if (ans.y >= BASE_FOR_NUMBER)
315		{
316			ans.y -= BASE_FOR_NUMBER;
317			ans.z += 1;
318		}
319	}
320
321	if (ans.z >= BASE_FOR_NUMBER)
322	{
323		ans.z -= BASE_FOR_NUMBER;
324		ans.w += 1;
325
326		if (ans.z >= BASE_FOR_NUMBER)
327		{
328			ans.z -= BASE_FOR_NUMBER;
329			ans.w += 1;
330		}
331	}
332
333	return ans;
334}
335
336ivec4 Add(ivec4 a, ivec4 b)
337{
338	ivec4 ans = a + b;
339
340	if (ans.x >= BASE_FOR_NUMBER)
341	{
342		ans.x -= BASE_FOR_NUMBER;
343		ans.y += 1;
344	}
345
346	if (ans.y >= BASE_FOR_NUMBER)
347	{
348		ans.y -= BASE_FOR_NUMBER;
349		ans.z += 1;
350	}
351
352	if (ans.z >= BASE_FOR_NUMBER)
353	{
354		ans.z -= BASE_FOR_NUMBER;
355		ans.w += 1;
356	}
357
358	return ans;
359}
360
361// a must be > b
362ivec4 Sub(ivec4 a, ivec4 b)
363{
364	ivec4 ans = a - b;
365
366	if (ans.x < 0)
367	{
368		ans.x += BASE_FOR_NUMBER;
369		ans.y -= 1;
370	}
371
372	if (ans.y < 0)
373	{
374		ans.y += BASE_FOR_NUMBER;
375		ans.z -= 1;
376	}
377
378	if (ans.z < 0)
379	{
380		ans.z += BASE_FOR_NUMBER;
381		ans.w -= 1;
382	}
383
384	return ans;
385}
386
387ivec4 Add(ivec4 a, bool aneg, ivec4 b, bool bneg, out bool out_a_plus_b_neg)
388{
389	if (aneg == bneg)
390	{
391		out_a_plus_b_neg = aneg;
392
393		return Add(a,b);
394	}
395
396	// Signs are different.
397	int sign = CompareAbsValues(a,b);
398
399	if (sign == 0)
400	{
401		out_a_plus_b_neg = false;
402
403		return ivec4(0,0,0,0);
404	}
405
406	if (sign < 0)
407	{
408		out_a_plus_b_neg = bneg;
409
410		return Sub(b,a);
411	}
412
413	out_a_plus_b_neg = aneg;
414
415	return Sub(a,b);
416}
417
418// Divides by BASE_FOR_NUMBER.
419void ApplyShift(out ivec4 a)
420{
421	a.x = a.y;
422	a.y = a.z;
423	a.z = a.w;
424	a.w = 0;
425}
426
427// Return Frac(10^power * Abs(num)/denom)
428float GetFractionalPart(ivec4 numerator, int denominator, int power_of_ten)
429{
430	if (power_of_ten >= 0)
431	{
432		int m;
433		Div(numerator, denominator, m);
434
435		const int MAX_ITERS = MAX_DIGIT / POW10_PER_COMPONENT;
436
437		for (int iter = 0; iter < MAX_ITERS; iter++)
438		{
439			if (power_of_ten < POW10_PER_COMPONENT)
440			{
441				break;
442			}
443
444			m *= BASE_FOR_NUMBER;
445			m  = Mod(m, denominator);
446
447			power_of_ten -= POW10_PER_COMPONENT;
448
449			if (m == 0)
450			{
451				return 0.0;
452			}
453		}
454
455		if (power_of_ten >= 4) {m = Mod(10000 * m, denominator); power_of_ten -= 4;}
456		if (power_of_ten >= 2) {m = Mod(100   * m, denominator); power_of_ten -= 2;}
457		if (power_of_ten >= 1) {m = Mod(10    * m, denominator); power_of_ten -= 1;}
458
459		return float(m) / float(denominator);
460	}
461
462	const int NUM_POWERS_OF_10_TO_KEEP = 4;
463
464	// Throw away terms we don't need.
465	const int MAX_ITERS = MAX_DIGIT / POW10_PER_COMPONENT;
466
467	for (int iter = 0; iter < MAX_ITERS; iter++)
468	{
469		if (power_of_ten + POW10_PER_COMPONENT > -NUM_POWERS_OF_10_TO_KEEP)
470		{
471			break;
472		}
473
474		ApplyShift(numerator);
475
476		if (IsZero(numerator))
477		{
478			return 0.0;
479		}
480
481		power_of_ten += POW10_PER_COMPONENT;
482	}
483
484	// Divide by the denominator to get the fractional part in the wrong place...
485	int the_mod;
486
487	numerator = Div(numerator, denominator, the_mod);
488
489	float ans = float(the_mod) / float(denominator);
490
491	// We can't divide by more than 100 at a time.
492	const int MAX_DIV100_ITERS = (NUM_POWERS_OF_10_TO_KEEP + POW10_PER_COMPONENT) / 2;
493
494	for (int iter = 0; iter < MAX_DIV100_ITERS; iter++)
495	{
496		if (power_of_ten > -2)
497		{
498			break;
499		}
500
501		numerator = Div(numerator, 100, the_mod);
502
503		ans += float(the_mod);
504		ans *= 0.01;
505
506		power_of_ten += 2;
507	}
508
509	// And one more if required.
510	if (power_of_ten == -1)
511	{
512		numerator = Div(numerator, 100, the_mod);
513
514		ans += float(the_mod);
515		ans *= 0.1;
516	}
517
518	return ans - floor(ans);
519}
520
521// Im((2i - 1)^n) / (10^n n)
522float GetNthDigitOfSpiral2(int nth_digit)
523{
524	int		num_terms	= 8 + (MAX_2OVER11_TERMS-8) * nth_digit / (MAX_DIGIT-1);
525	int		shift		= 0;
526	float	sum			= 0.0;
527
528	ivec4 re = ivec4(1,0,0,0); bool re_neg = true;
529	ivec4 im = ivec4(2,0,0,0); bool im_neg = false;
530
531	for (int term = 1; term < MAX_2OVER11_TERMS; term++)
532	{
533		int shifted_digit = nth_digit - term + shift;
534
535		float f = GetFractionalPart(im, term, shifted_digit);
536
537		if (im_neg)
538		{
539			sum -= f;
540		}
541		else
542		{
543			sum += f;
544		}
545
546		if (im.w*3 < 0 ||
547			re.w*3 < 0)
548		{
549			int mod;
550
551			im = Div(im,10,mod);
552			re = Div(re,10,mod);
553
554			shift += 1;
555		}
556
557		bool new_re_neg;
558		bool new_im_neg;
559
560		ivec4 new_re = Add(Double(im), !im_neg, re, !re_neg, new_re_neg);
561		ivec4 new_im = Add(Double(re),  re_neg, im, !im_neg, new_im_neg);
562
563		re = new_re; re_neg = new_re_neg;
564		im = new_im; im_neg = new_im_neg;
565
566		if (term == num_terms)
567		{
568			break;
569		}
570	}
571
572	sum = sum - floor(sum);
573
574	return sum;
575}
576
577// Im((3i + 1)^n) / (10^n n)
578float GetNthDigitOfSpiral3(int nth_digit)
579{
580	int		num_terms	= 8 + (MAX_1OVER3_TERMS-8) * nth_digit / (MAX_DIGIT-1);
581	int		shift		= 0;
582	float	sum			= 0.0;
583
584	ivec4 re = ivec4(1,0,0,0); bool re_neg = false;
585	ivec4 im = ivec4(3,0,0,0); bool im_neg = false;
586
587	for (int term = 1; term < MAX_1OVER3_TERMS; term++)
588	{
589		int shifted_digit = nth_digit - term + shift;
590
591		float f = GetFractionalPart(im, term, shifted_digit);
592
593		if (im_neg)
594		{
595			sum -= f;
596		}
597		else
598		{
599			sum += f;
600		}
601
602		if (im.w*3 < 0 ||
603			re.w*3 < 0)
604		{
605			int mod;
606
607			im = Div(im,10,mod);
608			re = Div(re,10,mod);
609
610			shift += 1;
611		}
612
613		bool new_re_neg;
614		bool new_im_neg;
615
616		ivec4 new_re = Add(Treble(im), !im_neg, re, re_neg, new_re_neg);
617		ivec4 new_im = Add(Treble(re),  re_neg, im, im_neg, new_im_neg);
618
619		re = new_re; re_neg = new_re_neg;
620		im = new_im; im_neg = new_im_neg;
621
622		if (term == num_terms)
623		{
624			break;
625		}
626	}
627
628	sum = sum - floor(sum);
629
630	return sum;
631}
632
633int GetNthDigitOfPi(int nth_digit)
634{
635	float a = GetNthDigitOfSpiral3(nth_digit);
636	float b = GetNthDigitOfSpiral2(nth_digit);
637
638	float s = 4.0 * (a*3.0-b);
639
640	s -= floor(s);
641
642	int digit = int(floor(10.0 * s));
643
644	return digit;
645}
646
647#else
648
649const float	POW10_PER_COMPONENT	= 5.0;
650const float	BASE_FOR_NUMBER		= 100000.0;
651const float	MAX_DIGIT			= 40.0;
652const float	MAX_1OVER3_TERMS	= 80.0;
653const float	MAX_2OVER11_TERMS	= 60.0;
654
655bool IsZero(vec4 a)
656{
657	return a.x == 0.0 && a.y == 0.0 && a.z == 0.0 && a.w == 0.0;
658}
659
660// Returns +1(a>b), 0, -1(a<b)
661float CompareAbsValues(vec4 a, vec4 b)
662{
663	if (a.w > b.w) {return +1.0;}
664	if (a.w < b.w) {return -1.0;}
665
666	if (a.z > b.z) {return +1.0;}
667	if (a.z < b.z) {return -1.0;}
668
669	if (a.y > b.y) {return +1.0;}
670	if (a.y < b.y) {return -1.0;}
671
672	if (a.x > b.x) {return +1.0;}
673	if (a.x < b.x) {return -1.0;}
674
675	return 0.0;
676}
677
678void DivMod(float a, float b, out float out_div, out float out_mod)
679{
680	if (a == 0.0)
681	{
682		out_div = 0.0;
683		out_mod = 0.0;
684
685		return;
686	}
687
688	float d = floor(a / b);
689
690	out_div = d;
691	out_mod = a - d * b;
692}
693
694float Mod(float a, float b)
695{
696	float d		= floor(a / b);
697	float mod	= a - d * b;
698
699	return mod;
700}
701
702vec4 Div(vec4 a, float divisor, out float out_mod)
703{
704	vec4 ans = a;
705
706	if (ans.w != 0.0)
707	{
708		float div_w;
709		float mod_w;
710
711		DivMod(ans.w, divisor, div_w, mod_w);
712
713		ans.w  = div_w;
714		ans.z += mod_w * BASE_FOR_NUMBER;
715	}
716
717	if (ans.z != 0.0)
718	{
719		float div_z;
720		float mod_z;
721
722		DivMod(ans.z, divisor, div_z, mod_z);
723
724		ans.z  = div_z;
725		ans.y += mod_z * BASE_FOR_NUMBER;
726	}
727
728	if (ans.y != 0.0)
729	{
730		float div_y;
731		float mod_y;
732
733		DivMod(ans.y, divisor, div_y, mod_y);
734
735		ans.y  = div_y;
736		ans.x += mod_y * BASE_FOR_NUMBER;
737	}
738
739	if (ans.x != 0.0)
740	{
741		float div_x;
742		float mod_x;
743
744		DivMod(ans.x, divisor, div_x, mod_x);
745
746		ans.x = div_x;
747
748		out_mod =  mod_x;
749	}
750	else
751	{
752		out_mod = 0.0;
753	}
754
755	return ans;
756}
757
758vec4 Double(vec4 a)
759{
760	vec4 ans = a + a;
761
762	if (ans.x >= BASE_FOR_NUMBER)
763	{
764		ans.x -= BASE_FOR_NUMBER;
765		ans.y += 1.0;
766	}
767
768	if (ans.y >= BASE_FOR_NUMBER)
769	{
770		ans.y -= BASE_FOR_NUMBER;
771		ans.z += 1.0;
772	}
773
774	if (ans.z >= BASE_FOR_NUMBER)
775	{
776		ans.z -= BASE_FOR_NUMBER;
777		ans.w += 1.0;
778	}
779
780	return ans;
781}
782
783vec4 Treble(vec4 a)
784{
785	vec4 ans = a + a + a;
786
787	if (ans.x >= BASE_FOR_NUMBER)
788	{
789		ans.x -= BASE_FOR_NUMBER;
790		ans.y += 1.0;
791
792		if (ans.x >= BASE_FOR_NUMBER)
793		{
794			ans.x -= BASE_FOR_NUMBER;
795			ans.y += 1.0;
796		}
797	}
798
799	if (ans.y >= BASE_FOR_NUMBER)
800	{
801		ans.y -= BASE_FOR_NUMBER;
802		ans.z += 1.0;
803
804		if (ans.y >= BASE_FOR_NUMBER)
805		{
806			ans.y -= BASE_FOR_NUMBER;
807			ans.z += 1.0;
808		}
809	}
810
811	if (ans.z >= BASE_FOR_NUMBER)
812	{
813		ans.z -= BASE_FOR_NUMBER;
814		ans.w += 1.0;
815
816		if (ans.z >= BASE_FOR_NUMBER)
817		{
818			ans.z -= BASE_FOR_NUMBER;
819			ans.w += 1.0;
820		}
821	}
822
823	return ans;
824}
825
826vec4 Add(vec4 a, vec4 b)
827{
828	vec4 ans = a + b;
829
830	if (ans.x >= BASE_FOR_NUMBER)
831	{
832		ans.x -= BASE_FOR_NUMBER;
833		ans.y += 1.0;
834	}
835
836	if (ans.y >= BASE_FOR_NUMBER)
837	{
838		ans.y -= BASE_FOR_NUMBER;
839		ans.z += 1.0;
840	}
841
842	if (ans.z >= BASE_FOR_NUMBER)
843	{
844		ans.z -= BASE_FOR_NUMBER;
845		ans.w += 1.0;
846	}
847
848	return ans;
849}
850
851// a must be > b
852vec4 Sub(vec4 a, vec4 b)
853{
854	vec4 ans = a - b;
855
856	if (ans.x < 0.0)
857	{
858		ans.x += BASE_FOR_NUMBER;
859		ans.y -= 1.0;
860	}
861
862	if (ans.y < 0.0)
863	{
864		ans.y += BASE_FOR_NUMBER;
865		ans.z -= 1.0;
866	}
867
868	if (ans.z < 0.0)
869	{
870		ans.z += BASE_FOR_NUMBER;
871		ans.w -= 1.0;
872	}
873
874	return ans;
875}
876
877vec4 Add(vec4 a, bool aneg, vec4 b, bool bneg, out bool out_a_plus_b_neg)
878{
879	if (aneg == bneg)
880	{
881		out_a_plus_b_neg = aneg;
882
883		return Add(a,b);
884	}
885
886	// Signs are different.
887	float sign = CompareAbsValues(a,b);
888
889	if (sign == 0.0)
890	{
891		out_a_plus_b_neg = false;
892
893		return vec4(0.0,0.0,0.0,0.0);
894	}
895
896	if (sign < 0.0)
897	{
898		out_a_plus_b_neg = bneg;
899
900		return Sub(b,a);
901	}
902
903	out_a_plus_b_neg = aneg;
904
905	return Sub(a,b);
906}
907
908// Divides by BASE_FOR_NUMBER.
909void ApplyShift(out vec4 a)
910{
911	a.x = a.y;
912	a.y = a.z;
913	a.z = a.w;
914	a.w = 0.0;
915}
916
917// Return Frac(10^power * Abs(num)/denom)
918float GetFractionalPart(vec4 numerator, float denominator, float power_of_ten)
919{
920	if (power_of_ten >= 0.0)
921	{
922		float m;
923		Div(numerator, denominator, m);
924
925		const int MAX_ITERS = int(MAX_DIGIT / POW10_PER_COMPONENT);
926
927		for (int iter = 0; iter < MAX_ITERS; iter++)
928		{
929			if (power_of_ten < POW10_PER_COMPONENT)
930			{
931				break;
932			}
933
934			m *= BASE_FOR_NUMBER;
935			m  = Mod(m, denominator);
936
937			power_of_ten -= POW10_PER_COMPONENT;
938
939			if (m == 0.0)
940			{
941				return 0.0;
942			}
943		}
944
945		if (power_of_ten >= 4.0) {m = Mod(10000.0 * m, denominator); power_of_ten -= 4.0;}
946		if (power_of_ten >= 2.0) {m = Mod(100.0   * m, denominator); power_of_ten -= 2.0;}
947		if (power_of_ten >= 1.0) {m = Mod(10.0    * m, denominator); power_of_ten -= 1.0;}
948
949		return float(m) / float(denominator);
950	}
951
952	const float NUM_POWERS_OF_10_TO_KEEP = 4.0;
953
954	// Throw away terms we don't need.
955	const int MAX_ITERS = int(MAX_DIGIT / POW10_PER_COMPONENT);
956
957	for (int iter = 0; iter < MAX_ITERS; iter++)
958	{
959		if (power_of_ten + POW10_PER_COMPONENT > -NUM_POWERS_OF_10_TO_KEEP)
960		{
961			break;
962		}
963
964		ApplyShift(numerator);
965
966		if (IsZero(numerator))
967		{
968			return 0.0;
969		}
970
971		power_of_ten += POW10_PER_COMPONENT;
972	}
973
974	// Divide by the denominator to get the fractional part in the wrong place...
975	float the_mod;
976
977	numerator = Div(numerator, denominator, the_mod);
978
979	float ans = float(the_mod) / float(denominator);
980
981	// We can't divide by more than 100 at a time.
982	const int MAX_DIV100_ITERS = int(NUM_POWERS_OF_10_TO_KEEP + POW10_PER_COMPONENT) / 2;
983
984	for (int iter = 0; iter < MAX_DIV100_ITERS; iter++)
985	{
986		if (power_of_ten > -2.0)
987		{
988			break;
989		}
990
991		numerator = Div(numerator, 100.0, the_mod);
992
993		ans += float(the_mod);
994		ans *= 0.01;
995
996		power_of_ten += 2.0;
997	}
998
999	// And one more if required.
1000	if (power_of_ten == -1.0)
1001	{
1002		numerator = Div(numerator, 10.0, the_mod);
1003
1004		ans += float(the_mod);
1005		ans *= 0.1;
1006	}
1007
1008	return ans - floor(ans);
1009}
1010
1011// Im((2i - 1)^n) / (10^n n)
1012float GetNthDigitOfSpiral2(float nth_digit)
1013{
1014    int		num_terms	= int(8.0 + (MAX_2OVER11_TERMS - 8.0) * nth_digit / (MAX_DIGIT-1.0));
1015	float	shift		= 0.0;
1016	float	sum			= 0.0;
1017
1018	vec4 re = vec4(1.0, 0.0, 0.0, 0.0); bool re_neg = true;
1019	vec4 im = vec4(2.0, 0.0, 0.0, 0.0); bool im_neg = false;
1020
1021	for (int term = 1; term < int(MAX_2OVER11_TERMS); term++)
1022	{
1023		float shifted_digit = nth_digit - float(term) + shift;
1024
1025		float f = GetFractionalPart(im, float(term), shifted_digit);
1026
1027		if (im_neg)
1028		{
1029			sum -= f;
1030		}
1031		else
1032		{
1033			sum += f;
1034		}
1035
1036		if (im.w * 2.0 > BASE_FOR_NUMBER ||
1037			re.w * 2.0 > BASE_FOR_NUMBER)
1038		{
1039			float mod;
1040
1041			im = Div(im,10.0,mod);
1042			re = Div(re,10.0,mod);
1043
1044			shift += 1.0;
1045		}
1046
1047		bool new_re_neg;
1048		bool new_im_neg;
1049
1050		vec4 new_re = Add(Double(im), !im_neg, re, !re_neg, new_re_neg);
1051		vec4 new_im = Add(Double(re),  re_neg, im, !im_neg, new_im_neg);
1052
1053		re = new_re; re_neg = new_re_neg;
1054		im = new_im; im_neg = new_im_neg;
1055
1056		if (term == num_terms)
1057		{
1058			break;
1059		}
1060	}
1061
1062	sum = sum - floor(sum);
1063
1064	return sum;
1065}
1066
1067// Im((3i + 1)^n) / (10^n n)
1068float GetNthDigitOfSpiral3(float nth_digit)
1069{
1070	int		num_terms	= int(8.0 + (MAX_1OVER3_TERMS - 8.0) * nth_digit / (MAX_DIGIT-1.0));
1071	float	shift		= 0.0;
1072	float	sum			= 0.0;
1073
1074	vec4 re = vec4(1.0, 0.0, 0.0, 0.0); bool re_neg = false;
1075	vec4 im = vec4(3.0, 0.0, 0.0, 0.0); bool im_neg = false;
1076
1077	for (int term = 1; term < int(MAX_1OVER3_TERMS); term++)
1078	{
1079		float shifted_digit = nth_digit - float(term) + shift;
1080
1081		float f = GetFractionalPart(im, float(term), shifted_digit);
1082
1083		if (im_neg)
1084		{
1085			sum -= f;
1086		}
1087		else
1088		{
1089			sum += f;
1090		}
1091
1092		if (im.w * 3.0 > BASE_FOR_NUMBER ||
1093			re.w * 3.0 > BASE_FOR_NUMBER)
1094		{
1095			float mod;
1096
1097			im = Div(im,10.0,mod);
1098			re = Div(re,10.0,mod);
1099
1100			shift += 1.0;
1101		}
1102
1103		bool new_re_neg;
1104		bool new_im_neg;
1105
1106		vec4 new_re = Add(Treble(im), !im_neg, re, re_neg, new_re_neg);
1107		vec4 new_im = Add(Treble(re),  re_neg, im, im_neg, new_im_neg);
1108
1109		re = new_re; re_neg = new_re_neg;
1110		im = new_im; im_neg = new_im_neg;
1111
1112		if (term == num_terms)
1113		{
1114			break;
1115		}
1116	}
1117
1118	sum = sum - floor(sum);
1119
1120	return sum;
1121}
1122
1123int GetNthDigitOfPi(float nth_digit)
1124{
1125	float a = GetNthDigitOfSpiral3(nth_digit);
1126	float b = GetNthDigitOfSpiral2(nth_digit);
1127
1128	float s = 4.0 * (a*3.0-b);
1129
1130	s -= floor(s);
1131
1132	int digit = int(floor(10.0 * s));
1133
1134	return digit;
1135}
1136
1137
1138#endif
1139
1140
1141
1142
1143// ============================================================================================
1144
1145bool Is0To1(float x)
1146{
1147	return x >= 0.0 && x < 1.0;
1148}
1149
1150bool Is0To1(vec2 uv)
1151{
1152	return
1153		uv.x >= 0.0 && uv.x < 1.0 &&
1154		uv.y >= 0.0 && uv.y < 1.0;
1155}
1156
1157// ============================================================================================
1158
1159float GetPositionAlongLineSegmentNearestToPoint(vec2 line_segment_a, vec2 line_segment_b, vec2 point)
1160{
1161	vec2 p = point			- line_segment_a;
1162	vec2 l = line_segment_b	- line_segment_a;
1163
1164	float dprod = dot(p,l);
1165	float len2  = dot(l,l);
1166
1167	return clamp(dprod / len2, 0.0, 1.0);
1168}
1169
1170float GetPositionAlongLineNearestToPoint(vec2 line_segment_a, vec2 line_segment_b, vec2 point)
1171{
1172	vec2 p = point			- line_segment_a;
1173	vec2 l = line_segment_b	- line_segment_a;
1174
1175	float dprod = dot(p,l);
1176	float len2  = dot(l,l);
1177
1178	return dprod / len2;
1179}
1180
1181float GetDistSqFromLineSegmentToPoint(vec2 line_segment_a, vec2 line_segment_b, vec2 point)
1182{
1183	float t = GetPositionAlongLineSegmentNearestToPoint(line_segment_a, line_segment_b, point);
1184
1185	vec2 to_nearest = point - mix(line_segment_a, line_segment_b, t);
1186
1187	return dot(to_nearest, to_nearest);
1188}
1189
1190vec3 GetPointOnCubicSpline(vec3 cp0, vec3 cp1, vec3 cp2, vec3 cp3, float t)
1191{
1192	float p = t;
1193	float n = 1.0 - t;
1194
1195	vec3 ans;
1196
1197	ans  = cp0 * (n*n*n);
1198	ans += cp1 * (n*n*p*3.0);
1199	ans += cp2 * (n*p*p*3.0);
1200	ans += cp3 * (p*p*p);
1201
1202    return ans;
1203}
1204
1205vec2 GetPointOnCubicSpline(vec2 cp0, vec2 cp1, vec2 cp2, vec2 cp3, float t)
1206{
1207	float p = t;
1208	float n = 1.0 - t;
1209
1210	vec2 ans;
1211
1212	ans  = cp0 * (n*n*n);
1213	ans += cp1 * (n*n*p*3.0);
1214	ans += cp2 * (n*p*p*3.0);
1215	ans += cp3 * (p*p*p);
1216
1217    return ans;
1218}
1219
1220float GetNearestPointAlongCubicSpline(vec2 cp0, vec2 cp1, vec2 cp2, vec2 cp3, vec2 uv)
1221{
1222	float t = GetPositionAlongLineSegmentNearestToPoint(cp0,cp3, uv);
1223
1224	// Now refine once.
1225	{
1226		float t0 = max(0.0, t - 0.1);
1227		float t1 = min(1.0, t + 0.1);
1228
1229		vec2 p0 = GetPointOnCubicSpline(cp0,cp1,cp2,cp3, t0);
1230		vec2 p1 = GetPointOnCubicSpline(cp0,cp1,cp2,cp3, t1);
1231
1232		t = clamp(mix(t0,t1,GetPositionAlongLineNearestToPoint(p0,p1, uv)), 0.0, 1.0);
1233	}
1234
1235	return t;
1236}
1237
1238// ============================================================================================
1239
1240float GetColourForLineSegment(vec3 cp0, vec3 cp1, vec2 uv, float pixel_uv_size)
1241{
1242	float t = GetPositionAlongLineSegmentNearestToPoint(cp0.xy,cp1.xy, uv);
1243
1244	vec2	best_point		= mix(cp0.xy,cp1.xy,t);
1245	float	best_thickness	= mix(cp0.z, cp1.z, t*t);	// Non-linear for thickness...
1246    float	best_dist 		= length(uv - best_point.xy);
1247    float	best_surface	= best_thickness - best_dist;
1248
1249    float aa = smoothstep(0.0, pixel_uv_size, best_surface);
1250
1251    return aa;
1252}
1253
1254float GetColourForCubicSpline(vec3 cp0, vec3 cp1, vec3 cp2, vec3 cp3, vec2 uv, float pixel_uv_size)
1255{
1256	float	t				= GetNearestPointAlongCubicSpline(cp0.xy,cp1.xy,cp2.xy,cp3.xy, uv);
1257    vec3	best_point		= GetPointOnCubicSpline(cp0,cp1,cp2,cp3, t);
1258    float	best_dist 		= length(uv - best_point.xy);
1259    float	best_surface	= best_point.z - best_dist;
1260
1261    float aa = smoothstep(0.0, pixel_uv_size, best_surface);
1262
1263    return aa;
1264}
1265
1266float GetColourForEllispse(vec2 origin, vec2 scale, float thickness, vec2 uv, float pixel_uv_size)
1267{
1268    vec2 r = (uv - origin) / scale;
1269    vec2 n = normalize(r) * scale + origin;
1270
1271    // Stylish!
1272    thickness *= 1.0+r.x*r.y*0.5;
1273
1274    float d = distance(n, uv);
1275    float s = thickness - d;
1276
1277    float aa = smoothstep(0.0, pixel_uv_size, s);
1278
1279    return aa;
1280}
1281
1282// ============================================================================================
1283// THE FONT
1284
1285// Control over the aspect ratio.
1286#define CHAR_ASPECT 0.75
1287#define CHAR_HEIGHT 0.95
1288#define AR(x) (((x)-0.5)*CHAR_ASPECT+0.5)
1289#define H(y) ((y)*CHAR_HEIGHT)
1290
1291bool IsDigitUV(vec2 uv)
1292{
1293	return uv.x > AR(0.0) && uv.x < AR(1.0) && uv.y > 0.01 * CHAR_HEIGHT && uv.y < 0.99 * CHAR_HEIGHT;
1294}
1295
1296float Char0(vec2 uv, float pixel_uv_size)
1297{
1298    return GetColourForEllispse(vec2(0.5,0.5*CHAR_HEIGHT),vec2(0.35 * CHAR_ASPECT,0.35),0.09,uv,pixel_uv_size);
1299}
1300
1301float Char1(vec2 uv, float pixel_uv_size)
1302{
1303	float s0 = GetColourForLineSegment(vec3(0.52,0.9*CHAR_HEIGHT,0.09),vec3(0.50,0.15*CHAR_HEIGHT,0.13), uv, pixel_uv_size);
1304    float s1 = GetColourForLineSegment(vec3(0.52,0.9*CHAR_HEIGHT,0.09),vec3(0.30,0.75*CHAR_HEIGHT,0.06), uv, pixel_uv_size);
1305
1306    return max(s0,s1);
1307}
1308
1309float Char2(vec2 uv, float pixel_uv_size)
1310{
1311    float s0 = GetColourForCubicSpline(vec3(AR(0.4),H(0.80),0.16), vec3(AR(0.8),H(0.6),0.06), vec3(AR(0.6),H(0.4),0.03), vec3(AR(0.2),H(0.15),0.13), uv, pixel_uv_size);
1312    float s1 = GetColourForCubicSpline(vec3(AR(0.2),H(0.15),0.13), vec3(AR(0.4),H(0.3),0.08), vec3(AR(0.6),H(0.2),0.08), vec3(AR(0.8),H(0.20),0.16), uv, pixel_uv_size);
1313
1314    return max(s0,s1);
1315}
1316
1317float Char3(vec2 uv, float pixel_uv_size)
1318{
1319	float s0 = GetColourForCubicSpline(vec3(AR(0.5),H(0.6),0.09), vec3(AR(0.7),H(0.7),0.08), vec3(AR(0.6),H(0.9),0.07), vec3(AR(0.4),H(0.87),0.11), uv, pixel_uv_size);
1320    float s1 = GetColourForCubicSpline(vec3(AR(0.5),H(0.6),0.09), vec3(AR(1.0),H(0.4),0.05), vec3(AR(0.5),H(0.1),0.06), vec3(AR(0.2),H(0.20),0.16), uv, pixel_uv_size);
1321
1322    return max(s0,s1);
1323}
1324
1325float Char4(vec2 uv, float pixel_uv_size)
1326{
1327	float s0 = GetColourForCubicSpline(vec3(AR(0.15),H(0.4),0.11), vec3(AR(0.3),H(0.5),0.06), vec3(AR(0.3),H(0.7),0.06), vec3(AR(0.2),H(0.80),0.11), uv, pixel_uv_size);
1328    float s1 = GetColourForCubicSpline(vec3(AR(0.18),H(0.4),0.10), vec3(AR(0.4),H(0.5),0.06), vec3(AR(0.6),H(0.3),0.06), vec3(AR(0.8),H(0.40),0.13), uv, pixel_uv_size);
1329    float s2 = GetColourForCubicSpline(vec3(AR(0.60),H(0.6),0.09), vec3(AR(0.5),H(0.4),0.06), vec3(AR(0.6),H(0.3),0.06), vec3(AR(0.5),H(0.15),0.09), uv, pixel_uv_size);
1330
1331    return max(s0,max(s1,s2));
1332}
1333
1334float Char5(vec2 uv, float pixel_uv_size)
1335{
1336    float s0 = GetColourForCubicSpline(vec3(AR(0.3),H(0.80),0.10), vec3(AR(0.5),H(0.70),0.07), vec3(AR(0.6),H(0.80),0.07), vec3(AR(0.8),H(0.80),0.11), uv, pixel_uv_size);
1337    float s2 = GetColourForCubicSpline(vec3(AR(0.3),H(0.55),0.09), vec3(AR(0.8),H(0.55),0.03), vec3(AR(0.9),H(0.25),0.09), vec3(AR(0.5),H(0.15),0.11), uv, pixel_uv_size);
1338
1339    float s1 = GetColourForLineSegment(vec3(AR(0.3),H(0.55),0.09), vec3(AR(0.3),H(0.8),0.10), uv, pixel_uv_size);
1340
1341	return max(s0,max(s1,s2));
1342}
1343
1344float Char6(vec2 uv, float pixel_uv_size)
1345{
1346	float s0 = GetColourForEllispse(vec2(0.5,0.35*CHAR_HEIGHT),vec2(0.3*CHAR_ASPECT,0.25*CHAR_HEIGHT),0.08,uv,pixel_uv_size);
1347    float s1 = GetColourForCubicSpline(vec3(AR(0.2),H(0.23),0.08), vec3(AR(0.1),H(0.6),0.09), vec3(AR(0.2),H(0.9),0.02), vec3(AR(0.6),H(0.85),0.13), uv, pixel_uv_size);
1348
1349    return max(s0,s1);
1350}
1351
1352float Char7(vec2 uv, float pixel_uv_size)
1353{
1354    float s0 = GetColourForCubicSpline(vec3(AR(0.2),H(0.8),0.11), vec3(AR(0.4),H(0.7),0.06), vec3(AR(0.6),H(0.8),0.06), vec3(AR(0.8),H(0.8),0.13), uv, pixel_uv_size);
1355    float s1 = GetColourForCubicSpline(vec3(AR(0.8),H(0.8),0.13), vec3(AR(0.5),H(0.6),0.02), vec3(AR(0.4),H(0.4),0.06), vec3(AR(0.5),H(0.2),0.16), uv, pixel_uv_size);
1356
1357    return max(s0,s1);
1358}
1359
1360float Char8(vec2 uv, float pixel_uv_size)
1361{
1362	float s0 = GetColourForEllispse(vec2(0.5,0.35*CHAR_HEIGHT),vec2(0.3 * CHAR_ASPECT,0.25*CHAR_HEIGHT),0.08,uv,pixel_uv_size);
1363	float s1 = GetColourForEllispse(vec2(0.5,0.76*CHAR_HEIGHT),vec2(0.2 * CHAR_ASPECT,0.14*CHAR_HEIGHT),0.07,uv,pixel_uv_size);
1364
1365    return max(s0,s1);
1366}
1367
1368float Char9(vec2 uv, float pixel_uv_size)
1369{
1370	return Char6(vec2(1.0-uv.x, CHAR_HEIGHT-uv.y),pixel_uv_size);
1371}
1372
1373#undef AR
1374#undef H
1375
1376
1377float CharDot(vec2 uv, float pixel_uv_size)
1378{
1379    float thickness = 0.1;
1380
1381    float d = distance(uv, vec2(0.8,0.2));
1382    float s = thickness - d;
1383
1384    float aa = smoothstep(0.0, pixel_uv_size, s);
1385
1386    return aa;
1387}
1388
1389float CharPi(vec2 uv, float pixel_uv_size)
1390{
1391    float s0 = GetColourForCubicSpline(vec3(0.20,0.85,0.13), vec3(0.4,0.8,0.10), vec3(0.70,0.8,0.075), vec3(0.8,0.9,0.09), uv, pixel_uv_size);
1392    float s1 = GetColourForCubicSpline(vec3(0.35,0.80,0.10), vec3(0.4,0.6,0.08), vec3(0.40,0.3,0.010), vec3(0.2,0.2,0.08), uv, pixel_uv_size);
1393    float s2 = GetColourForCubicSpline(vec3(0.70,0.80,0.08), vec3(0.7,0.7,0.08), vec3(0.73,0.4,0.050), vec3(0.9,0.2,0.04), uv, pixel_uv_size);
1394
1395    return (min(s0+s1+s2,1.0) + max(s0,max(s1,s2))) * 0.5;
1396}
1397
1398float CharDigit(int digit, vec2 uv, float pixel_uv_size)
1399{
1400	if (digit < 5)
1401	{
1402		if (digit == 0) {return Char0(uv,pixel_uv_size);}
1403		if (digit == 1) {return Char1(uv,pixel_uv_size);}
1404		if (digit == 2) {return Char2(uv,pixel_uv_size);}
1405		if (digit == 3) {return Char3(uv,pixel_uv_size);}
1406
1407		return Char4(uv,pixel_uv_size);
1408	}
1409	else
1410	{
1411		if (digit == 5) {return Char5(uv,pixel_uv_size);}
1412		if (digit == 6) {return Char6(uv,pixel_uv_size);}
1413		if (digit == 7) {return Char7(uv,pixel_uv_size);}
1414		if (digit == 8) {return Char8(uv,pixel_uv_size);}
1415
1416		return Char9(uv,pixel_uv_size);
1417	}
1418}
1419
1420// ============================================================================================
1421// GRAPHICS PRIMS
1422
1423vec2 ApplyWarp(vec2 uv, float seed)
1424{
1425	uv += cos(4.0 * uv.x - seed*67.0) * vec2(0.007, 0.009);
1426	uv += sin(5.0 * uv.y - seed*89.0) * vec2(0.009,-0.008);
1427
1428	return uv;
1429}
1430
1431vec2 ApplyBulge(vec2 uv)
1432{
1433    //return uv - normalize(uv - vec2(0,5)) * smoothstep(3.0, 12.0, distance(uv, vec2(0,7))) + sin( cos(uv.x * uv.x * 0.05) + iGlobalTime) * 0.04;
1434    return uv + normalize(uv - vec2(0,20.0)) * 2.0 + sin( cos(uv.x * uv.x * 0.06) + iGlobalTime) * 0.05;
1435}
1436
1437// box_dx/dy normalised please. Returns colour/alpha.
1438vec2 Box(vec2 box_origin, vec2 box_dx, vec2 box_dy, vec2 box_dims, float thickness, float roundness, vec2 uv, float pixel_uv_size)
1439{
1440	vec2 r = uv - box_origin;
1441
1442	{
1443		float x = dot(r, box_dx);
1444		float y = dot(r, box_dy);
1445
1446		r = ApplyWarp(vec2(x,y), 1.2);
1447	}
1448
1449	vec2 r_in_box = clamp(r, vec2(0.0,0.0), box_dims);
1450
1451	float dist_from_box = distance(r, r_in_box);
1452	float surface		= dist_from_box - thickness;
1453
1454	float	colour	= smoothstep(pixel_uv_size, 0.0, surface);
1455	float	alpha	= colour;
1456
1457    if (r == r_in_box)
1458    {
1459		vec2 box_half_size	= box_dims * 0.5;
1460		vec2 from_mid		= r - box_half_size;
1461		vec2 to_corner		= sign(from_mid);
1462		vec2 nearest_corner = box_half_size + to_corner * box_half_size;
1463		vec2 corner_sphere	= nearest_corner - to_corner * roundness;
1464		vec2 to_edge		= abs(box_half_size) - abs(from_mid);
1465
1466		float dist_from_edge = min(to_edge.x, to_edge.y);
1467
1468		colour = smoothstep(pixel_uv_size, 0.0, dist_from_edge);
1469
1470		if (sign(r - corner_sphere) == to_corner)
1471		{
1472			float dist_from_corner = roundness - distance(corner_sphere, r);
1473
1474			colour = max(colour, smoothstep(pixel_uv_size, 0.0, dist_from_corner));
1475		}
1476    }
1477
1478	return vec2(colour, alpha);
1479}
1480
1481vec2 Circle(vec2 circle_origin, float circle_radius, float thickness, vec2 uv, float pixel_uv_size)
1482{
1483	float d = distance(uv, circle_origin);
1484
1485	float surface	= abs(d - circle_radius) - thickness * 0.5;
1486	float colour	= smoothstep(pixel_uv_size, 0.0, surface);
1487	float alpha		= (d < circle_radius) ? 1.0 : colour;
1488
1489	return vec2(colour, alpha);
1490}
1491
1492// Circle with a sprial in the middle!
1493vec2 Wheel(vec2 wheel_origin, float wheel_radius, float wheel_angle, float thickness, vec2 uv, float pixel_uv_size)
1494{
1495	vec2 r = uv - wheel_origin;
1496
1497	// We need wheel space, w.
1498	float sa = sin(wheel_angle);
1499	float ca = cos(wheel_angle);
1500
1501	vec2 w = vec2(
1502				dot(r, vec2( sa,ca)),
1503				dot(r, vec2(-ca,sa)));
1504
1505	// Wonky please!
1506	w = ApplyWarp(w, 0.11);
1507
1508	float d = length(w);
1509
1510	float surface	= abs(d - wheel_radius) - thickness * 0.5;
1511	float colour	= smoothstep(pixel_uv_size, 0.0, surface);
1512	float alpha		= (d < wheel_radius) ? 1.0 : colour;
1513
1514	if (d < wheel_radius)
1515	{
1516        if (d < thickness * 2.0)
1517        {
1518            colour = smoothstep(pixel_uv_size, 0.0, d - thickness * 1.0);
1519        }
1520
1521		// So the point on the spiral, s.
1522		float a = atan(w.y,w.x);
1523		float d = a + 3.1;
1524		d *= d;
1525		d *= 0.027;
1526		d *= wheel_radius;
1527
1528		vec2 s = normalize(w) * d;
1529
1530		float spiral_surface	= distance(s, w) - thickness * 0.5;
1531		float spiral_colour		= smoothstep(pixel_uv_size, 0.0, spiral_surface);
1532
1533		colour = max(colour, spiral_colour);
1534	}
1535
1536	return vec2(colour, alpha);
1537}
1538
1539// chimney_dims = width bot, height, width top.
1540vec2 Chimney(vec2 chimney_origin, vec2 chimney_dx, vec2 chimney_dy, vec3 chimney_dims, float anim, float thickness, vec2 uv, float pixel_uv_size)
1541{
1542	vec2 r = vec2(
1543				dot(uv - chimney_origin, chimney_dx),
1544				dot(uv - chimney_origin, chimney_dy));
1545
1546	if (r.y < 0.0 || r.y > chimney_dims.y + thickness)
1547	{
1548		return vec2(0.0, 0.0);
1549	}
1550
1551	r.x = abs(r.x);
1552
1553	if (r.x > chimney_dims.x + thickness + chimney_dims.y * 0.4)
1554	{
1555		return vec2(0.0, 0.0);
1556	}
1557
1558    float b = 1.0 - anim;
1559	float q = 1.0-b*b*b*b;
1560	float a = 2.0*(1.0-q)*q;
1561
1562    float down	= 1.0 - a * 0.5;
1563    float push	= a;
1564
1565    chimney_dims.z *= 1.0 + a * 0.2;
1566
1567	vec2 cp0 = vec2(chimney_dims.x, 0.0);
1568	vec2 cp1 = vec2(chimney_dims.x - chimney_dims.y * 0.33 * push, chimney_dims.y * 0.33);
1569	vec2 cp2 = vec2(chimney_dims.z, chimney_dims.y * 0.66 * down);
1570	vec2 cp3 = vec2(chimney_dims.z, chimney_dims.y * down);
1571
1572	float t = GetNearestPointAlongCubicSpline(cp0,cp1,cp2,cp3, r);
1573
1574	vec2 pt = GetPointOnCubicSpline(cp0,cp1,cp2,cp3, t);
1575
1576	float dist_from_edge	= distance(pt, r);
1577	float surface			= dist_from_edge  - thickness * 0.5;
1578	float colour			= smoothstep(pixel_uv_size, 0.0, surface);
1579	float alpha				= (r.x < pt.x) ? 1.0 : colour;
1580
1581	// Top?
1582    float bend = 1.0 - a * 0.3;
1583
1584	//if (alpha > 0.0 && r.y > chimney_dims.y * bend - thickness)
1585	{
1586		cp0 = vec2(chimney_dims.z * -1.0, chimney_dims.y * down);
1587		cp1 = vec2(chimney_dims.z * -0.3, chimney_dims.y * down * bend);
1588		cp2 = vec2(chimney_dims.z * +0.3, chimney_dims.y * down * bend);
1589		cp3 = vec2(chimney_dims.z * +1.0, chimney_dims.y * down);
1590
1591		t = GetNearestPointAlongCubicSpline(cp0,cp1,cp2,cp3, r);
1592
1593		pt = GetPointOnCubicSpline(cp0,cp1,cp2,cp3, t);
1594
1595		dist_from_edge	= distance(pt, r);
1596		surface			= dist_from_edge  - thickness * 0.5;
1597		colour			= max(colour, smoothstep(pixel_uv_size, 0.0, surface));
1598		alpha			= (r.y > pt.y) ? colour : alpha;
1599	}
1600
1601	return vec2(colour, alpha);
1602}
1603
1604float Puff(float puff, float seed, vec2 uv, float pixel_uv_size)
1605{
1606	if (!Is0To1(uv))
1607    {
1608        return 0.0;
1609    }
1610
1611    const float GRID_SIZE = 4.0;
1612
1613	vec2 grid		= uv * vec2(GRID_SIZE, GRID_SIZE);
1614	vec2 gsquare	= floor(grid);
1615
1616	seed *= gsquare.x + 7.8;
1617	seed *= gsquare.y + 9.8;
1618	seed  = sin(seed);
1619
1620	float	random_colour	= mix( 1.0, 2.0, fract(seed * 15.67));
1621	float	random_time		= mix(-0.3, 0.3, fract(seed * 27.89));
1622	vec2	random_middle	= vec2(
1623								mix(0.35, 0.65, fract(seed * 37.22)),
1624								mix(0.35, 0.65, fract(seed * 47.89)));
1625
1626	float dist_from_middle_of_puff		= distance(gsquare + vec2(0.5,0.5), vec2(GRID_SIZE,GRID_SIZE) * 0.5);
1627	float dist_from_middle_of_square	= distance(grid, gsquare + random_middle);
1628
1629	float shape			= max(0.0, 1.25 - dist_from_middle_of_puff / (GRID_SIZE*0.5-0.5));
1630	float start_time	= mix(0.5, 0.0, dist_from_middle_of_puff / (GRID_SIZE * 0.5));
1631	float s				= clamp(puff + start_time + random_time, 0.0, 1.0);
1632	float fade			= random_colour * shape * smoothstep(1.0,0.9,s);
1633	float size			= (1.0-s)*s * 1.2;
1634
1635	float colour = smoothstep(pixel_uv_size * GRID_SIZE, 0.0, abs(dist_from_middle_of_square - size)-0.01);
1636
1637	return colour * fade;
1638}
1639
1640float PrintNumber(float n, vec2 digit_uv, vec2 uv, float pixel_uv_size)
1641{
1642	vec2 rel_uv = uv - digit_uv;
1643
1644	if (rel_uv.y < 0.0 || rel_uv.y > 1.0)
1645	{
1646		return 0.0;
1647	}
1648
1649	float digit_place = floor(rel_uv.x - 3.0);
1650
1651	if (digit_place >= 1.0 || digit_place <= -3.0)
1652	{
1653		return 0.0;
1654	}
1655
1656	int digit = int(10.0 * fract(n * pow(10.0, digit_place)));
1657
1658	return CharDigit(digit, fract(rel_uv), pixel_uv_size);
1659}
1660
1661
1662// ============================================================================================
1663
1664#define SPEED_SCALE 3.0
1665
1666float GetTrainXAtTime(float time)
1667{
1668    if (time < 2.0)
1669    {
1670        return SPEED_SCALE * (time*time / 4.0 - 3.5);
1671    }
1672
1673	return SPEED_SCALE * (time - 4.5);
1674}
1675
1676vec2 GetCameraPosAtTime(float time)
1677{
1678    if (time < 12.0)
1679    {
1680        return vec2(time*time / 24.0, 0.0) * SPEED_SCALE;
1681    }
1682
1683	float t = time - 12.0;
1684
1685	if (t > 60.0)
1686	{
1687		return vec2(61.0, 0.0) * SPEED_SCALE;
1688	}
1689
1690	if (t > 50.0)
1691	{
1692		float x = (t - 50.0) / 20.0;
1693
1694		return vec2(56.0 + 20.0*x - 20.0*x*x, 0.0) * SPEED_SCALE;
1695	}
1696
1697	return vec2(6.0+t, 0.0) * SPEED_SCALE;
1698}
1699
1700float GetTrackHeight(float world_x)
1701{
1702	return 2.5 + sin(world_x * 0.29) + sin(world_x * 0.47) * 0.43;
1703}
1704
1705void GetTrainWheelPositions(float time, out vec2 out_wheel_0, out vec2 out_wheel_1)
1706{
1707	const float TRAIN_WHEELBASE = 3.0;
1708
1709	float train_x0 = GetTrainXAtTime(time);
1710	float train_x1 = train_x0 + TRAIN_WHEELBASE;
1711
1712	vec2 train_wheel_0 = vec2(train_x0, GetTrackHeight(train_x0));
1713	vec2 train_wheel_1 = vec2(train_x1, GetTrackHeight(train_x1));
1714
1715	train_wheel_1	= train_wheel_0 + normalize(train_wheel_1 - train_wheel_0) * TRAIN_WHEELBASE;
1716	train_wheel_1.y	= GetTrackHeight(train_wheel_1.x);
1717	train_wheel_1	= train_wheel_0 + normalize(train_wheel_1 - train_wheel_0) * TRAIN_WHEELBASE;
1718	train_wheel_1.y	= GetTrackHeight(train_wheel_1.x);
1719
1720	out_wheel_0 = train_wheel_0;
1721	out_wheel_1 = train_wheel_1;
1722}
1723
1724void GetTrainChimneyPosition(float time, out vec2 out_chimney_top, out vec2 out_chimney_dir)
1725{
1726	vec2 wheel_0;
1727	vec2 wheel_1;
1728
1729	GetTrainWheelPositions(time, wheel_0, wheel_1);
1730
1731	vec2 dx = normalize(wheel_1 - wheel_0);
1732	vec2 dy = vec2(-dx.y,dx.x);
1733
1734	out_chimney_top = wheel_0 + dx * 2.25 + dy * 3.8;
1735	out_chimney_dir = dy;
1736}
1737
1738// ============================================================================================
1739
1740#define SCREEN_HEIGHT 15.0
1741
1742void mainImage( out vec4 fragColor, in vec2 fragCoord )
1743{
1744	// Our sequence takes one minute.
1745    float time = min(max(0.0, iGlobalTime - 2.0) * (1.0 / 60.0),1.0) * 80.0;
1746
1747	// Our screen_uv space has (0,0) at the middle, bottom of the screen and is SCREEN_HEIGHT high and +/- 12*aspect/2 wide.
1748	vec2	uv		= fragCoord.xy / iResolution.xy;
1749    float	aspect	= float(iResolution.x) / float(iResolution.y);
1750
1751	uv.x -= 0.5;
1752    uv.x *= aspect;
1753	uv   *= SCREEN_HEIGHT;
1754
1755	// Size of a pixel in uv space for antialiasing. Enlarge to get extra creamy AA!
1756	float pixel_uv_size = 2.0 * SCREEN_HEIGHT / iResolution.y;
1757
1758    // Accumulate final colour into here.
1759	vec2 final_colour = vec2(0.0, 0.0);
1760
1761    // Do this now so we can blur the screen edges.
1762    float vignette;
1763	{
1764        float fade_out = smoothstep(65.0, 79.0, time);
1765
1766        uv *= 1.0 - fade_out * 0.15;
1767        uv.y += fade_out * 4.0;
1768
1769        vec2 vignette_uv = abs(vec2(2.04,2.0) * (fragCoord.xy / iResolution.xy) - vec2(1.02,1.0));
1770        vignette_uv += fade_out * 0.5;
1771        vignette_uv *= vignette_uv;
1772        vignette_uv *= vignette_uv;
1773        vignette = mix(0.2 * (1.0 - fade_out), 1.0 - fade_out * fade_out * fade_out, 1.0 - vignette_uv.x - vignette_uv.y);
1774
1775        pixel_uv_size /= vignette + 0.1;
1776	}
1777
1778	// World space is in the same scale as uv space.
1779	vec2 camera_pos = GetCameraPosAtTime(time);
1780
1781	// The track first.
1782	vec2 world_pos = camera_pos + uv;
1783
1784	{
1785		float track_y		= GetTrackHeight(world_pos.x);
1786		float dist_to_track	= abs(track_y - world_pos.y - 0.1);
1787		float surface		= dist_to_track - 0.05;
1788		float colour		= smoothstep(pixel_uv_size, 0.0, surface);
1789
1790		final_colour.xy += colour;
1791	}
1792
1793	#if USE_INTEGERS
1794	const float	DIGITS_PER_LINE			= 15.0;
1795	const float	LINES_OF_PI				= (float(MAX_DIGIT) + DIGITS_PER_LINE - 1.0) / DIGITS_PER_LINE;
1796	#else
1797	const float	DIGITS_PER_LINE			= 10.0;
1798	const float	LINES_OF_PI				= ceil((MAX_DIGIT + DIGITS_PER_LINE - 1.0) / DIGITS_PER_LINE);
1799	#endif
1800
1801	const int	NUM_DIGITS_IN_FLIGHT	= 4;
1802	const float	TIME_FOR_FLIGHT			= 4.0;
1803	const float TIME_PER_PUFF			= TIME_FOR_FLIGHT / float(NUM_DIGITS_IN_FLIGHT);
1804	const vec2	DEST_DIGIT_UV			= vec2(DIGITS_PER_LINE * -0.5, SCREEN_HEIGHT * 0.65);
1805	const float	KERNING					= 0.9;
1806	const float	TIME_TO_START_PUFFING	= 3.0;
1807
1808
1809    float train_x = GetTrainXAtTime(time);
1810
1811    if (world_pos.x > train_x - 3.0 && world_pos.x < train_x + 5.0 && world_pos.y > SCREEN_HEIGHT * 0.05 && world_pos.y < SCREEN_HEIGHT * 0.6)
1812    {
1813        // Where is the train now?
1814        vec2 train_wheel_0;
1815        vec2 train_wheel_1;
1816
1817        GetTrainWheelPositions(time, train_wheel_0, train_wheel_1);
1818
1819        vec2 train_dx = normalize(train_wheel_1 - train_wheel_0);
1820        vec2 train_dy = vec2(-train_dx.y,train_dx.x);
1821
1822        // Big wheel.
1823        {
1824            float	wheel_angle		= time * 3.0;
1825            float	wheel_wonky		= sin(wheel_angle) * 0.05;
1826
1827            train_wheel_0 += train_dy * wheel_wonky;
1828
1829            vec2	wheel_uv		= train_wheel_0 + train_dy * 1.0;
1830            float	wheel_radius	= 1.0;
1831
1832            vec2 wheel_colour = Wheel(wheel_uv, wheel_radius, wheel_angle, 0.1, world_pos, pixel_uv_size);
1833
1834            final_colour += wheel_colour * (1.0 - final_colour.y);
1835        }
1836
1837        // Little wheel.
1838        {
1839            float	wheel_angle		= time * 5.1;
1840            float	wheel_wonky		= sin(wheel_angle) * 0.04;
1841
1842            train_wheel_1 += train_dy * wheel_wonky;
1843
1844            vec2	wheel_uv		= train_wheel_1 + train_dy * 0.6;
1845            float	wheel_radius	= 0.6;
1846
1847            vec2 wheel_colour = Wheel(wheel_uv, wheel_radius, wheel_angle, 0.1, world_pos, pixel_uv_size);
1848
1849            final_colour += wheel_colour * (1.0 - final_colour.y);
1850        }
1851
1852        // Recalculate train basis due to wonky wheels!
1853        train_dx = normalize(train_wheel_1 - train_wheel_0);
1854        train_dy = vec2(-train_dx.y,train_dx.x);
1855
1856        // ------------------------------------------------------------------------
1857        // Train
1858
1859        {
1860            vec2 box_uv		= train_wheel_0 - train_dx * 0.2 + train_dy * 0.6;
1861            vec2 box_dims	= vec2(3.0, 2.0);
1862            vec2 box_colour	= Box(box_uv, train_dx, train_dy, box_dims, 0.1, 0.1, world_pos, pixel_uv_size);
1863
1864            final_colour += box_colour * (1.0 - final_colour.y);
1865
1866            {
1867                vec2 cab_uv		= box_uv + train_dx * 0.2 + train_dy * 2.1;
1868                vec2 cab_dx		= train_dx;
1869                vec2 cab_dy		= train_dy;
1870                vec2 cab_dims	= vec2(1.0, 1.6);
1871
1872                vec2 cab_colour	= Box(cab_uv, cab_dx, cab_dy, cab_dims, 0.1, 0.1, world_pos, pixel_uv_size);
1873
1874                if (dot(uv - cab_uv, cab_dx) < 1.0)
1875                {
1876                    vec2 window_uv = cab_uv + cab_dx * 0.9 + cab_dy * 0.9;
1877                    vec2 window_colour	= Circle(window_uv, 0.4, 0.1, world_pos, pixel_uv_size);
1878
1879                    cab_colour.x += window_colour.x * cab_colour.y;
1880                }
1881
1882                final_colour += cab_colour * (1.0 - final_colour.y);
1883            }
1884
1885            {
1886                vec2	chimney_uv		= box_uv + train_dx * box_dims.x * 0.8 + train_dy * box_dims.y;
1887                vec2	chimney_dx		= train_dx;
1888                vec2	chimney_dy		= train_dy;
1889                vec3	chimney_dims	= vec3(0.3, 1.1, 0.4);
1890                float	chimney_anim	= fract(time * TIME_PER_PUFF);
1891
1892                if (time < TIME_TO_START_PUFFING || time + TIME_PER_PUFF >= TIME_TO_START_PUFFING + float(MAX_DIGIT) * TIME_PER_PUFF)
1893                {
1894                    chimney_anim = 0.0;
1895                }
1896
1897                vec2	chimney_colour	= Chimney(chimney_uv, chimney_dx, chimney_dy, chimney_dims, chimney_anim, 0.1, world_pos, pixel_uv_size);
1898
1899                final_colour += chimney_colour * (1.0 - final_colour.y);
1900            }
1901
1902            {
1903                vec2 circle_uv		= box_uv + train_dx * 3.0 + train_dy * 1.1;
1904                vec2 circle_colour	= Circle(circle_uv, 0.6, 0.1, world_pos, pixel_uv_size);
1905
1906                final_colour += circle_colour * (1.0 - final_colour.y);
1907            }
1908        }
1909    }
1910
1911	// ------------------------------------------------------------------------
1912	// The digits we've already found.
1913
1914    if (time < 79.0)
1915	{
1916
1917        vec2 found_uv 	= (ApplyBulge(uv) - DEST_DIGIT_UV) / vec2(KERNING,1.0);
1918		vec2 found_base	= floor(found_uv);
1919
1920		// Which digit of pi?
1921		float line		= -floor(found_base.y);
1922		float column	=  floor(found_base.x);
1923
1924		if (line >= 0.0 && column >= 0.0)
1925		{
1926			float nth_digit = float(MAX_DIGIT);	// MAX_DIGIT -> Invalid.
1927
1928			if (column == 0.0)
1929			{
1930				// First column only has the first digit.
1931				if (line == 0.0)
1932				{
1933					nth_digit = -1.0;
1934				}
1935			}
1936			else
1937            if (column <= DIGITS_PER_LINE)
1938			{
1939				nth_digit = line * DIGITS_PER_LINE + (column - 1.0);
1940			}
1941
1942			if (nth_digit < float(MAX_DIGIT))
1943			{
1944				// How many digits have arrived already?
1945				float num_arrived = floor(time - TIME_FOR_FLIGHT - TIME_TO_START_PUFFING);
1946
1947				if (nth_digit < num_arrived)
1948				{
1949					vec2 digit_uv = (found_uv - found_base) * vec2(KERNING,1.0) + vec2((1.0 - KERNING)*0.5, 0.0);
1950
1951					if (IsDigitUV(digit_uv))
1952					{
1953                        #if USE_INTEGERS
1954						int pi_digit = GetNthDigitOfPi(int(nth_digit));
1955                        #else
1956						int pi_digit = GetNthDigitOfPi(nth_digit);
1957                        #endif
1958						final_colour.x += CharDigit(pi_digit, digit_uv, pixel_uv_size);
1959                    }
1960
1961                    if (nth_digit == -1.0)
1962                    {
1963                        final_colour.x += CharDot(digit_uv, pixel_uv_size);
1964					}
1965				}
1966			}
1967		}
1968	}
1969
1970	// ------------------------------------------------------------------------
1971	// Digit puffs.
1972
1973    if (uv.x > SCREEN_HEIGHT * -0.7 && uv.y > SCREEN_HEIGHT * 0.3 && uv.y < SCREEN_HEIGHT * 0.9)
1974    {
1975        for (int i = 0; i < NUM_DIGITS_IN_FLIGHT; i++)
1976        {
1977            float offset_i	= float(i) * (TIME_FOR_FLIGHT / float(NUM_DIGITS_IN_FLIGHT));
1978            float puff_time	= (time - offset_i - TIME_TO_START_PUFFING) / TIME_FOR_FLIGHT;
1979
1980            if (puff_time < 0.0)
1981            {
1982                continue;
1983            }
1984
1985            float puff_digit = floor(puff_time) * float(NUM_DIGITS_IN_FLIGHT) + float(i) - 1.0;
1986
1987            if (puff_digit >= 60.0)	// Always puff even if no more digits... .to keep in sync with sound
1988            {
1989                continue;
1990            }
1991
1992            float	puff_start_time				= floor(puff_time) * TIME_FOR_FLIGHT + offset_i + TIME_TO_START_PUFFING;
1993            float	puff_t						= pow(fract(puff_time), 0.75);
1994            float	puff_size					= 1.0 + puff_t * 3.0;
1995            float	digit_size					= min(0.3 + puff_t, 1.0);
1996            vec2	camera_pos_at_start_time	= GetCameraPosAtTime(puff_start_time);
1997
1998            vec2 chimney_pos_at_start_time;
1999            vec2 chimney_dir_at_start_time;
2000
2001            GetTrainChimneyPosition(puff_start_time, chimney_pos_at_start_time, chimney_dir_at_start_time);
2002
2003            vec2 puff_cp0 = chimney_pos_at_start_time;
2004            vec2 puff_cp1 = chimney_pos_at_start_time + chimney_dir_at_start_time * 5.0;
2005            vec2 puff_cp2 = puff_cp1 + vec2(0.0, -1.0);
2006            vec2 puff_cp3 = puff_cp1 + vec2(0.0, -2.0);
2007
2008            vec2 puff_pos	= GetPointOnCubicSpline(puff_cp0, puff_cp1, puff_cp2, puff_cp3, puff_t);
2009            vec2 puff_uv	= (world_pos - puff_pos)/puff_size+0.5;
2010
2011            {
2012                final_colour.x += Puff(puff_t * 1.5, puff_start_time + 5.6, puff_uv - 0.05, pixel_uv_size / puff_size) * (1.0 - final_colour.y);
2013                final_colour.x += Puff(puff_t * 1.5, puff_start_time + 7.9, puff_uv + 0.05, pixel_uv_size / puff_size) * (1.0 - final_colour.y);
2014            }
2015
2016            if (puff_digit >= float(MAX_DIGIT))
2017            {
2018                continue;
2019            }
2020
2021            vec2 digit_dest_uv;
2022
2023            if (puff_digit == -1.0)
2024            {
2025                // This is the first digit of pi.
2026                digit_dest_uv = DEST_DIGIT_UV - (1.0 - KERNING) * 0.5;
2027            }
2028            else
2029            {
2030				#if USE_INTEGERS
2031                int line;
2032                int column;
2033                DivMod(int(puff_digit), int(DIGITS_PER_LINE), line, column);
2034				#else
2035                float line;
2036                float column;
2037                DivMod(puff_digit, DIGITS_PER_LINE, line, column);
2038				#endif
2039
2040                digit_dest_uv = DEST_DIGIT_UV + vec2((float(column) + 1.0) * KERNING - (1.0 - KERNING) * 0.5, -float(line));
2041            }
2042
2043            // The digit of pi are we puffing out of the train.
2044            vec2 digit_cp0 = chimney_pos_at_start_time;
2045            vec2 digit_cp1 = chimney_pos_at_start_time + chimney_dir_at_start_time * 5.0;
2046            vec2 digit_cp3 = digit_dest_uv + camera_pos;
2047            vec2 digit_cp2 = digit_cp3 + vec2(2.0, -8.0);
2048
2049            float	bulge_amount	= smoothstep(0.0, 0.9, puff_t);
2050            vec2	bulged_uv		= mix(uv, ApplyBulge(uv), bulge_amount);
2051
2052            vec2 digit_pos	= GetPointOnCubicSpline(digit_cp0, digit_cp1, digit_cp2, digit_cp3, puff_t);
2053            vec2 digit_uv	= (bulged_uv + camera_pos - digit_pos)/digit_size;
2054
2055            if (IsDigitUV(digit_uv))
2056            {
2057				#if USE_INTEGERS
2058                int pi_digit = GetNthDigitOfPi(int(puff_digit));
2059                #else
2060                int pi_digit = GetNthDigitOfPi(puff_digit);
2061                #endif
2062
2063                final_colour += CharDigit(pi_digit, digit_uv, pixel_uv_size) * (1.0 - final_colour.y);
2064            }
2065        }
2066    }
2067
2068	// Apply vignette
2069    final_colour.x = sqrt(clamp(final_colour.x,0.0,1.0)) * vignette;
2070
2071    if (time >= 75.0)
2072    {
2073        vec2 original_uv = fragCoord.xy / iResolution.xy;
2074        original_uv.x -= 0.5;
2075        original_uv.x *= aspect;
2076        original_uv   *= SCREEN_HEIGHT;
2077        float original_pixel_uv_size = 2.0 * SCREEN_HEIGHT / iResolution.y;
2078
2079        vec2 pi_uv = (original_uv - vec2(0.0, SCREEN_HEIGHT * 0.62)) * 0.4 + 0.5;
2080        float pi = CharPi(pi_uv, original_pixel_uv_size) * 4.0 * smoothstep(75.0,80.0,time);
2081        final_colour.x += sqrt(pi);
2082    }
2083
2084    fragColor = vec4(final_colour.xxx,1.0);
2085}
2086
2087 void main(void)
2088{
2089  //just some shit to wrap shadertoy's stuff
2090  vec2 FragCoord = vTexCoord.xy*OutputSize.xy;
2091  mainImage(FragColor,FragCoord);
2092}
2093#endif
2094