private int FindTile(int[] colors) { List <int> candidates = new List <int>(); for (int i = 0; i < this.tiles.Count; i++) { WangTile t = tiles[i]; bool match = true; for (int j = 0; j < (int)WangTile.neighbour_e.NUM; j++) { if (colors[j] < 0) { continue; } if (t.edgeColors[j] != colors[j]) { match = false; break; } } if (match) { candidates.Add(i); } } if (candidates.Count == 0) { return(-1); } return(candidates[random.Next(candidates.Count)]); }
private void ToImage(Image img, Size tileSize, List <Bitmap> tileImgs) { Graphics g = Graphics.FromImage(img); int tilesW = img.Width / tileSize.Width; int tilesH = img.Height / tileSize.Height; WangTile lastTile = null; int last = -1; int[] lastRow = new int[tilesW]; int[] currentRow = new int[tilesW]; for (int i = 0; i < tilesW; i++) { lastRow[i] = -1; currentRow[i] = -1; } for (int j = 0; j < tilesH; j++) { for (int i = 0; i < tilesW; i++) { int current; List <int> availableTiles = null; if (last == -1) { // no restrictions current = random.Next(tiles.Count); } else { if (j == 0) { // only restriction is west edge, which has to match // the east edge of the last tile placed availableTiles = tilesSortedByWest[lastTile.edgeColors[(int)WangTile.neighbour_e.EAST]]; } else { int east = (i == 0 ? random.Next(numColors) : lastTile.edgeColors[(int)WangTile.neighbour_e.EAST]); int north = tiles[lastRow[i]].edgeColors[(int)WangTile.neighbour_e.SOUTH]; // restrict by west and north edges availableTiles = tilesSortedByWestNorth[east, north]; } current = availableTiles[random.Next(availableTiles.Count)];; } currentRow[i] = current; g.DrawImage(tileImgs[current], new Point(i * tileSize.Width, j * tileSize.Height)); last = current; lastTile = tiles[last]; } for (int k = 0; k < tilesW; k++) { lastRow[k] = currentRow[k]; } } }
private void Subdivide(WangTile tile, int subdivisions, List<int>[] rules, out int[,] result) { result = new int[subdivisions, subdivisions]; // the rules determine, for each edge color on the source tile, a // list of colors it is substituted with in the subdivided tile set /* * * _a__b__c__d_ _______________ * -------cN-------- m|\ /\ /\ /\ /|i |\ /|\ /|\ /|\ /| * |\ /| |/ \| |/_\|/_\|/_\|/_\| * | \ / | n|\ /|j |\ /|\ /|\ /|\ /| * | \ / | |/ \| |/_\|/_\|/_\|/_\| * | \ / | o|\ /|k |\ /|\ /|\ /|\ /| * cW X cE --> |/ \| --> |/_\|/_\|/_\|/_\| * | / \ | p|\ /|l |\ /|\ /|\ /|\ /| * | / \ | |/_\/_\/_\/_\| |/_\|/_\|/_\|/_\| * | / \ | e f g h * | / \| * |/------cS-------- * * cN --> a,b,c,d * cS --> e,f,g,h * cE --> i,j,k,l * cW --> m,n,o,p * * Only corners have 2 restrictions (m-a, d-i, p-e, l-h), remaining * edges have 1 restriction then we use the scanline algorithm to * fill-in the gaps */ int[] restrictions = new int[(int)WangTile.neighbour_e.NUM]; int previous = -1; int[] upperRow = new int[subdivisions]; for (int i = 0; i < subdivisions; i++) { for (int j = 0; j < subdivisions; j++) { restrictions[(int)WangTile.neighbour_e.NORTH] = -1; restrictions[(int)WangTile.neighbour_e.SOUTH] = -1; restrictions[(int)WangTile.neighbour_e.EAST] = -1; restrictions[(int)WangTile.neighbour_e.WEST] = -1; if (i == 0) restrictions[(int)WangTile.neighbour_e.NORTH] = rules[tile.edgeColors[(int)WangTile.neighbour_e.NORTH]][j]; if (i == subdivisions - 1 ) restrictions[(int)WangTile.neighbour_e.SOUTH] = rules[tile.edgeColors[(int)WangTile.neighbour_e.SOUTH]][j]; if (j == 0) restrictions[(int)WangTile.neighbour_e.WEST] = rules[tile.edgeColors[(int)WangTile.neighbour_e.WEST]][i]; if (j == subdivisions - 1 ) restrictions[(int)WangTile.neighbour_e.EAST] = rules[tile.edgeColors[(int)WangTile.neighbour_e.EAST]][i]; if (i > 0) restrictions[(int)WangTile.neighbour_e.NORTH] = tiles[upperRow[j]].edgeColors[(int)WangTile.neighbour_e.SOUTH]; if (j > 0) restrictions[(int)WangTile.neighbour_e.WEST] = tiles[upperRow[j]].edgeColors[(int)WangTile.neighbour_e.EAST]; result[i, j] = FindTile(restrictions); previous = result[i, j]; } for (int j = 0; j < subdivisions; j++) upperRow[j] = result[i, j]; } }
public void Generate(int numColors, int samplesPerTile, int numThreads ) { random = new Random(); this.numColors = numColors; tiles = new List<WangTile>(); const int numEdges = 4; int numTiles = (int)Math.Pow(numColors, numEdges); #region generate Poisson distributions List<PoissonSample>[] distributions = new List<PoissonSample>[numTiles]; generatePoissonDistParam_t[] threadParams = new generatePoissonDistParam_t[numThreads]; PoissonDistribution[] generators = new PoissonDistribution[numThreads]; AutoResetEvent[] waitHandles = new AutoResetEvent[numThreads]; for (int i = 0; i < numThreads; i++) { threadParams[i].dist = new PoissonDistribution(); threadParams[i].numSamples = samplesPerTile; waitHandles[i] = new AutoResetEvent(false); threadParams[i].autoResetEvent = waitHandles[i]; } int t = 0; Queue<int> availableThreads = new Queue<int>(); for (int i = 0; i < numThreads; i++) availableThreads.Enqueue(i); if (OnConsoleMessage != null) OnConsoleMessage("Generating " + numTiles + " source Poisson Distributions with " + samplesPerTile + " samples each..."); Image debugImage = null; if (OnDebugStep != null) debugImage = new Bitmap(800, 800); while (t < numTiles || availableThreads.Count < numThreads) { while (availableThreads.Count > 0 && t < numTiles) { int i = availableThreads.Dequeue(); if (OnProgressReport != null) OnProgressReport((float)(t + 1) / numTiles); distributions[t] = new List<PoissonSample>(samplesPerTile); threadParams[i].result = distributions[t]; ThreadPool.QueueUserWorkItem(new WaitCallback(GeneratePoissonDistThreaded), threadParams[i]); t++; } int index = WaitHandle.WaitAny(waitHandles); if (debugImage != null) { PoissonDistribution.ToImage( threadParams[index].dist.Samples, Color.Black, debugImage, 1); OnDebugStep(debugImage, "Poisson Distribution " + t); } availableThreads.Enqueue(index); } #endregion #region generate seam tiles List<SourceTile> seamTiles = new List<SourceTile>(); #if DEBUG Color[] colors = new Color[numColors]; for( int i = 0; i < numColors; i++ ) colors[i] = Color.FromArgb(random.Next(256), random.Next(256), random.Next(256)); #endif for (int i = 0; i < numColors; i++) { PoissonDistribution distribution = new PoissonDistribution(); distribution.Generate(samplesPerTile); #if DEBUG foreach (PoissonSample p in distribution.Samples) p.color = colors[i]; #endif seamTiles.Add(new SourceTile(distribution.Samples, i)); } #endregion #region generate all edge permutations /* * 0000 0001 0002 ... 000n * 0010 0011 0012 ... 001n * 0020 0021 0022 ... 002n * ... * 00n0 00n1 00n2 ... 00nn * * 0100 0101 0102 ... 010n * 0110 0111 0112 ... 011n * ... * 01n0 01n1 01n2 ... 01nn * * ... * * nnn0 nnn1 nnn2 ... nnnn */ int[,] edgeCol = new int[numTiles,numEdges]; for (int i = 0; i < numEdges; i++) edgeCol[0, i] = 0; for (int i = 1; i < numTiles; i++) { for (int j = 0; j < numEdges; j++) { edgeCol[i,j] = (edgeCol[i-1,j] + (i % (int)Math.Pow(numColors, j) == 0 ? 1 : 0)) % numColors; } } #endregion #region generate wang tiles WangTile[] tileArray = new WangTile[numTiles]; t = 0; availableThreads.Clear(); createWangTileParam_t[] createWangTileParams = new createWangTileParam_t[numThreads]; for (int i = 0; i < numThreads; i++) { availableThreads.Enqueue(i); createWangTileParams[i] = new createWangTileParam_t(); createWangTileParams[i].autoResetEvent = waitHandles[i]; createWangTileParams[i].autoResetEvent.Reset(); createWangTileParams[i].tileArray = tileArray; } if (OnConsoleMessage != null) OnConsoleMessage("Merging distributions to generate Wang Tiles..." ); while (t < numTiles || availableThreads.Count < numThreads) { while (availableThreads.Count > 0 && t < numTiles) { int i = availableThreads.Dequeue(); if (OnProgressReport != null) OnProgressReport((float)( t + 1 ) / numTiles); createWangTileParams[i].tileBaseDistribution = distributions[t]; createWangTileParams[i].neighbours = new List<SourceTile>(); createWangTileParams[i].tileIndex = t; if (numColors > 1) { for (int j = 0; j < numEdges; j++) createWangTileParams[i].neighbours.Add(seamTiles[edgeCol[t, j]]); } ThreadPool.QueueUserWorkItem(new WaitCallback(CreateWangTileThreaded), createWangTileParams[i]); t++; } int index = WaitHandle.WaitAny(waitHandles); availableThreads.Enqueue(index); } tiles = tileArray.ToList(); SortTiles(); MakeRecursive(); if (OnDebugStep != null) { ToColorTiles(debugImage, new Size(8, 8)); OnDebugStep(debugImage, "Sample coverage of the generated Wang Tiles" ); } #endregion }
private void Subdivide(WangTile tile, int subdivisions, List <int>[] rules, out int[,] result) { result = new int[subdivisions, subdivisions]; // the rules determine, for each edge color on the source tile, a // list of colors it is substituted with in the subdivided tile set /* * * _a__b__c__d_ _______________ * -------cN-------- m|\ /\ /\ /\ /|i |\ /|\ /|\ /|\ /| * |\ /| |/ \| |/_\|/_\|/_\|/_\| * | \ / | n|\ /|j |\ /|\ /|\ /|\ /| * | \ / | |/ \| |/_\|/_\|/_\|/_\| * | \ / | o|\ /|k |\ /|\ /|\ /|\ /| * cW X cE --> |/ \| --> |/_\|/_\|/_\|/_\| * | / \ | p|\ /|l |\ /|\ /|\ /|\ /| * | / \ | |/_\/_\/_\/_\| |/_\|/_\|/_\|/_\| * | / \ | e f g h * | / \| * |/------cS-------- * * cN --> a,b,c,d * cS --> e,f,g,h * cE --> i,j,k,l * cW --> m,n,o,p * * Only corners have 2 restrictions (m-a, d-i, p-e, l-h), remaining * edges have 1 restriction then we use the scanline algorithm to * fill-in the gaps */ int[] restrictions = new int[(int)WangTile.neighbour_e.NUM]; int previous = -1; int[] upperRow = new int[subdivisions]; for (int i = 0; i < subdivisions; i++) { for (int j = 0; j < subdivisions; j++) { restrictions[(int)WangTile.neighbour_e.NORTH] = -1; restrictions[(int)WangTile.neighbour_e.SOUTH] = -1; restrictions[(int)WangTile.neighbour_e.EAST] = -1; restrictions[(int)WangTile.neighbour_e.WEST] = -1; if (i == 0) { restrictions[(int)WangTile.neighbour_e.NORTH] = rules[tile.edgeColors[(int)WangTile.neighbour_e.NORTH]][j]; } if (i == subdivisions - 1) { restrictions[(int)WangTile.neighbour_e.SOUTH] = rules[tile.edgeColors[(int)WangTile.neighbour_e.SOUTH]][j]; } if (j == 0) { restrictions[(int)WangTile.neighbour_e.WEST] = rules[tile.edgeColors[(int)WangTile.neighbour_e.WEST]][i]; } if (j == subdivisions - 1) { restrictions[(int)WangTile.neighbour_e.EAST] = rules[tile.edgeColors[(int)WangTile.neighbour_e.EAST]][i]; } if (i > 0) { restrictions[(int)WangTile.neighbour_e.NORTH] = tiles[upperRow[j]].edgeColors[(int)WangTile.neighbour_e.SOUTH]; } if (j > 0) { restrictions[(int)WangTile.neighbour_e.WEST] = tiles[upperRow[j]].edgeColors[(int)WangTile.neighbour_e.EAST]; } result[i, j] = FindTile(restrictions); previous = result[i, j]; } for (int j = 0; j < subdivisions; j++) { upperRow[j] = result[i, j]; } } }
public void Generate(int numColors, int samplesPerTile, int numThreads) { random = new Random(); this.numColors = numColors; tiles = new List <WangTile>(); const int numEdges = 4; int numTiles = (int)Math.Pow(numColors, numEdges); #region generate Poisson distributions List <PoissonSample>[] distributions = new List <PoissonSample> [numTiles]; generatePoissonDistParam_t[] threadParams = new generatePoissonDistParam_t[numThreads]; PoissonDistribution[] generators = new PoissonDistribution[numThreads]; AutoResetEvent[] waitHandles = new AutoResetEvent[numThreads]; for (int i = 0; i < numThreads; i++) { threadParams[i].dist = new PoissonDistribution(); threadParams[i].numSamples = samplesPerTile; waitHandles[i] = new AutoResetEvent(false); threadParams[i].autoResetEvent = waitHandles[i]; } int t = 0; Queue <int> availableThreads = new Queue <int>(); for (int i = 0; i < numThreads; i++) { availableThreads.Enqueue(i); } if (OnConsoleMessage != null) { OnConsoleMessage("Generating " + numTiles + " source Poisson Distributions with " + samplesPerTile + " samples each..."); } Image debugImage = null; if (OnDebugStep != null) { debugImage = new Bitmap(800, 800); } while (t < numTiles || availableThreads.Count < numThreads) { while (availableThreads.Count > 0 && t < numTiles) { int i = availableThreads.Dequeue(); if (OnProgressReport != null) { OnProgressReport((float)(t + 1) / numTiles); } distributions[t] = new List <PoissonSample>(samplesPerTile); threadParams[i].result = distributions[t]; ThreadPool.QueueUserWorkItem(new WaitCallback(GeneratePoissonDistThreaded), threadParams[i]); t++; } int index = WaitHandle.WaitAny(waitHandles); if (debugImage != null) { PoissonDistribution.ToImage(threadParams[index].dist.Samples, Color.Black, debugImage, 1); OnDebugStep(debugImage, "Poisson Distribution " + t); } availableThreads.Enqueue(index); } #endregion #region generate seam tiles List <SourceTile> seamTiles = new List <SourceTile>(); #if DEBUG Color[] colors = new Color[numColors]; for (int i = 0; i < numColors; i++) { colors[i] = Color.FromArgb(random.Next(256), random.Next(256), random.Next(256)); } #endif for (int i = 0; i < numColors; i++) { PoissonDistribution distribution = new PoissonDistribution(); distribution.Generate(samplesPerTile); #if DEBUG foreach (PoissonSample p in distribution.Samples) { p.color = colors[i]; } #endif seamTiles.Add(new SourceTile(distribution.Samples, i)); } #endregion #region generate all edge permutations /* * 0000 0001 0002 ... 000n * 0010 0011 0012 ... 001n * 0020 0021 0022 ... 002n * ... * 00n0 00n1 00n2 ... 00nn * * 0100 0101 0102 ... 010n * 0110 0111 0112 ... 011n * ... * 01n0 01n1 01n2 ... 01nn * * ... * * nnn0 nnn1 nnn2 ... nnnn */ int[,] edgeCol = new int[numTiles, numEdges]; for (int i = 0; i < numEdges; i++) { edgeCol[0, i] = 0; } for (int i = 1; i < numTiles; i++) { for (int j = 0; j < numEdges; j++) { edgeCol[i, j] = (edgeCol[i - 1, j] + (i % (int)Math.Pow(numColors, j) == 0 ? 1 : 0)) % numColors; } } #endregion #region generate wang tiles WangTile[] tileArray = new WangTile[numTiles]; t = 0; availableThreads.Clear(); createWangTileParam_t[] createWangTileParams = new createWangTileParam_t[numThreads]; for (int i = 0; i < numThreads; i++) { availableThreads.Enqueue(i); createWangTileParams[i] = new createWangTileParam_t(); createWangTileParams[i].autoResetEvent = waitHandles[i]; createWangTileParams[i].autoResetEvent.Reset(); createWangTileParams[i].tileArray = tileArray; } if (OnConsoleMessage != null) { OnConsoleMessage("Merging distributions to generate Wang Tiles..."); } while (t < numTiles || availableThreads.Count < numThreads) { while (availableThreads.Count > 0 && t < numTiles) { int i = availableThreads.Dequeue(); if (OnProgressReport != null) { OnProgressReport((float)(t + 1) / numTiles); } createWangTileParams[i].tileBaseDistribution = distributions[t]; createWangTileParams[i].neighbours = new List <SourceTile>(); createWangTileParams[i].tileIndex = t; if (numColors > 1) { for (int j = 0; j < numEdges; j++) { createWangTileParams[i].neighbours.Add(seamTiles[edgeCol[t, j]]); } } ThreadPool.QueueUserWorkItem(new WaitCallback(CreateWangTileThreaded), createWangTileParams[i]); t++; } int index = WaitHandle.WaitAny(waitHandles); availableThreads.Enqueue(index); } tiles = tileArray.ToList(); SortTiles(); MakeRecursive(); if (OnDebugStep != null) { ToColorTiles(debugImage, new Size(8, 8)); OnDebugStep(debugImage, "Sample coverage of the generated Wang Tiles"); } #endregion }