· 9 years ago · Dec 04, 2016, 01:36 AM
1using UnityEngine;
2using System.Collections;
3using System.Collections.Generic;
4using System.Text;
5using System.Threading;
6using System.Linq;
7using System.Text.RegularExpressions;
8
9public enum PixelCoordMethod { Prime, Circular, Ordered, RandomWalk, HilbertCurve }
10
11public enum SourceColorType { Image, Greyscale, ColorShell, RandomWalk, HilbertCurve }
12
13// from http://www.tiac.net/~sw/2008/10/Hilbert/hilbert.py
14public static class HilbertCurve {
15
16 public static int[] IntToHilbert( int i, int nd ) {
17
18 int[] indexChunks = UnpackIndex( i, nd );
19 int mask = (1 << nd) - 1;
20 int start = 0;
21 int end = InitialEnd( indexChunks.Length, nd );
22 int[] coordChunks = new int[ indexChunks.Length ];
23
24 for (int j = 0; j < indexChunks.Length; j++) {
25
26 i = indexChunks[ j ];
27 coordChunks[ j ] = GrayEncodeTravel( start, end, mask, i );
28 ChildStartEnd( ref start, ref end, mask, i );
29 }
30 return PackCoords( coordChunks, nd );
31 }
32
33 static int[] UnpackIndex( int i, int nd ) {
34
35 int p = 1 << nd;
36 int nChunks = Mathf.Max( 1, Mathf.RoundToInt( Mathf.Ceil( Mathf.Log( i + 1, p ) ) ) );
37 int[] chunks = new int[ nChunks ];
38
39 for (int j = nChunks - 1; j > -1; j--) {
40
41 chunks[ j ] = TrueMod( i, p );
42 i /= p;
43 }
44 return chunks;
45 }
46
47 static int InitialEnd( int nChunks, int nd ) {
48
49 return 1 << TrueMod( -nChunks - 1, nd );
50 }
51
52 static int GrayEncodeTravel( int start, int end, int mask, int i ) {
53
54 int travelBit = start ^ end;
55 int modulus = mask + 1;
56 int g = GrayEncode( i ) * travelBit * 2;
57 return ((g | (g / modulus) ) & mask) ^ start;
58 }
59
60 static int GrayEncode( int bn ) {
61
62 return bn ^ (bn / 2);
63 }
64
65 static void ChildStartEnd( ref int start, ref int end, int mask, int i ) {
66
67 int parentStart = start, parentEnd = end;
68 int startI = Mathf.Max( 0, (i - 1) & ~1);
69 int endI = Mathf.Min( mask, (i + 1) | 1);
70 start = GrayEncodeTravel( parentStart, parentEnd, mask, startI );
71 end = GrayEncodeTravel( parentStart, parentEnd, mask, endI );
72 }
73
74 static int[] TransposeBits( int[] srcs, int nDests ) {
75
76 int[] dests = new int[ nDests ];
77
78 for (int j = nDests - 1; j > -1; j--) {
79
80 int dest = 0;
81
82 for (int k = 0; k < srcs.Length; k++) {
83
84 dest = dest * 2 + TrueMod( srcs[ k ], 2 );
85 srcs[ k ] /= 2;
86 }
87 dests[ j ] = dest;
88 }
89 return dests;
90 }
91
92 static int[] PackCoords( int[] chunks, int nd ) {
93
94 return TransposeBits( chunks, nd );
95 }
96
97 static int TrueMod( int x, int m ) {
98
99 if (m < 0) { m = -m; }
100 return (x % m + m) % m;
101 }
102}
103
104public class ColorDistributor: MonoBehaviour {
105
106 public static StringBuilder staticSB = new StringBuilder();
107
108 [Tooltip( "Should be 64 for a 512*512 image." )]
109 public int levels = 64;
110 public Renderer targetObj;
111 public float redrawInterval = 0.05f, reportInterval = 2.0f;
112
113 [Header( "Pixel order" )]
114 public PixelCoordMethod pixelPattern = PixelCoordMethod.RandomWalk;
115 public int minPixelCloseness = 2;
116 public float primeMultipleBase = 1.2918f;
117
118 [Header( "Source colours order" )]
119 public SourceColorType sourceColorType = SourceColorType.Image;
120 [Tooltip( "Should be a 512*512 image." )]
121 public Texture2D image;
122 public int minColorCloseness = 3;
123 public bool randomLutOffset = true;
124
125 [Header( "Threading" )]
126 public bool threaded = true;
127 public int numThreads = 5, cycleSize = 19, minFallBehind = 100, sleepTime = 2;
128
129 Texture2D target;
130 int numCreatedThreads = 0, prime, highestIndexAssigned = 0, lutOffset = 0;
131 IntColor[] colorSearchLUT;
132 bool[] threadsStarted;
133 bool abortThreads;
134 Thread[] threads;
135
136 [System.NonSerialized] int[,,] assignedColors;
137 [System.NonSerialized] bool[] assignedPixels;
138 [System.NonSerialized] Color[] sourceColors;
139 [System.NonSerialized] Color[] targetColors;
140 [System.NonSerialized] int[] positionLUT, lastAssignedPerThread;
141
142 void Start() {
143
144 StartCoroutine( DistributeColors() );
145 }
146
147 struct IntColor {
148
149 public int r, g, b;
150 }
151
152 IntColor[] SphereLUT( int coverage ) {
153
154 int lutSpan = coverage * 2 - 1;
155 int numShells = (int) (Mathf.Sqrt( 3 ) * lutSpan) + 2;
156 List< IntColor >[] shells = new List< IntColor >[ numShells ];
157 for (int shell = 0; shell < numShells; shell++) { shells[ shell ] = new List< IntColor >(); }
158
159 IntColor[] lut = new IntColor[ lutSpan * lutSpan * lutSpan ];
160
161 for (int x = -coverage + 1; x < coverage; x++) {
162 for (int y = -coverage + 1; y < coverage; y++) {
163 for (int z = -coverage + 1; z < coverage; z++) {
164 IntColor ic = new IntColor { r = x, g = y, b = z };
165 shells[ (int) Mathf.Sqrt( x * x + y * y + z * z ) ].Add( ic );
166 }
167 }
168 }
169 int i = 0;
170 for (int shell = 0; shell < numShells; shell++) {
171 for (int j = 0; j < shells[ shell ].Count; j++) {
172 lut[ i ] = shells[ shell ][ j ];
173 i++;
174 }
175 }
176 return lut;
177 }
178
179 int[] Flat2DCircleLUT( int coverage ) {
180
181 int lutSpan = coverage * 2 - 1;
182 int numRings = (int) (Mathf.Sqrt( 2 ) * lutSpan) + 1;
183 List< int >[] rings = new List< int >[ numRings ];
184 for (int ring = 0; ring < numRings; ring++) { rings[ ring ] = new List< int >(); }
185
186 int[] lut = new int[ lutSpan * lutSpan ];
187
188 for (int x = -coverage + 1; x < coverage; x++) {
189 for (int y = -coverage + 1; y < coverage; y++) {
190 rings[ (int) Mathf.Sqrt( x * x + y * y ) ].Add( (x + coverage - 1) * lutSpan + y + coverage - 1 );
191 }
192 }
193 int i = 0;
194 for (int ring = 0; ring < numRings; ring++) {
195 for (int j = 0; j < rings[ ring ].Count; j++) {
196 lut[ i ] = rings[ ring ][ j ];
197 i++;
198 }
199 }
200 return lut;
201 }
202
203 int[] Flat2DCircleLUTCentred( int coverage ) {
204
205 int numRings = (int) (Mathf.Sqrt( 2 ) * (coverage / 2)) + 1;
206 List< int >[] rings = new List< int >[ numRings ];
207 for (int ring = 0; ring < numRings; ring++) { rings[ ring ] = new List< int >(); }
208
209 int[] lut = new int[ coverage * coverage ];
210
211 for (int x = 0; x < coverage; x++) {
212 for (int y = 0; y < coverage; y++) {
213 int x2 = x - coverage / 2;
214 int y2 = y - coverage / 2;
215 rings[ (int) Mathf.Sqrt( x2 * x2 + y2 * y2 ) ].Add( x * coverage + y );
216 }
217 }
218 int i = 0;
219 for (int ring = 0; ring < numRings; ring++) {
220 for (int j = 0; j < rings[ ring ].Count; j++) {
221 lut[ i ] = rings[ ring ][ j ];
222 i++;
223 }
224 }
225 return lut;
226 }
227
228 Color[] OutwardColorMap( int levels ) {
229
230 int numShells = (int) (Mathf.Sqrt( 3 ) * (levels / 2)) + 1;
231 List< Color >[] shells = new List< Color >[ numShells ];
232 for (int shell = 0; shell < numShells; shell++) { shells[ shell ] = new List< Color >(); }
233
234 Color[] lut = new Color[ levels * levels * levels ];
235 float divisor = levels - 1;
236
237 for (int x = 0; x < levels; x++) {
238 for (int y = 0; y < levels; y++) {
239 for (int z = 0; z < levels; z++) {
240 int x2 = x - levels / 2;
241 int y2 = y - levels / 2;
242 int z2 = z - levels / 2;
243 Color color = new Color( x / divisor, y / divisor, z / divisor );
244 shells[ (int) Mathf.Sqrt( x2 * x2 + y2 * y2 + z2 * z2 ) ].Add( color );
245 }
246 }
247 }
248 int i = 0;
249 for (int shell = 0; shell < numShells; shell++) {
250 for (int j = 0; j < shells[ shell ].Count; j++) {
251 lut[ i ] = shells[ shell ][ j ];
252 i++;
253 }
254 }
255 return lut;
256 }
257
258 int[] SphereLUTCentred( int coverage ) {
259
260 int numShells = (int) (Mathf.Sqrt( 3 ) * (coverage / 2)) + 1;
261 List< int >[] shells = new List< int >[ numShells ];
262 for (int shell = 0; shell < numShells; shell++) { shells[ shell ] = new List< int >(); }
263
264 int[] lut = new int[ coverage * coverage * coverage ];
265
266 for (int x = 0; x < coverage; x++) {
267 for (int y = 0; y < coverage; y++) {
268 for (int z = 0; z < coverage; z++) {
269 int x2 = x - coverage / 2;
270 int y2 = y - coverage / 2;
271 int z2 = z - coverage / 2;
272 shells[ (int) Mathf.Sqrt( x2 * x2 + y2 * y2 + z2 * z2 ) ]
273 .Add( z * coverage * coverage + x * coverage + y );
274 }
275 }
276 }
277 int i = 0;
278 for (int shell = 0; shell < numShells; shell++) {
279 for (int j = 0; j < shells[ shell ].Count; j++) {
280 lut[ i ] = shells[ shell ][ j ];
281 i++;
282 }
283 }
284 return lut;
285 }
286
287 // data for generating random walks - avoids unnecessary allocations & parameters during process
288 bool sequenceDebug = false;
289 int currIndex = 0, walkWidth, numAdjacent;
290 FlatMDArray< bool > visited;
291 int[][] adjacent;
292
293 int callsToAdjacentUnvisited2D = 0;
294
295 void CheckAdjacency2D( int[] coord, bool close, ref int numActualAdjacent ) {
296
297 if (!visited[ coord ]) {
298
299 if (!close || NumDiagonallyAdjacentVisited2D( coord ) >= minPixelCloseness) {
300
301 adjacent[ numAdjacent ] = coord;
302 numAdjacent++;
303 }
304 numActualAdjacent++;
305 }
306 }
307
308 // returns true if there is zero or one spare adjacent coordinates and we need to mark this one boxed in
309 bool AdjacentUnvisited2D( int[] coord, bool close ) {
310
311 callsToAdjacentUnvisited2D++;
312
313 numAdjacent = 0;
314 int numActualAdjacent = 0;
315
316 if (coord[ 0 ] == 0 || coord[ 0 ] == walkWidth - 1 || coord[ 1 ] == 0 || coord[ 1 ] == walkWidth - 1) {
317 close = false;
318 }
319
320 if (coord[ 0 ] > 0) {
321 CheckAdjacency2D( new[] { coord[ 0 ] - 1, coord[ 1 ] }, close, ref numActualAdjacent );
322 }
323 if (coord[ 0 ] < walkWidth - 1 ) {
324 CheckAdjacency2D( new[] { coord[ 0 ] + 1, coord[ 1 ] }, close, ref numActualAdjacent );
325 }
326 if (coord[ 1 ] > 0) {
327 CheckAdjacency2D( new[] { coord[ 0 ], coord[ 1 ] - 1 }, close, ref numActualAdjacent );
328 }
329 if (coord[ 1 ] < walkWidth - 1 ) {
330 CheckAdjacency2D( new[] { coord[ 0 ], coord[ 1 ] + 1 }, close, ref numActualAdjacent );
331 }
332
333 return numActualAdjacent < 2;
334 }
335
336 void CheckAdjacency3D( int[] coord, bool close, ref int numActualAdjacent ) {
337
338 if (!visited[ coord ]) {
339
340 if (!close || NumDiagonallyAdjacentVisited3D( coord ) >= minColorCloseness) {
341
342 adjacent[ numAdjacent ] = coord;
343 numAdjacent++;
344 }
345 numActualAdjacent++;
346 }
347 }
348
349 // returns true if there is zero or one spare adjacent coordinates and we need to mark this one boxed in
350 bool AdjacentUnvisited3D( int[] coord, bool close ) {
351
352 numAdjacent = 0;
353 int numActualAdjacent = 0;
354
355 if (coord[ 0 ] == 0 || coord[ 0 ] == walkWidth - 1 || coord[ 1 ] == 0 || coord[ 1 ] == walkWidth - 1
356 || coord[ 2 ] == 0 || coord[ 2 ] == walkWidth - 1) {
357
358 close = false;
359 }
360
361 if (coord[ 0 ] > 0) {
362 CheckAdjacency3D( new[] { coord[ 0 ] - 1, coord[ 1 ], coord[ 2 ] }, close, ref numActualAdjacent );
363 }
364 if (coord[ 0 ] < walkWidth - 1) {
365 CheckAdjacency3D( new[] { coord[ 0 ] + 1, coord[ 1 ], coord[ 2 ] }, close, ref numActualAdjacent );
366 }
367 if (coord[ 1 ] > 0) {
368 CheckAdjacency3D( new[] { coord[ 0 ], coord[ 1 ] - 1, coord[ 2 ] }, close, ref numActualAdjacent );
369 }
370 if (coord[ 1 ] < walkWidth - 1) {
371 CheckAdjacency3D( new[] { coord[ 0 ], coord[ 1 ] + 1, coord[ 2 ] }, close, ref numActualAdjacent );
372 }
373 if (coord[ 2 ] > 0) {
374 CheckAdjacency3D( new[] { coord[ 0 ], coord[ 1 ], coord[ 2 ] - 1 }, close, ref numActualAdjacent );
375 }
376 if (coord[ 2 ] < walkWidth - 1) {
377 CheckAdjacency3D( new[] { coord[ 0 ], coord[ 1 ], coord[ 2 ] + 1 }, close, ref numActualAdjacent );
378 }
379
380 return numActualAdjacent < 2;
381 }
382
383 int NumDiagonallyAdjacentVisited2D( params int[] coord ) {
384
385 int adjVisited = 0;
386 for (int x = coord[ 0 ] - 1; x <= coord[ 0 ] + 1; x++) {
387 if (x < 0 || x >= walkWidth) { continue; }
388 for (int y = coord[ 1 ] - 1; y <= coord[ 1 ] + 1; y++) {
389 if (y < 0 || y >= walkWidth) { continue; }
390 if (x == coord[ 0 ] && y == coord[ 1 ]) { continue; }
391 if (visited[ x, y ]) { adjVisited++; }
392 }
393 }
394 return adjVisited;
395 }
396
397 int NumDiagonallyAdjacentVisited3D( params int[] coord ) {
398
399 int adjVisited = 0;
400 for (int x = coord[ 0 ] - 1; x <= coord[ 0 ] + 1; x++) {
401 if (x < 0 || x >= walkWidth) { continue; }
402 for (int y = coord[ 1 ] - 1; y <= coord[ 1 ] + 1; y++) {
403 if (y < 0 || y >= walkWidth) { continue; }
404 for (int z = coord[ 1 ] - 1; z <= coord[ 1 ] + 1; z++) {
405 if (z < 0 || z >= walkWidth) { continue; }
406 if (x == coord[ 0 ] && y == coord[ 1 ] && z == coord[ 2 ]) { continue; }
407 if (visited[ x, y, z ]) { adjVisited++; }
408 }
409 }
410 }
411 return adjVisited;
412 }
413
414 void LogTimeTaken( string name, System.Action action ) {
415
416 float startTime = Time.realtimeSinceStartup;
417 action();
418 Debug.Log( "Time taken for " + name + ": " + (Time.realtimeSinceStartup - startTime) );
419 }
420
421 int[] RandomWalkSequence2D( int width ) {
422
423 walkWidth = width;
424 int length = width * width;
425 adjacent = new int[ 4 ][];
426 visited = new Flat2DArray< bool >( width, width );
427 var boxedIn = new bool[ length ];
428 var sequence = new int[ length ][];
429 currIndex = 0;
430 int[] startPoint = new[] { width / 2, width / 2 };
431 sequence[ currIndex++ ] = startPoint;
432 visited[ startPoint ] = true;
433
434 while (currIndex < length) {
435
436 int currCheck = currIndex;
437 numAdjacent = 0;
438 if (currCheck <= 0) { Debug.Log( "oops" ); Debug.Break(); }
439
440 do if (!boxedIn[ --currCheck ]
441 && AdjacentUnvisited2D( sequence[ currCheck ], currIndex - 10 > minPixelCloseness )) {
442 boxedIn[ currCheck ] = true;
443 } while (numAdjacent == 0);
444
445 int[] next = adjacent[ Random.Range( 0, numAdjacent ) ];
446 visited[ next ] = true;
447 sequence[ currIndex++ ] = next;
448 }
449
450 return sequence.Select( coord => visited.FlatIndex( coord ) ).ToArray();
451 }
452
453 void SaveCurrentWalkImage( int width ) {
454
455 Color[] colors = new Color[ width * width ];
456 for (int x = 0; x < width; x++) {
457 for (int y = 0; y < width; y++) {
458 colors[ x + y * width ] = visited[ x, y ] ? Color.white : Color.black;
459 }
460 }
461 Texture2D foo = new Texture2D( width, width );
462 foo.SetPixels( colors );
463 foo.Apply();
464 var bytes = foo.EncodeToPNG();
465 System.IO.File.WriteAllBytes( Application.dataPath + "/../visitedProgress.png", bytes );
466 }
467
468 Color[] RandomWalkColorSequence( int width ) {
469
470 walkWidth = width;
471 int length = width * width * width;
472 adjacent = new int[ 6 ][];
473 visited = new Flat3DArray< bool >( width, width, width );
474 var boxedIn = new bool[ length ];
475 var sequence = new int[ length ][];
476 currIndex = 0;
477 int[] startPoint = new[] { width / 2, width / 2, width / 2 };
478 sequence[ currIndex++ ] = startPoint;
479 visited[ startPoint ] = true;
480
481 while (currIndex < length) {
482
483 int currCheck = currIndex;
484 numAdjacent = 0;
485 if (currCheck <= 0) { Debug.Log( "oops" ); Debug.Break(); return null; }
486
487 do if (!boxedIn[ --currCheck ]
488 && AdjacentUnvisited3D( sequence[ currCheck ], currIndex - 10 > minColorCloseness )) {
489 boxedIn[ currCheck ] = true;
490 } while (numAdjacent == 0);
491
492 int[] next = adjacent[ Random.Range( 0, numAdjacent ) ];
493 visited[ next ] = true;
494 sequence[ currIndex++ ] = next;
495 }
496
497 float divisor = width - 1;
498 return sequence.Select( coord => new Color( coord[ 0 ] / divisor, coord[ 1 ] / divisor, coord[ 2 ] / divisor ) )
499 .ToArray();
500 }
501
502 Color[] HilbertColorSequence( int levels ) {
503
504 Color[] sequence = new Color[ levels * levels * levels ];
505 float divisor = levels - 1;
506
507 for (int i = 0; i < sequence.Length; i++) {
508
509 int[] hilbert = HilbertCurve.IntToHilbert( i, 3 );
510 sequence[ (i + lutOffset) % sequence.Length ]
511 = new Color( hilbert[ 0 ] / divisor, hilbert[ 1 ] / divisor, hilbert[ 2 ] / divisor );
512 }
513 return sequence;
514 }
515
516 Color[] GreyColorSequence( int length ) {
517
518 Color[] colors = new Color[ length ];
519
520 for (int i = 0; i < length; i++) {
521 colors[ (i + lutOffset) % colors.Length ] = Color.black + Color.white * (i / (float) (length - 1));
522 }
523 return colors;
524 }
525
526 int[] PrimeSequence( int length ) {
527
528 int[] sequence = new int[ length ];
529 int primeIndex = prime % length;
530
531 for (int i = 0; i < length; i++) {
532
533 primeIndex = (primeIndex + prime) % length;
534 sequence[ i ] = primeIndex;
535 }
536 return sequence;
537 }
538
539 void BayerLevel( int x, int y, int size, int value, int step, int[] sequence, int tableWidth ) {
540
541 if (size == 1) {
542 sequence[ value ] = x + y * tableWidth;
543 return;
544 }
545 int half = size / 2;
546 int quadStep = step * 4;
547
548 BayerLevel( x, y, half, value + (step * 0), quadStep, sequence, tableWidth );
549 BayerLevel( x + half, y + half, half, value + (step * 1), quadStep, sequence, tableWidth );
550 BayerLevel( x + half, y, half, value + (step * 2), quadStep, sequence, tableWidth );
551 BayerLevel( x, y + half, half, value + (step * 3), quadStep, sequence, tableWidth );
552 }
553
554 int[] OrderedDitherSequence( int width ) {
555
556 if (!Mathf.IsPowerOfTwo( width )) { return null; }
557
558 int[] sequence = new int[ width * width ];
559 BayerLevel( 0, 0, width, 0, 1, sequence, width );
560
561 return sequence;
562 }
563
564 int[] Hilbert2DSequence( int width ) {
565
566 int[] sequence = new int[ width * width ];
567
568 for (int i = 0; i < sequence.Length; i++) {
569
570 int[] hilbert = HilbertCurve.IntToHilbert( i, 2 );
571 sequence[ i ] = hilbert[ 0 ] + hilbert[ 1 ] * width;
572 }
573 return sequence;
574 }
575
576 bool IsPrime( int n ) {
577
578 if (n < 2) { return false; }
579 if (n <= 3) { return true; }
580 if (n % 2 == 0 || n % 3 == 0) { return false; }
581 for (int i = 5; i * i < n; i += 6) if (n % i == 0 || n % (i + 2) == 0) { return false; }
582 return true;
583 }
584
585 void ThreadLoop() {
586
587 int threadIndex = numCreatedThreads++;
588 threadsStarted[ threadIndex ] = true;
589
590 int primeIndex = prime % sourceColors.Length, cycle = 0;
591 for (int t = 0; t < threadIndex; t++) { primeIndex = (primeIndex + prime) % sourceColors.Length; }
592
593 for (int i = threadIndex; i < sourceColors.Length; i += numThreads) {
594
595 if (sourceColorType != SourceColorType.Image) {
596 targetColors[ positionLUT[ i ] ] = sourceColors[ i ];
597 }
598 else {
599 Color c = sourceColors[ positionLUT[ i ] ];
600
601 int r0 = Mathf.Clamp( Mathf.RoundToInt( c.r * levels ), 0, levels-1 );
602 int g0 = Mathf.Clamp( Mathf.RoundToInt( c.g * levels ), 0, levels-1 );
603 int b0 = Mathf.Clamp( Mathf.RoundToInt( c.b * levels ), 0, levels-1 );
604
605 bool found = false;
606 int r1 = r0, g1 = g0, b1 = b0;
607 if (assignedColors[ r0, g0, b0 ] == 0) {
608 found = true;
609 }
610
611 if (!found) {
612 for (int lutIndex = 0; lutIndex < colorSearchLUT.Length; lutIndex++) {
613
614 IntColor ic = colorSearchLUT[ lutIndex ];
615 r1 = ic.r + r0;
616 g1 = ic.g + g0;
617 b1 = ic.b + b0;
618 if (r1 < 0 || r1 >= levels || g1 < 0 || g1 >= levels || b1 < 0 || b1 >= levels) { continue; }
619 if (assignedColors[ r1, g1, b1 ] == 1) { continue; }
620 break;
621 }
622 }
623
624 assignedColors[ r1, g1, b1 ] = assignedColors[ r1, g1, b1 ] > 1 ? 1 : assignedColors[ r1, g1, b1 ] + 1;
625 targetColors[ positionLUT[ i ] ] = new Color( (float) r1 / levels, (float) g1 / levels,
626 (float) b1 / levels );
627 }
628
629 if (i > highestIndexAssigned) { highestIndexAssigned = i; }
630 lastAssignedPerThread[ threadIndex ] = i;
631
632 if (cycle == cycleSize) {
633 cycle = 0;
634 for (int t = 0; t < numThreads; t++) {
635 if (i - lastAssignedPerThread[ t ] > minFallBehind) {
636 Thread.Sleep( sleepTime );
637 break;
638 }
639 }
640 }
641
642 for (int t = 0; t < numThreads; t++) { primeIndex = (primeIndex + prime) % sourceColors.Length; }
643 cycle++;
644 if (abortThreads) { break; }
645 }
646 }
647
648 IEnumerator DistributeColors() {
649
650 float startTime = Time.realtimeSinceStartup;
651
652 if (image && image.width * image.height != levels * levels * levels) {
653
654 Debug.LogError( "Wrong size image - number of pixels must match number of levels cubed!" );
655 yield break;
656 }
657
658 int length = levels * levels * levels;
659 assignedColors = new int[ levels, levels, levels ];
660 assignedPixels = new bool[ image.width * image.height ];
661 sourceColors = image.GetPixels();
662 targetColors = new Color[ image.width * image.height ];
663 target = new Texture2D( image.width, image.height, TextureFormat.ARGB32, false );
664 lastAssignedPerThread = new int[ numThreads ];
665 if (randomLutOffset) { lutOffset = Random.Range( 0, sourceColors.Length ); }
666 prime = (int) (sourceColors.Length * primeMultipleBase);
667 while (!IsPrime( prime )) { prime++; }
668
669 switch (pixelPattern) {
670 case PixelCoordMethod.Prime: { positionLUT = PrimeSequence( length ); } break;
671 case PixelCoordMethod.Circular: { positionLUT = Flat2DCircleLUTCentred( image.width ); } break;
672 case PixelCoordMethod.Ordered: { positionLUT = OrderedDitherSequence( image.width ); } break;
673 case PixelCoordMethod.RandomWalk: { positionLUT = RandomWalkSequence2D( image.width ); } break;
674 case PixelCoordMethod.HilbertCurve: { positionLUT = Hilbert2DSequence( image.width ); } break;
675 }
676 colorSearchLUT = SphereLUT( levels );
677
678 Debug.Log( "Time to generate position lookup table: " + (Time.realtimeSinceStartup - startTime) );
679 startTime = Time.realtimeSinceStartup;
680
681 switch (sourceColorType) {
682 case SourceColorType.Image: { sourceColors = image.GetPixels(); } break;
683 case SourceColorType.Greyscale: { sourceColors = GreyColorSequence( length ); } break;
684 case SourceColorType.ColorShell: { sourceColors = OutwardColorMap( levels ); } break;
685 case SourceColorType.RandomWalk: { sourceColors = RandomWalkColorSequence( levels ); } break;
686 case SourceColorType.HilbertCurve: { sourceColors = HilbertColorSequence( levels ); } break;
687 }
688
689 Debug.Log( "Time to generate color lookup table: " + (Time.realtimeSinceStartup - startTime) );
690 startTime = Time.realtimeSinceStartup;
691 float reportTime = startTime;
692
693 if (threaded) {
694
695 threads = new Thread[ numThreads ];
696 threadsStarted = new bool[ numThreads ];
697
698 for (int t = 0; t < numThreads; t++) {
699
700 threads[ t ] = new Thread( new ThreadStart( ThreadLoop ) );
701 threads[ t ].Start();
702 }
703
704 Thread.Sleep( (int) (redrawInterval * 1000) );
705
706 while (threads.Any( thread => thread.IsAlive )) {
707
708 Thread.Sleep( (int) (redrawInterval * 1000) );
709 SetTexture();
710
711 if (Time.realtimeSinceStartup > reportTime + reportInterval) {
712
713 float average = (float) lastAssignedPerThread.Average();
714 string percent = ((average / length) * 100.0f).ToString( "0.0" );
715 Debug.Log( "Progress after " + (Time.realtimeSinceStartup - startTime) + " seconds: "
716 + average + " (" + percent + "%)" );
717 reportTime = Time.realtimeSinceStartup;
718 }
719 yield return null;
720 }
721 }
722 else {
723 for (int i = 0; i < sourceColors.Length; i++) {
724
725 if (sourceColorType != SourceColorType.Image) {
726 targetColors[ positionLUT[ i ] ] = sourceColors[ i ];
727 }
728 else {
729 Color c = sourceColors[ positionLUT[ i ] ];
730
731 int r0 = Mathf.Clamp( Mathf.RoundToInt( c.r * levels ), 0, levels-1 );
732 int g0 = Mathf.Clamp( Mathf.RoundToInt( c.g * levels ), 0, levels-1 );
733 int b0 = Mathf.Clamp( Mathf.RoundToInt( c.b * levels ), 0, levels-1 );
734
735 bool found = false;
736 int r1 = r0, g1 = g0, b1 = b0;
737 if (assignedColors[ r0, g0, b0 ] == 0) { found = true; }
738
739 if (!found) {
740 for (int lutIndex = 0; lutIndex < colorSearchLUT.Length; lutIndex++) {
741
742 IntColor ic = colorSearchLUT[ lutIndex ];
743 r1 = ic.r + r0;
744 g1 = ic.g + g0;
745 b1 = ic.b + b0;
746 if (r1 < 0 || r1 >= levels || g1 < 0 || g1 >= levels || b1 < 0 || b1 >= levels) { continue; }
747 if (assignedColors[ r1, g1, b1 ] == 1) { continue; }
748 break;
749 }
750 }
751
752 assignedColors[ r1, g1, b1 ] = assignedColors[ r1, g1, b1 ] > 1 ? 1 : assignedColors[ r1, g1, b1 ] + 1;
753 targetColors[ positionLUT[ i ] ] = new Color( (float) r1 / levels, (float) g1 / levels,
754 (float) b1 / levels );
755 }
756
757 if (Time.realtimeSinceStartup > startTime + redrawInterval) {
758
759 SetTexture();
760
761 if (Time.realtimeSinceStartup + reportTime > redrawInterval) {
762
763 string percent = (((float) highestIndexAssigned / length) * 100.0f).ToString( "0.0" );
764 Debug.Log( "Progress after " + (Time.realtimeSinceStartup - startTime) + " seconds: "
765 + highestIndexAssigned + " (" + percent + "%)" );
766 reportTime = Time.realtimeSinceStartup;
767 }
768 yield return null;
769 }
770
771 if (Input.GetKeyDown( KeyCode.S )) {
772
773 byte[] bytes2 = target.EncodeToPNG();
774 System.IO.File.WriteAllBytes( Application.dataPath + "/../output.png", bytes2 );
775 }
776
777 if (Input.GetKeyDown( KeyCode.A )) { yield break; }
778 highestIndexAssigned = i;
779 }
780 }
781
782 SetTexture();
783 byte[] bytes = target.EncodeToPNG();
784 System.IO.File.WriteAllBytes(Application.dataPath + "/../output.png", bytes);
785 Debug.Log( "Total time to fill image: " + (Time.realtimeSinceStartup - startTime) );
786
787 //int[] assignmentHistogram = new int[ 10 ];
788 //for (int x = 0; x < levels; x++) {
789 // for (int y = 0; y < levels; y++) {
790 // for (int z = 0; z < levels; z++) {
791 // assignmentHistogram[ Mathf.Clamp( assignedColors[ x, y, z ], 0, 9 ) ]++;
792 // }
793 // }
794 //}
795 //for (int i = 0; i < 10; i++) {
796 // Debug.Log( "Number of colours assigned " + i + " times: " + assignmentHistogram[ i ] );
797 //}
798 }
799
800 void SetTexture() {
801
802 target.SetPixels( targetColors );
803 target.Apply();
804 targetObj.material.mainTexture = target;
805 }
806
807 void EndThreads() {
808
809 abortThreads = true;
810 }
811
812 void OnDestroy() {
813
814 EndThreads();
815 }
816
817 void OnDisable() {
818
819 Destroy( target );
820 EndThreads();
821 }
822
823 void OnApplicationQuit() {
824
825 EndThreads();
826 }
827}
828
829class FlatMDArray< T > {
830
831 public static FlatMDArray< T > Create( params int[] dimensions ) {
832
833 switch (dimensions.Length) {
834
835 case 2: { return new Flat2DArray< T >( dimensions[ 0 ], dimensions[ 1 ] ); }
836 case 3: { return new Flat3DArray< T >( dimensions[ 0 ], dimensions[ 1 ], dimensions[ 2 ] ); }
837 default: { return new FlatMDArray< T >( dimensions ); }
838 }
839 }
840
841 public T[] flat { get { return m_array; } }
842 public int[] dimensions { get { return m_dimensions; } }
843 public string dimensionsString { get { return m_dimensionsString; } }
844
845 protected T[] m_array;
846 protected int[] m_dimensions;
847 protected string m_dimensionsString;
848
849 public FlatMDArray( params int[] dimensions ) {
850
851 m_dimensions = dimensions;
852 int length = 1;
853 foreach (int d in dimensions) { length *= d; }
854 m_array = new T[ length ];
855 m_dimensionsString = "[" + string.Join( ", ", dimensions.Select( d => d.ToString() ).ToArray() ) + "]";
856 }
857
858 public virtual int FlatIndex( params int[] indices ) {
859
860 if (indices.Length != m_dimensions.Length) {
861
862 throw new System.ArgumentException( "Expected " + m_dimensions.Length + " indices; only "
863 + indices.Length + " were specified." );
864 }
865 int index = 0;
866
867 for (int i = 0; i < indices.Length; i++) {
868
869 int multiplier = 1;
870
871 if (indices[ i ] < 0 || indices[ i ] >= m_dimensions[ i ]) {
872
873 throw new System.ArgumentException( "Index #" + i + ": " + indices[ i ]
874 + " is out of bounds. Dimensions: " + m_dimensionsString );
875 }
876 for (int j = i - 1; j >= 0; j--) { multiplier *= m_dimensions[ j ]; }
877
878 index += indices[ i ] * multiplier;
879 }
880 return index;
881 }
882
883 // this should not be used - exists to be overridden in Flat2DArray
884 public virtual int FlatIndex( int x, int y ) {
885
886 return FlatIndex( new[] { x, y } );
887 }
888
889 // this should not be used - exists to be overridden in Flat2DArray
890 public virtual int FlatIndex( int x, int y, int z ) {
891
892 return FlatIndex( new[] { x, y, z } );
893 }
894
895 public T this[ params int[] indices ] {
896 get { return m_array[ FlatIndex( indices ) ]; }
897 set { m_array[ FlatIndex( indices ) ] = value; }
898 }
899}
900
901class Flat2DArray< T >: FlatMDArray< T > {
902
903 public int width { get { return m_width; } }
904 public int height { get { return m_height; } }
905
906 protected int m_width, m_height;
907
908 public Flat2DArray( int width, int height ): base( width, height ) {
909
910 m_width = width;
911 m_height = height;
912 }
913
914 public override int FlatIndex( int x, int y ) {
915
916 if (x < 0 || x > m_width || y < 0 || y > m_height) {
917
918 throw new System.ArgumentException( "Index (x: " + x + ", y: " + y
919 + ") out of bounds. Dimensions: " + m_dimensionsString );
920 }
921 return x + y * m_width;
922 }
923}
924
925class Flat3DArray< T >: FlatMDArray< T > {
926
927 public int width { get { return m_width; } }
928 public int height { get { return m_height; } }
929 public int depth { get { return m_depth; } }
930
931 protected int m_width, m_height, m_depth, zMultiple;
932
933 public Flat3DArray( int width, int height, int depth ): base( width, height, depth ) {
934
935 m_width = width;
936 m_height = height;
937 m_depth = depth;
938 zMultiple = width * height;
939 }
940
941 public override int FlatIndex( int x, int y, int z ) {
942
943 if (x < 0 || x >= m_width || y < 0 || y >= m_height || z < 0 || z >= m_depth ) {
944
945 throw new System.ArgumentException( "Index (x: " + x + ", y: " + y + ", z: " + z
946 + ") out of bounds. Dimensions: " + m_dimensionsString );
947 }
948 return x + y * m_width + z * zMultiple;
949 }
950}