예제 #1
0
    /// <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);
    }
예제 #2
0
        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));
        }
예제 #3
0
        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();
            }
        }
예제 #4
0
        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);
        }
예제 #5
0
        /// <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");
            }
        }
예제 #6
0
        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));
        }
예제 #7
0
        public void TestPriority()
        {
            var t1    = new Tile(1);
            var t2    = new Tile(2);
            var t3    = new Tile(3);
            var model = new AdjacentModel(DirectionSet.Cartesian2d);

            model.AddAdjacency(t1, t1, Direction.XPlus);
            model.AddAdjacency(t1, t2, Direction.XPlus);
            model.AddAdjacency(t2, t2, Direction.XPlus);
            model.AddAdjacency(t2, t3, Direction.XPlus);
            model.AddAdjacency(t3, t3, Direction.XPlus);

            model.SetUniformFrequency();

            var topology = new GridTopology(6, 1, false).WithMask(new bool[] { true, true, true, true, true, false });

            IDictionary <Tile, PriorityAndWeight> weights = new Dictionary <Tile, PriorityAndWeight>
            {
                { t1, new PriorityAndWeight {
                      Priority = 0, Weight = 1
                  } },
                { t2, new PriorityAndWeight {
                      Priority = 1, Weight = 1
                  } },
                { t3, new PriorityAndWeight {
                      Priority = 2, Weight = 1
                  } },
            };

            var weightsArray = TopoArray.CreateByIndex(_ => weights, topology);

            var propagator = new TilePropagator(model, topology, new TilePropagatorOptions
            {
                IndexPickerType  = IndexPickerType.ArrayPriorityMinEntropy,
                WeightSetByIndex = TopoArray.CreateByIndex(_ => 0, topology),
                WeightSets       = new Dictionary <int, IDictionary <Tile, PriorityAndWeight> > {
                    { 0, weights }
                },
            });

            propagator.Select(0, 0, 0, t1);

            propagator.Run();

            Assert.AreEqual(Resolution.Decided, propagator.Status);

            var r = propagator.ToValueArray <int>();

            Assert.AreEqual(1, r.Get(0, 0));
            Assert.AreEqual(2, r.Get(1, 0));
            Assert.AreEqual(3, r.Get(2, 0));
            Assert.AreEqual(3, r.Get(3, 0));
        }
예제 #8
0
        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.");
                }
            }
        }
예제 #9
0
        /**
         * Returns an array where each cell is a list of remaining possible patterns.
         */
        public ITopoArray <ISet <int> > ToTopoArraySets()
        {
            return(TopoArray.CreateByIndex(index =>
            {
                var hs = new HashSet <int>();
                for (var pattern = 0; pattern < patternCount; pattern++)
                {
                    if (wave.Get(index, pattern))
                    {
                        hs.Add(pattern);
                    }
                }

                return (ISet <int>)(hs);
            }, topology));
        }
예제 #10
0
        public void TestDirtyIndexPicker()
        {
            var t1    = new Tile(1);
            var t2    = new Tile(2);
            var t3    = new Tile(3);
            var model = new AdjacentModel(DirectionSet.Cartesian2d);

            model.AddAdjacency(t1, t1, Direction.XPlus);
            model.AddAdjacency(t1, t2, Direction.XPlus);
            model.AddAdjacency(t2, t2, Direction.XPlus);
            model.AddAdjacency(t2, t3, Direction.XPlus);
            model.AddAdjacency(t3, t3, Direction.XPlus);
            model.AddAdjacency(t3, t2, Direction.XPlus);
            model.AddAdjacency(t2, t1, Direction.XPlus);

            model.SetUniformFrequency();

            var topology = new GridTopology(6, 1, false);

            var options = new TilePropagatorOptions
            {
                IndexPickerType = IndexPickerType.Dirty,
                TilePickerType  = TilePickerType.Ordered,
                CleanTiles      = TopoArray.FromConstant(t1, topology),
            };

            var propagator = new TilePropagator(model, topology, options);

            propagator.Select(3, 0, 0, t3);

            propagator.Run();

            var a = propagator.ToValueArray <int?>();

            Assert.AreEqual(null, a.Get(0, 0));
            Assert.AreEqual(null, a.Get(1, 0));
            Assert.AreEqual(2, a.Get(2, 0));
            Assert.AreEqual(3, a.Get(3, 0));
            Assert.AreEqual(2, a.Get(4, 0));
            Assert.AreEqual(null, a.Get(5, 0));
        }
예제 #11
0
        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 },
            });
        }
예제 #12
0
        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(),
            });
        }
예제 #13
0
        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));
        }
예제 #14
0
        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));
        }
예제 #15
0
 public ITopoArray <IDictionary <Tile, double> > ToWeightedArraySets()
 {
     return(TopoArray.CreateByIndex(GetWeightedTiles, topology));
 }
예제 #16
0
        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,
            });
        }
예제 #17
0
        internal override TileModelMapping GetTileModelMapping(ITopology topology)
        {
            var gridTopology = topology.AsGridTopology();
            var patternModel = new PatternModel
            {
                Propagator  = propagator.Select(x => x.Select(y => y.ToArray()).ToArray()).ToArray(),
                Frequencies = frequencies.ToArray(),
            };

            GridTopology patternTopology;
            Dictionary <int, IReadOnlyDictionary <Tile, ISet <int> > > tilesToPatternsByOffset;
            Dictionary <int, IReadOnlyDictionary <int, Tile> >         patternsToTilesByOffset;
            ITopoArray <(Point, int, int)>         tileCoordToPatternCoordIndexAndOffset;
            ITopoArray <List <(Point, int, int)> > patternCoordToTileCoordIndexAndOffset;

            if (!(gridTopology.PeriodicX && gridTopology.PeriodicY && gridTopology.PeriodicZ))
            {
                // Shrink the topology as patterns can cover multiple tiles.
                patternTopology = gridTopology.WithSize(
                    gridTopology.PeriodicX ? topology.Width : topology.Width - NX + 1,
                    gridTopology.PeriodicY ? topology.Height : topology.Height - NY + 1,
                    gridTopology.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, 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);
                    var patternIndex = patternTopology.GetIndex(px, py, pz);

                    return(new Point(px, py, pz), patternIndex, CombineOffsets(ox, oy, oz));
                }

                /*
                 * (Point, int, int) RMap(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);
                 *  var patternIndex = patternTopology.GetIndex(px, py, pz);
                 *  return (new Point(px, py, pz), patternIndex, CombineOffsets(ox, oy, oz));
                 * }
                 */

                tileCoordToPatternCoordIndexAndOffset = TopoArray.CreateByPoint(Map, gridTopology);
                var patternCoordToTileCoordIndexAndOffsetValues = new List <(Point, int, int)> [patternTopology.Width, patternTopology.Height, patternTopology.Depth];
예제 #18
0
        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.");
                }
            }
        }
예제 #19
0
        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));
        }
예제 #20
0
 /**
  * Returns the array of decided patterns, writing
  * -1 or -2 to indicate cells that are undecided or in contradiction.
  */
 public ITopoArray <int> ToTopoArray()
 {
     return(TopoArray.CreateByIndex(GetDecidedPattern, topology));
 }
예제 #21
0
        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));
        }
예제 #22
0
 /// <summary>
 /// Convert the generated result to an array of sets, where each set
 /// indicates the tiles that are still valid at the location.
 /// The size of the set indicates the resolution of that location:
 /// * Greater than 1: <see cref="Resolution.Undecided"/>
 /// * Exactly 1: <see cref="Resolution.Decided"/>
 /// * Exactly 0: <see cref="Resolution.Contradiction"/>
 /// </summary>
 public ITopoArray <ISet <Tile> > ToArraySets()
 {
     return(TopoArray.CreateByIndex(GetPossibleTiles, topology));
 }
예제 #23
0
 /// <summary>
 /// Converts the generated results to an <see cref="ITopoArray{T}"/>,
 /// by extracting the value of each decided tile and
 /// using specific values for locations that have not been decided or are in contradiction.
 /// This is simply a convenience over
 /// The arguments are not relevant if the <see cref="Status"/> is <see cref="Resolution.Decided"/>.
 /// </summary>
 public ITopoArray <T> ToValueArray <T>(T undecided = default(T), T contradiction = default(T))
 {
     return(TopoArray.CreateByIndex(index => GetValue(index, undecided, contradiction), topology));
 }
예제 #24
0
 /// <summary>
 /// Converts the generated results to an <see cref="ITopoArray{Tile}"/>,
 /// using specific tiles for locations that have not been decided or are in contradiction.
 /// The arguments are not relevant if the <see cref="Status"/> is <see cref="Resolution.Decided"/>.
 /// </summary>
 public ITopoArray <Tile> ToArray(Tile undecided = default, Tile contradiction = default)
 {
     return(TopoArray.CreateByIndex(index => GetTile(index, undecided, contradiction), topology));
 }
예제 #25
0
        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)));
        }
예제 #26
0
        /// <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();
        }
예제 #27
0
        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,
                },
            });
        }
예제 #28
0
 /// <summary>
 /// Convert the generated result to an array of sets, where each set
 /// indicates the values of tiles that are still valid at the location.
 /// The size of the set indicates the resolution of that location:
 /// * Greater than 1: <see cref="Resolution.Undecided"/>
 /// * Exactly 1: <see cref="Resolution.Decided"/>
 /// * Exactly 0: <see cref="Resolution.Contradiction"/>
 /// </summary>
 public ITopoArray <ISet <T> > ToValueSets <T>()
 {
     return(TopoArray.CreateByIndex(GetPossibleValues <T>, topology));
 }
예제 #29
0
    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);
    }
예제 #30
0
 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)));
 }