public void TestMask() { var a = new int[, ] { { 1, 0 }, { 0, 1 }, }; var model = new AdjacentModel(); model.AddSample(TopoArray.Create(a, true).ToTiles()); var mask = new bool[5 * 5]; for (var x = 0; x < 5; x++) { for (var y = 0; y < 5; y++) { if (x == 2 || y == 2) { mask[x + y * 5] = false; } else { mask[x + y * 5] = true; } } } var topology = new GridTopology(5, 5, true).WithMask(mask); var propagator = new TilePropagator(model, topology); propagator.Run(); Assert.AreEqual(Resolution.Decided, propagator.Status); }
public static ITopoArray <T> Scale <T>(ITopoArray <T> topoArray, int scale) { var topology = topoArray.Topology.AsGridTopology(); if (topology.Mask != null) { throw new NotSupportedException(); } var result = new T[topology.Width * scale, topology.Height *scale, topology.Depth *scale]; for (var z = 0; z < topology.Depth; z++) { for (var y = 0; y < topology.Height; y++) { for (var x = 0; x < topology.Width; x++) { var value = topoArray.Get(x, y, z); for (var dz = 0; dz < scale; dz++) { for (var dy = 0; dy < scale; dy++) { for (var dx = 0; dx < scale; dx++) { result[x * scale + dx, y *scale + dy, z *scale + dz] = value; } } } } } } var resultTopology = topology.WithSize(topology.Width * scale, topology.Height * scale, topology.Depth * scale); return(TopoArray.Create(result, resultTopology)); }
/// <summary> /// Builds a WFC sample from a Godot scene. /// </summary> /// <returns></returns> private ITopoArray <LostGen.Block> BuildSample() { var cells = GetChildren().Cast <SampleCell>(); // Get the max size of th esample var size = new LostGen.Point( Mathf.RoundToInt(cells.Max(c => c.Translation.x)), Mathf.RoundToInt(cells.Max(c => c.Translation.y)), Mathf.RoundToInt(cells.Max(c => c.Translation.z)) ); var sample = TopoArray.Create( cells.Aggregate( new LostGen.Block[size.Z, size.Y, size.X], (array, cell) => { var index = new LostGen.Point( Mathf.RoundToInt(cell.Translation.x), Mathf.RoundToInt(cell.Translation.y), Mathf.RoundToInt(cell.Translation.z) ); array[index.Z, index.Y, index.Z] = cell.AsBlock; return(array); } ), periodic: true ); // Generate a board using ID numbers, which we key to the child SampleCells return(null); }
public void QuickStartTest() { ITopoArray <char> sample = TopoArray.Create(new[, ] { { '_', '_', '_' }, { '_', '*', '_' }, { '_', '_', '_' }, }, periodic: false); // Specify the model used for generation var model = new AdjacentModel(sample.ToTiles()); // Set the output dimensions var topology = new Topology(10, 10, periodic: false); // Actually run the algorithm var propagator = new TilePropagator(model, topology); var status = propagator.Run(); if (status != Resolution.Decided) { throw new Exception("Undecided"); } var output = propagator.ToValueArray <char>(); // Display the results for (var y = 0; y < 10; y++) { for (var x = 0; x < 10; x++) { System.Console.Write(output.Get(x, y)); } System.Console.WriteLine(); } }
/// <summary> /// Reads a layer of a Map into an <see cref="ITopoArray{T}"/> /// </summary> public static ITopoArray <Tile> ReadLayer(Map map, TileLayer layer) { if (map.Orientation == Orientation.orthogonal || map.Orientation == Orientation.isometric && map.StaggerAxis == StaggerAxis.None) { var layerArray = new Tile[layer.Width, layer.Height]; var i = 0; for (int y = 0; y < layer.Height; y++) { for (int x = 0; x < layer.Width; x++) { layerArray[x, y] = GidToTile(layer.Data[i++], map.Orientation); } } return(TopoArray.Create(layerArray, false)); } else if (map.Orientation == Orientation.hexagonal) { // Tiled uses a staggered hex layout, while we use an axial one // Convert between them, masking out the dead space // For now, only support one mode of staggering if (map.StaggerAxis != StaggerAxis.y) { throw new NotImplementedException($"Maps staggered on x axis not supported"); } var width = layer.Width + (layer.Height + 1) / 2; var height = layer.Height; var layerArray = new Tile[width, height]; var mask = new bool[width * height]; var topology = new Topology(DirectionSet.Hexagonal2d, width, height, false, false, mask); int i = 0; var isStaggered = map.StaggerIndex == StaggerIndex.even; var xoffset = isStaggered ? -1 : 0; for (int y = 0; y < layer.Height; y++) { if (isStaggered) { xoffset += 1; } for (int x = 0; x < layer.Width; x++) { var newY = y; var newX = x + xoffset; layerArray[newX, newY] = GidToTile(layer.Data[i++], Orientation.hexagonal); var index = topology.GetIndex(newX, newY, 0); mask[index] = true; } isStaggered = !isStaggered; } return(TopoArray.Create(layerArray, topology)); } else { throw new NotImplementedException($"{map.Orientation} not supported"); } }
private static ITopoArray <byte> Load(Size size, Xyzi xyzi) { var data = new byte[size.SizeX, size.SizeY, size.SizeZ]; foreach (var voxel in xyzi.Voxels) { data[voxel.X, voxel.Y, voxel.Z] = voxel.ColorIndex; } return(TopoArray.Create(data, false)); }
public void Export(TileModel model, TilePropagator propagator, string filename, DeBroglieConfig config, ExportOptions exportOptions) { if (config.Animate) { if (exportOptions is BitmapExportOptions) { var topoArray = propagator.ToWeightedArraySets().Map(WeightedColorAverage); var bitmap = BitmapUtils.ToBitmap(topoArray.ToArray2d()); bitmap.Save(filename); } else if (exportOptions is BitmapSetExportOptions bseo) { var topoArray = propagator.ToWeightedArraySets(); var tileTopology = topoArray.Topology.AsGridTopology().WithSize(bseo.TileWidth, bseo.TileHeight, 1); var subTiles = bseo.Bitmaps.ToDictionary(x => x.Key, x => TopoArray.Create(BitmapUtils.ToColorArray(x.Value), tileTopology).Map(c => new Tile(c))); var exploded = MoreTopoArrayUtils.ExplodeWeightedTiles(topoArray, subTiles, bseo.TileWidth, bseo.TileHeight, 1).Map(WeightedColorAverage); var bitmap = BitmapUtils.ToBitmap(exploded.ToArray2d()); bitmap.Save(filename); } else { throw new System.Exception($"Cannot export from {exportOptions.TypeDescription} to bitmap."); } } else { if (exportOptions is BitmapExportOptions) { var topoArray = propagator.ToValueArray(Rgba32.Gray, Rgba32.Magenta); var bitmap = BitmapUtils.ToBitmap(topoArray.ToArray2d()); bitmap.Save(filename); } else if (exportOptions is BitmapSetExportOptions bseo) { var undecided = new Tile(new object()); var contradiction = new Tile(new object()); var topoArray = propagator.ToArray(undecided, contradiction); var tileTopology = topoArray.Topology.AsGridTopology().WithSize(bseo.TileWidth, bseo.TileHeight, 1); var subTiles = bseo.Bitmaps.ToDictionary(x => x.Key, x => TopoArray.Create(BitmapUtils.ToColorArray(x.Value), tileTopology)); subTiles[undecided] = TopoArray.FromConstant(Rgba32.Gray, tileTopology); subTiles[contradiction] = TopoArray.FromConstant(Rgba32.Magenta, tileTopology); var exploded = MoreTopoArrayUtils.ExplodeTiles(topoArray, subTiles, bseo.TileWidth, bseo.TileHeight, 1); var bitmap = BitmapUtils.ToBitmap(exploded.ToArray2d()); bitmap.Save(filename); } else { throw new System.Exception($"Cannot export from {exportOptions.TypeDescription} to bitmap."); } } }
public SampleSet Load(string filename) { var data = new List <List <List <Tile> > >(); data.Add(new List <List <Tile> >()); foreach (var line in File.ReadLines(filename)) { if (string.IsNullOrWhiteSpace(line)) { data.Add(new List <List <Tile> >()); continue; } var row = line.Split(",").Select(Parse).ToList(); data[data.Count - 1].Add(row); } var width = data[0][0].Count; var height = data[0].Count; var depth = data.Count; var dataArray = new Tile[width, height, depth]; for (var x = 0; x < width; x++) { for (var y = 0; y < height; y++) { for (var z = 0; z < depth; z++) { dataArray[x, y, z] = data[z][y][x]; } } } var sample = TopoArray.Create(dataArray, false); return(new SampleSet { Directions = DirectionSet.Cartesian3d, Samples = new[] { sample }, }); }
public SampleSet Load(string filename) { Image <Rgba32> bitmap; try { bitmap = Image.Load(filename); } catch (ArgumentException) { throw new Exception($"Couldn't load filename: {filename}"); } var colorArray = BitmapUtils.ToColorArray(bitmap); var topology = new GridTopology(DirectionSet.Cartesian2d, colorArray.GetLength(0), colorArray.GetLength(1), false, false); return(new SampleSet { Directions = DirectionSet.Cartesian2d, Samples = new[] { TopoArray.Create(colorArray, topology).ToTiles() }, ExportOptions = new BitmapExportOptions(), }); }
public void TestHexRotate() { var a = new int[2, 2]; a[0, 0] = 1; a[1, 0] = 2; a[0, 1] = 3; a[1, 1] = 4; var ta = TopoArray.Create(a, new Topology(DirectionSet.Hexagonal2d, 2, 2, false, false)); var r5 = TopoArrayUtils.HexRotate(ta, new Rotation(5 * 60, false)); Assert.AreEqual(2, r5.Get(0, 0)); Assert.AreEqual(1, r5.Get(0, 1)); Assert.AreEqual(4, r5.Get(1, 1)); Assert.AreEqual(3, r5.Get(1, 2)); var r1 = TopoArrayUtils.HexRotate(ta, new Rotation(1 * 60, false)); Assert.AreEqual(3, r1.Get(0, 0)); Assert.AreEqual(1, r1.Get(1, 0)); Assert.AreEqual(4, r1.Get(1, 1)); Assert.AreEqual(2, r1.Get(2, 1)); var r2 = TopoArrayUtils.HexRotate(ta, new Rotation(2 * 60, false)); Assert.AreEqual(3, r2.Get(0, 0)); Assert.AreEqual(4, r2.Get(0, 1)); Assert.AreEqual(1, r2.Get(1, 1)); Assert.AreEqual(2, r2.Get(1, 2)); var r3 = TopoArrayUtils.HexRotate(ta, new Rotation(3 * 60, false)); Assert.AreEqual(4, r3.Get(0, 0)); Assert.AreEqual(3, r3.Get(1, 0)); Assert.AreEqual(2, r3.Get(0, 1)); Assert.AreEqual(1, r3.Get(1, 1)); var refl = TopoArrayUtils.HexRotate(ta, new Rotation(0 * 60, true)); Assert.AreEqual(2, refl.Get(0, 0)); Assert.AreEqual(1, refl.Get(1, 0)); Assert.AreEqual(4, refl.Get(1, 1)); Assert.AreEqual(3, refl.Get(2, 1)); }
public void TestRotate() { var a = new int[2, 2]; a[0, 0] = 1; a[1, 0] = 2; a[0, 1] = 3; a[1, 1] = 4; var ta = TopoArray.Create(a, new Topology(2, 2, false)); var r1 = TopoArrayUtils.Rotate(ta, new Rotation(3 * 90)); Assert.AreEqual(2, r1.Get(0, 0)); Assert.AreEqual(4, r1.Get(1, 0)); Assert.AreEqual(1, r1.Get(0, 1)); Assert.AreEqual(3, r1.Get(1, 1)); var r3 = TopoArrayUtils.Rotate(ta, new Rotation(1 * 90)); Assert.AreEqual(3, r3.Get(0, 0)); Assert.AreEqual(1, r3.Get(1, 0)); Assert.AreEqual(4, r3.Get(0, 1)); Assert.AreEqual(2, r3.Get(1, 1)); var refl = TopoArrayUtils.Rotate(ta, new Rotation(0 * 90, true)); Assert.AreEqual(2, refl.Get(0, 0)); Assert.AreEqual(1, refl.Get(1, 0)); Assert.AreEqual(4, refl.Get(0, 1)); Assert.AreEqual(3, refl.Get(1, 1)); }
public DeBroglieBuilder Build(string src, int width, int height, int depth) { var file = new File(); file.Open(SampleSrc, (int)File.ModeFlags.Read); var sampleData = JsonConvert.DeserializeObject <DeBroglieSampleData>(file.GetAsText()); int sampleWidth = sampleData.Dimensions.X; int sampleHeight = sampleData.Dimensions.Y; int sampleDepth = sampleData.Dimensions.Z; var sample = TopoArray.Create <int>( p => sampleData.Sample[ p.Z * sampleWidth * sampleHeight + p.Y * sampleWidth + p.X ], new Topology(sampleWidth, sampleHeight, sampleDepth, sampleData.Periodic) ); DeBroglie.Models.TileModel model = null; switch (sampleData.Model) { case DeBroglieSampleData.ModelType.Adjacent: model = new DeBroglie.Models.AdjacentModel(sample.ToTiles()); break; case DeBroglieSampleData.ModelType.Overlapping: model = new DeBroglie.Models.OverlappingModel(sampleData.N); break; } var propogator = new DeBroglie.TilePropagator(model, sample.Topology, false); var status = propogator.Run(); if (status != DeBroglie.Resolution.Decided) { throw new Exception("Undecided"); } LostGen.Point.ForEachXYZ(Point.Zero, new Point(width, height, depth), p => { int tileIndex = sample.Topology.GetIndex(p.X, p.Y, p.Z); var tile = sampleData.Tiles[tileIndex]; int meshIndex; if (int.TryParse(tile.Src, out meshIndex)) { // If the tile Src is an int, it's referring to an index in the MeshLibrary SetCellItem(p.X, p.Y, p.Z, meshIndex, tile.Orientation); } else { // Otherwise, assume Src is a resource path pointing to another sample set var subBuilder = new DeBroglieBuilder(); AddChild(subBuilder); // Shrink the new builder and offset so that it fits in the larger GridMap subBuilder.SetScale(Vector3.One / tile.Size); subBuilder.Translate(new Vector3( p.X * CellSize.x, p.Y * CellSize.y, p.Z * CellSize.z )); subBuilder.Build(tile.Src, tile.Size, tile.Size, tile.Size); } } ); return(this); }
internal override TileModelMapping GetTileModelMapping(Topology topology) { var patternModel = new PatternModel { Propagator = propagator.Select(x => x.Select(y => y.ToArray()).ToArray()).ToArray(), Frequencies = frequencies.ToArray(), }; Topology patternTopology; Dictionary <int, IReadOnlyDictionary <Tile, ISet <int> > > tilesToPatternsByOffset; Dictionary <int, IReadOnlyDictionary <int, Tile> > patternsToTilesByOffset; ITopoArray <(Point, int)> tileCoordToPatternCoord; if (!(topology.PeriodicX && topology.PeriodicY && topology.PeriodicZ)) { // Shrink the topology as patterns can cover multiple tiles. patternTopology = topology.WithSize( topology.PeriodicX ? topology.Width : topology.Width - NX + 1, topology.PeriodicY ? topology.Height : topology.Height - NY + 1, topology.PeriodicZ ? topology.Depth : topology.Depth - NZ + 1); void OverlapCoord(int x, int width, out int px, out int ox) { if (x < width) { px = x; ox = 0; } else { px = width - 1; ox = x - px; } } int CombineOffsets(int ox, int oy, int oz) { return(ox + oy * NX + oz * NX * NY); } (Point, int) Map(Point t) { OverlapCoord(t.X, patternTopology.Width, out var px, out var ox); OverlapCoord(t.Y, patternTopology.Height, out var py, out var oy); OverlapCoord(t.Z, patternTopology.Depth, out var pz, out var oz); return(new Point(px, py, pz), CombineOffsets(ox, oy, oz)); } tileCoordToPatternCoord = TopoArray.Create(Map, topology); // Compute tilesToPatterns and patternsToTiles tilesToPatternsByOffset = new Dictionary <int, IReadOnlyDictionary <Tile, ISet <int> > >(); patternsToTilesByOffset = new Dictionary <int, IReadOnlyDictionary <int, Tile> >(); for (int ox = 0; ox < NX; ox++) { for (int oy = 0; oy < NY; oy++) { for (int oz = 0; oz < NZ; oz++) { var o = CombineOffsets(ox, oy, oz); var tilesToPatterns = new Dictionary <Tile, ISet <int> >(); tilesToPatternsByOffset[o] = tilesToPatterns; var patternsToTiles = new Dictionary <int, Tile>(); patternsToTilesByOffset[o] = patternsToTiles; for (var pattern = 0; pattern < patternArrays.Count; pattern++) { var patternArray = patternArrays[pattern]; var tile = patternArray.Values[ox, oy, oz]; patternsToTiles[pattern] = tile; if (!tilesToPatterns.TryGetValue(tile, out var patternSet)) { patternSet = tilesToPatterns[tile] = new HashSet <int>(); } patternSet.Add(pattern); } } } } } else { patternTopology = topology; tileCoordToPatternCoord = null; tilesToPatternsByOffset = new Dictionary <int, IReadOnlyDictionary <Tile, ISet <int> > >() { { 0, tilesToPatterns.ToDictionary(g => g.Key, g => (ISet <int>) new HashSet <int>(g)) } }; patternsToTilesByOffset = new Dictionary <int, IReadOnlyDictionary <int, Tile> > { { 0, patternsToTiles }, }; } // Masks interact a bit weirdly with the overlapping model // We choose a pattern mask that is a expansion of the topology mask // i.e. a pattern location is masked out if all the tile locations it covers is masked out. // This makes the propagator a bit conservative - it'll always preserve the overlapping property // but might ban some layouts that make sense. // The alternative is to contract the mask - that is more permissive, but sometimes will // violate the overlapping property. // (passing the mask verbatim is unacceptable as does not lead to symmetric behaviour) // See TestTileMaskWithThinOverlapping for an example of the problem, and // https://github.com/BorisTheBrave/DeBroglie/issues/7 for a possible solution. if (topology.Mask != null) { // TODO: This could probably do with some cleanup bool GetTopologyMask(int x, int y, int z) { if (!topology.PeriodicX && x >= topology.Width) { return(false); } if (!topology.PeriodicY && y >= topology.Height) { return(false); } if (!topology.PeriodicZ && z >= topology.Depth) { return(false); } x = x % topology.Width; y = y % topology.Height; z = z % topology.Depth; return(topology.Mask[topology.GetIndex(x, y, z)]); } bool GetPatternTopologyMask(Point p) { for (var oz = 0; oz < NZ; oz++) { for (var oy = 0; oy < NY; oy++) { for (var ox = 0; ox < NX; ox++) { if (GetTopologyMask(p.X + ox, p.Y + oy, p.Z + oz)) { return(true); } } } } return(false); } var patternMask = TopoArray.Create(GetPatternTopologyMask, patternTopology); patternTopology = patternTopology.WithMask(patternMask); } return(new TileModelMapping { PatternModel = patternModel, PatternsToTilesByOffset = patternsToTilesByOffset, TilesToPatternsByOffset = tilesToPatternsByOffset, PatternTopology = patternTopology, TileCoordToPatternCoord = tileCoordToPatternCoord, }); }
public static ITopoArray <IEnumerable <V> > ExplodeSets <U, V>(ITopoArray <ISet <U> > topoArray, Func <U, ITopoArray <V> > getSubTile, int tileWidth, int tileHeight, int tileDepth) { if (topoArray.Topology.Directions.Type != DirectionsType.Cartesian2d && topoArray.Topology.Directions.Type != DirectionsType.Cartesian3d) { throw new NotImplementedException(); } var inTopology = topoArray.Topology; var inWidth = inTopology.Width; var inHeight = inTopology.Height; var inDepth = inTopology.Depth; var resultTopology = inTopology.WithSize( inWidth * tileWidth, inHeight * tileHeight, inDepth * tileDepth ); var result = new IEnumerable <V> [resultTopology.Width, resultTopology.Height, resultTopology.Depth]; var mask = new bool[resultTopology.Width * resultTopology.Height * resultTopology.Depth]; for (var z = 0; z < inDepth; z++) { for (var y = 0; y < inHeight; y++) { for (var x = 0; x < inWidth; x++) { if (inTopology.Mask != null) { var index = inTopology.GetIndex(x, y, z); if (!inTopology.Mask[index]) { continue; } } var inTileSet = topoArray.Get(x, y, z); if (inTileSet.Count == 0) { continue; } for (var tz = 0; tz < tileDepth; tz++) { for (var ty = 0; ty < tileHeight; ty++) { for (var tx = 0; tx < tileWidth; tx++) { var outSet = new List <V>(); foreach (var inTile in inTileSet) { var subTile = getSubTile(inTile); if (subTile == null) { continue; } if (subTile.Topology.Mask != null) { var index = subTile.Topology.GetIndex(tx, ty, tz); if (!subTile.Topology.Mask[index]) { continue; } } outSet.Add(subTile.Get(tx, ty, tz)); mask[resultTopology.GetIndex(x * tileWidth + tx, y * tileHeight + ty, z * tileDepth + tz)] = true; } result[x * tileWidth + tx, y *tileHeight + ty, z *tileDepth + tz] = outSet; } } } } } } return(TopoArray.Create(result, resultTopology)); }
public void Export(TileModel model, TilePropagator propagator, string filename, DeBroglieConfig config, ExportOptions exportOptions) { if (config.Animate) { if (exportOptions is BitmapExportOptions) { var topoArray = propagator.ToValueSets <Rgba32>().Map(BitmapUtils.ColorAverage); var bitmap = BitmapUtils.ToBitmap(topoArray.ToArray2d()); bitmap.Save(filename); } else if (exportOptions is BitmapSetExportOptions bseo) { var topoArray = propagator.ToArraySets(); var tileTopology = topoArray.Topology.AsGridTopology().WithSize(bseo.TileWidth, bseo.TileHeight, 1); var subTiles = bseo.Bitmaps.ToDictionary(x => x.Key, x => TopoArray.Create(BitmapUtils.ToColorArray(x.Value), tileTopology)); var exploded = MoreTopoArrayUtils.ExplodeTileSets(topoArray, subTiles, bseo.TileWidth, bseo.TileHeight, 1).Map(BitmapUtils.ColorAverage); var bitmap = BitmapUtils.ToBitmap(exploded.ToArray2d()); bitmap.Save(filename); } else { throw new System.Exception($"Cannot export from {exportOptions.TypeDescription} to bitmap."); } } else { if (exportOptions is BitmapExportOptions) { var topoArray = propagator.ToValueArray(Rgba32.Gray, Rgba32.Magenta); var bitmap = BitmapUtils.ToBitmap(topoArray.ToArray2d()); var index = 0; var noExtension = Path.GetFileNameWithoutExtension(filename); var justExtension = Path.GetExtension(filename); var pathNoFile = Path.GetDirectoryName(filename); var seqFilename = ""; do { seqFilename = pathNoFile + Path.DirectorySeparatorChar + noExtension + (index == 0 ? "" : index.ToString()) + justExtension; index++; } while (File.Exists(seqFilename)); bitmap.Save(seqFilename); } else if (exportOptions is BitmapSetExportOptions bseo) { var undecided = new Tile(new object()); var contradiction = new Tile(new object()); var topoArray = propagator.ToArray(undecided, contradiction); var tileTopology = topoArray.Topology.AsGridTopology().WithSize(bseo.TileWidth, bseo.TileHeight, 1); var subTiles = bseo.Bitmaps.ToDictionary(x => x.Key, x => TopoArray.Create(BitmapUtils.ToColorArray(x.Value), tileTopology)); subTiles[undecided] = TopoArray.FromConstant(Rgba32.Gray, tileTopology); subTiles[contradiction] = TopoArray.FromConstant(Rgba32.Magenta, tileTopology); var exploded = MoreTopoArrayUtils.ExplodeTiles(topoArray, subTiles, bseo.TileWidth, bseo.TileHeight, 1); var bitmap = BitmapUtils.ToBitmap(exploded.ToArray2d()); bitmap.Save(filename); } else { throw new System.Exception($"Cannot export from {exportOptions.TypeDescription} to bitmap."); } } }
private static IDictionary <Tile, ITopoArray <Rgba32> > GetSubTiles(BitmapSetExportOptions bseo, out GridTopology subTileTopology) { var t = subTileTopology = new GridTopology(bseo.TileWidth, bseo.TileHeight, false); return(bseo.Bitmaps.ToDictionary(x => x.Key, x => TopoArray.Create(BitmapUtils.ToColorArray(x.Value), t))); }
/// <summary> /// Constructs a TilePropagator. /// </summary> /// <param name="tileModel">The model to guide the generation.</param> /// <param name="topology">The dimensions of the output to generate</param> /// <param name="backtrack">If true, store additional information to allow rolling back choices that lead to a contradiction.</param> /// <param name="constraints">Extra constraints to control the generation process.</param> /// <param name="random">Source of randomness</param> public TilePropagator(TileModel tileModel, Topology topology, bool backtrack = false, ITileConstraint[] constraints = null, Random random = null) { this.tileModel = tileModel; this.topology = topology; var overlapping = tileModel as OverlappingModel; Topology patternTopology; if (!(topology.PeriodicX && topology.PeriodicY && topology.PeriodicZ) && overlapping != null) { // Shrink the topology as patterns can cover multiple tiles. patternTopology = topology.WithSize( topology.PeriodicX ? topology.Width : topology.Width - overlapping.NX + 1, topology.PeriodicY ? topology.Height : topology.Height - overlapping.NY + 1, topology.PeriodicZ ? topology.Depth : topology.Depth - overlapping.NZ + 1); mappingType = MappingType.Overlapping; mappingNX = overlapping.NX; mappingNY = overlapping.NY; mappingNZ = overlapping.NZ; // Compute tilesToPatterns and patternsToTiles var patternArrays = overlapping.PatternArrays; tilesToPatternsByOffset = new Dictionary <int, IReadOnlyDictionary <Tile, ISet <int> > >(); patternsToTilesByOffset = new Dictionary <int, IReadOnlyDictionary <int, Tile> >(); for (int ox = 0; ox < overlapping.NX; ox++) { for (int oy = 0; oy < overlapping.NY; oy++) { for (int oz = 0; oz < overlapping.NZ; oz++) { var o = CombineOffsets(ox, oy, oz); var tilesToPatterns = new Dictionary <Tile, ISet <int> >(); tilesToPatternsByOffset[o] = tilesToPatterns; var patternsToTiles = new Dictionary <int, Tile>(); patternsToTilesByOffset[o] = patternsToTiles; for (var pattern = 0; pattern < patternArrays.Count; pattern++) { var patternArray = patternArrays[pattern]; var tile = patternArray.Values[ox, oy, oz]; patternsToTiles[pattern] = tile; if (!tilesToPatterns.TryGetValue(tile, out var patternSet)) { patternSet = tilesToPatterns[tile] = new HashSet <int>(); } patternSet.Add(pattern); } } } } } else { patternTopology = topology; mappingType = MappingType.OneToOne; tilesToPatternsByOffset = new Dictionary <int, IReadOnlyDictionary <Tile, ISet <int> > >() { { 0, tileModel.TilesToPatterns.ToDictionary(g => g.Key, g => (ISet <int>) new HashSet <int>(g)) } }; patternsToTilesByOffset = new Dictionary <int, IReadOnlyDictionary <int, Tile> > { { 0, tileModel.PatternsToTiles }, }; } // Masks interact a bit weirdly with the overlapping model // We choose a pattern mask that is a expansion of the topology mask // i.e. a pattern location is masked out if all the tile locations it covers is masked out. // This makes the propagator a bit conservative - it'll always preserve the overlapping property // but might ban some layouts that make sense. // The alternative is to contract the mask - that is more permissive, but sometimes will // violate the overlapping property. // (passing the mask verbatim is unacceptable as does not lead to symmetric behaviour) // See TestTileMaskWithThinOverlapping for an example of the problem, and // https://github.com/BorisTheBrave/DeBroglie/issues/7 for a possible solution. if (topology.Mask != null && overlapping != null) { // TODO: This could probably do with some cleanup bool GetTopologyMask(int x, int y, int z) { if (!topology.PeriodicX && x >= topology.Width) { return(false); } if (!topology.PeriodicY && y >= topology.Height) { return(false); } if (!topology.PeriodicZ && z >= topology.Depth) { return(false); } x = x % topology.Width; y = y % topology.Height; z = z % topology.Depth; return(topology.Mask[topology.GetIndex(x, y, z)]); } bool GetPatternTopologyMask(Point p) { for (var oz = 0; oz < overlapping.NZ; oz++) { for (var oy = 0; oy < overlapping.NY; oy++) { for (var ox = 0; ox < overlapping.NX; ox++) { if (GetTopologyMask(p.X + ox, p.Y + oy, p.Z + oz)) { return(true); } } } } return(false); } var patternMask = TopoArray.Create(GetPatternTopologyMask, patternTopology); patternTopology = patternTopology.WithMask(patternMask); } var waveConstraints = (constraints?.Select(x => new TileConstraintAdaptor(x, this)).ToArray() ?? Enumerable.Empty <IWaveConstraint>()) .ToArray(); this.wavePropagator = new WavePropagator(tileModel.GetPatternModel(), patternTopology, backtrack, waveConstraints, random, clear: false); wavePropagator.Clear(); }
public SampleSet Load(string filename) { var srcFilename = filename; var map = TiledUtil.Load(filename); // Scan tilesets for tiles with a custom property "name" var tilesByName = new Dictionary <string, Tile>(); foreach (var tileset in map.Tilesets) { AddTileset(tilesByName, tileset); } ITopoArray <Tile> sample; if (map.Orientation == Orientation.hexagonal) { // Read a single layer var layer = (TileLayer)map.Layers[0]; sample = TiledUtil.ReadLayer(map, layer); } else { // Read all the layers into a 3d array. var tileLayers = map.Layers .Where(x => x is TileLayer) .Cast <TileLayer>() .ToList(); Tile[,,] results = null; GridTopology topology = null; for (var z = 0; z < tileLayers.Count; z++) { var layer = tileLayers[z]; var layerArray = TiledUtil.ReadLayer(map, layer); if (z == 0) { topology = layerArray.Topology.AsGridTopology(); results = new Tile[topology.Width, topology.Height, tileLayers.Count]; } for (var y = 0; y < topology.Height; y++) { for (var x = 0; x < topology.Width; x++) { results[x, y, z] = layerArray.Get(x, y); } } } if (tileLayers.Count > 1 && topology.Directions.Type == DirectionSetType.Cartesian2d) { topology = new GridTopology(DirectionSet.Cartesian3d, map.Width, map.Height, tileLayers.Count, false, false, false); } else { topology = new GridTopology(topology.Directions, topology.Width, topology.Height, tileLayers.Count, false, false, false); } sample = TopoArray.Create(results, topology); } return(new SampleSet { Directions = sample.Topology.AsGridTopology().Directions, Samples = new[] { sample }, TilesByName = tilesByName, ExportOptions = new TiledExportOptions { Template = map, SrcFileName = srcFilename, }, }); }
private static ITopoArray <T> WithPeriodic <T>(ITopoArray <T> topoArray, bool periodicX, bool periodicY, bool periodicZ = false) { return(TopoArray.Create(topoArray.ToArray3d(), topoArray.Topology.AsGridTopology().WithPeriodic(periodicX, periodicY, periodicZ))); }
public static ITopoArray <V> Explode <U, V>(ITopoArray <U> topoArray, Func <U, ITopoArray <V> > getSubTile, int tileWidth, int tileHeight, int tileDepth) { var inTopology = topoArray.Topology.AsGridTopology(); if (inTopology.Directions.Type != DirectionSetType.Cartesian2d && inTopology.Directions.Type != DirectionSetType.Cartesian3d) { throw new NotImplementedException(); } var inWidth = inTopology.Width; var inHeight = inTopology.Height; var inDepth = inTopology.Depth; var resultTopology = inTopology.WithSize( inWidth * tileWidth, inHeight * tileHeight, inDepth * tileDepth ); var result = new V[resultTopology.Width, resultTopology.Height, resultTopology.Depth]; var mask = new bool[resultTopology.Width * resultTopology.Height * resultTopology.Depth]; for (var z = 0; z < inDepth; z++) { for (var y = 0; y < inHeight; y++) { for (var x = 0; x < inWidth; x++) { if (inTopology.Mask != null) { var index = inTopology.GetIndex(x, y, z); if (!inTopology.Mask[index]) { continue; } } var inTile = topoArray.Get(x, y, z); var subTile = getSubTile(inTile); if (subTile == null) { continue; } for (var tz = 0; tz < tileDepth; tz++) { for (var ty = 0; ty < tileHeight; ty++) { for (var tx = 0; tx < tileWidth; tx++) { if (subTile.Topology.Mask != null) { var index = subTile.Topology.GetIndex(tx, ty, tz); if (!subTile.Topology.Mask[index]) { continue; } } result[x * tileWidth + tx, y *tileHeight + ty, z *tileDepth + tz] = subTile.Get(tx, ty, tz); mask[resultTopology.GetIndex(x * tileWidth + tx, y * tileHeight + ty, z * tileDepth + tz)] = true; } } } } } } return(TopoArray.Create(result, resultTopology)); }