/// <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(); }
/// <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 patternTopology = topology; if (!(topology.PeriodicX && topology.PeriodicY && topology.PeriodicZ) && tileModel is OverlappingModel overlapping) { // 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 { 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 }, }; } 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(); }