private async Task <IEnumerable <Point> > ApplyFloodFillAsync(
            Pixel[,] bitmap,
            Point startPoint,
            Func <Pixel, bool> boundaryFunc)
        {
            var fastBitmap = (Pixel[, ])bitmap.Clone();

            return(await Task.Run(delegate
            {
                Stack <Point> pixels = new Stack <Point>();
                pixels.Push(startPoint);
                var marked = new Dictionary <Point, byte>();
                while (pixels.Count > 0)
                {
                    Point a = pixels.Pop();
                    if (a.X < bitmap.GetLength(0) && a.X > -1 && a.Y < bitmap.GetLength(1) && a.Y > -1)
                    {
                        var pixel = fastBitmap[a.X, a.Y];
                        if (boundaryFunc(pixel) && !marked.ContainsKey(a))
                        {
                            marked.Add(a, pixel.G);
                            pixels.Push(new Point(a.X - 1, a.Y));
                            pixels.Push(new Point(a.X + 1, a.Y));
                            pixels.Push(new Point(a.X, a.Y - 1));
                            pixels.Push(new Point(a.X, a.Y + 1));
                        }
                    }
                }
                return marked.Keys;
            }));
        }
Example #2
0
        public PinnedBitmapData Pinned(BitmapOrientation orientation = BitmapOrientation.Normal)
        {
            var data = (Pixel[, ])_data.Clone();

            // TODO remove this hack once the OpenGL display is more sophisticated that DrawPixels
            if (orientation == BitmapOrientation.FlipY)
            {
                for (var y = 0; y < Height; y++)
                {
                    for (var x = 0; x < Width; x++)
                    {
                        data[y, x] = _data[Height - y - 1, x];
                    }
                }
            }

            return(new PinnedBitmapData(data));
        }
        public async Task <SettlementInfo> BuildAsync()
        {
            TerrainHelper.SetTerrains(_heightMap);
            var minHeight = _terrainHelper.GetTerrain <Sand>().UpperBound;
            var maxHeight = _terrainHelper.GetTerrain <Lowland>().UpperBound;

            #region find water aquens
            byte waterUpperBound        = _terrainHelper.GetTerrain <Water>().UpperBound;
            var  map                    = (Pixel[, ])_heightMap.Clone();
            var  waterAreasBoundaryFunc = new Func <Pixel, bool>(p => p.Intensity <= waterUpperBound);
            var  waterAreas             = new List <IEnumerable <Point> >();
            var  potentialWaterPoints   =
                GetPixels(map, waterAreasBoundaryFunc).ToList();

            while (potentialWaterPoints.Count > 0)
            {
                var area = await ApplyFloodFillAsync(
                    map,
                    potentialWaterPoints.First(),
                    waterAreasBoundaryFunc);

                potentialWaterPoints.RemoveAll(p => area.Contains(p));
                waterAreas.Add(area);
            }
            var maxWaterArea = waterAreas.Max(w => w.Count());
            waterAreas.RemoveAll(w => w.Count() < 0.4 * maxWaterArea);
            #endregion

            #region find settlement areas
            var previewBitmap = (Pixel[, ])_heightMap.Clone();
            var settlementAreaBoundaryFunc = new Func <Pixel, bool>(color => color.G >= minHeight && color.G <= maxHeight);
            var areas = new List <IEnumerable <Point> >();
            var potentialAreaPoints = GetPixels(previewBitmap, settlementAreaBoundaryFunc)
                                      .ToList();

            while (potentialAreaPoints.Count > 0)
            {
                var area = await ApplyFloodFillAsync(
                    previewBitmap,
                    potentialAreaPoints.First(),
                    settlementAreaBoundaryFunc);

                potentialAreaPoints.RemoveAll(p => area.Contains(p));
                areas.Add(area);
            }

            var selectedArea = areas.OrderByDescending(a => a.Count()).First().ToList();
            #endregion

            var builderHelper = new BuilderHelper();
            var waterMatrix   = new int[map.GetLength(1), map.GetLength(1)];
            foreach (var point in waterAreas.SelectMany(w => w))
            {
                waterMatrix[point.Y, point.X] = 1;
            }

            var boundaryPoints          = builderHelper.GetBoundaryPoints(waterMatrix).OrderBy(p => p.X).ThenBy(p => p.Y).ToList();
            var nStep                   = boundaryPoints.Count() / (100 * waterAreas.Count);
            var waterAreaBoundaryPoints = boundaryPoints.Where((x, i) => i % nStep == 0).ToList();

            var fields = selectedArea.Select(a => new
            {
                Point           = a,
                DistanceToWater = waterAreaBoundaryPoints.Min(w => builderHelper.DistanceTo(w, a))
            }).OrderBy(f => f.DistanceToWater).ToList();

            #region mark settlement area and water aquens on bitmap
            fields.Take((int)(fields.Count * 0.2)).ToList()
            .ForEach(p => MarkPoint(p.Point, previewBitmap, new Pixel(255, 0, 0), 1));
            fields.Skip((int)(fields.Count * 0.2)).Take((int)(fields.Count * 0.3)).ToList()
            .ForEach(p => MarkPoint(p.Point, previewBitmap, new Pixel(200, 0, 0), 1));
            fields.Skip((int)(fields.Count * 0.5)).ToList()
            .ForEach(p => MarkPoint(p.Point, previewBitmap, new Pixel(155, 0, 0), 1));

            waterAreas.ForEach(w => w.ToList().ForEach(p => MarkPoint(p, previewBitmap, new Pixel(0, 0, 255), 1)));

            //waterAreaBoundaryPoints.ForEach(p => { MarkPoint(p, previewBitmap, new Pixel(0, 255, 0), 3); });

            var fieldGrid = new Field[_heightMap.GetLength(0), _heightMap.GetLength(1)];
            for (int i = 0; i < _heightMap.GetLength(0); i++)
            {
                for (int j = 0; j < _heightMap.GetLength(1); j++)
                {
                    fieldGrid[i, j] = new Field()
                    {
                        Position = new Point(i, j),
                        Terrain  = _terrainHelper.GetTerrainForHeight(_heightMap[i, j].Intensity)
                    };
                }
            }
            foreach (var field in fields)
            {
                fieldGrid[field.Point.X, field.Point.Y].InSettlement    = true;
                fieldGrid[field.Point.X, field.Point.Y].DistanceToWater = field.DistanceToWater;
            }

            var rand           = new Random();
            var verticalRoad   = rand.NextDouble() >= 0.5;
            var min            = verticalRoad ? fields.Min(f => f.Point.Y) : fields.Min(f => f.Point.X);
            var max            = verticalRoad ? fields.Max(f => f.Point.Y) : fields.Max(f => f.Point.X);
            var startFields    = verticalRoad ? fields.Where(f => f.Point.Y == min).ToArray() : fields.Where(f => f.Point.X == min).ToArray();
            var start          = startFields[rand.Next(0, startFields.Count())];
            var endFields      = verticalRoad ? fields.Where(f => f.Point.Y == max).ToArray() : fields.Where(f => f.Point.X == max).ToArray();
            var end            = endFields[rand.Next(0, endFields.Count())];
            var mainRoadPoints = (await FindMainRoad(fieldGrid, start.Point, end.Point)).ToList();
            mainRoadPoints.ForEach(p => { MarkPoint(new Point(p.X, p.Y), previewBitmap, new Pixel(0, 255, 255), 2); });
            #endregion

            var mStep = mainRoadPoints.Count() / 100;
            var selectedRoadPoints = mainRoadPoints.OrderBy(p => p.X).ThenBy(p => p.Y).Where((x, i) => i % mStep == 0).ToList();
            foreach (var field in fields)
            {
                fieldGrid[field.Point.X, field.Point.Y].DistanceToMainRoad =
                    selectedRoadPoints.Min(p => builderHelper.DistanceTo(field.Point, p));
            }

            var settlementInfo = new SettlementInfo()
            {
                PreviewBitmap = previewBitmap,
                MainRoad      = mainRoadPoints,
                Fields        = fieldGrid
            };
            return(settlementInfo);
        }