示例#1
0
        private static void BoxBlur(Field2d <float> source, Field2d <float> target, int radius)
        {
            target.Replicate(source);

            BoxBlurHorizontal(target, source, radius);
            BoxBlurVertical(source, target, radius);
        }
示例#2
0
        public static IField2d <float> GetWaterinessMap(WaterTableField wtf, IField2d <float> rainfall, float waterPortability = 5f, float waterinessAttenuation = 20f)
        {
            // Generate "water flow" map using the rainfall map and the water table map, characterizing
            // how much water is in an area based on upstream.  Note that, in the simplistic case of
            // identical universal rainfall, this is just a scalar on depth; this whole shindig is
            // intended to support variable rainfall, as characterized by the rainfall map.
            Field2d <float> waterFlow = new Field2d <float>(rainfall);
            float           maxValue  = float.MinValue;

            foreach (TreeNode <Point2d> waterway in wtf.Waterways)
            {
                List <TreeNode <Point2d> > flattenedReversedRiverTree = new List <TreeNode <Point2d> >(waterway);
                flattenedReversedRiverTree.Reverse();

                foreach (var node in flattenedReversedRiverTree)
                {
                    if (node.parent != null)
                    {
                        Point2d cur = node.value;
                        Point2d par = node.parent.value;

                        waterFlow[par.y, par.x] += waterFlow[cur.y, cur.x];
                    }

                    maxValue = Math.Max(maxValue, waterFlow[node.value.y, node.value.x]);
                }
            }
            IField2d <float> waterinessUnblurred = new FunctionField <float>(waterFlow.Width, waterFlow.Height,
                                                                             (x, y) => 1f / (1f + (float)Math.Pow(Math.E, -waterinessAttenuation * waterFlow[y, x] / maxValue)));

            return(new BlurredField(waterinessUnblurred, waterPortability));
        }
示例#3
0
        private static void BoxBlurHorizontal(Field2d <float> source, Field2d <float> target, int radius)
        {
            // One over the number of values contained in the accumulator, used to
            // compute "rolling average."
            float accNormalizationFactor = 1f / (radius + radius + 1f);

            for (int y = 0; y < source.Height; y++)
            {
                int xTarget    = 0;
                int xFirstSrc  = xTarget;
                int xSecondSrc = xTarget + radius;

                float firstValue = source[y, 0];
                float lastValue  = source[y, source.Width - 1];

                // Seed the accumulator with "radius" + 1 times the leftmost value.
                // This simulates having that value extend infinitely off to the left.
                float acc = (radius + 1f) * firstValue;

                // For the first "radius" values in the row, accumulate the values.
                // No actual blurring can take place until the accumulator has reached
                // the rightmost edge of the relevant box.
                for (var x = 0; x < radius; x++)
                {
                    acc += source[y, x];
                }

                // Begin actual blurring by updating the accumulator, then using the
                // "averaged out" value.  In this way, the accumulator generates a
                // "rolling average."  This loop removes the "firstValue" values
                // artificially added when the accumulator was initialized.
                for (var x = 0; x <= radius; x++)
                {
                    acc += source[y, xSecondSrc++] - firstValue;
                    target[y, xTarget++] = acc * accNormalizationFactor;
                }

                // Continue actual blurring through the center of the image.  This
                // loop commences once the accumulator has been purged of artificial
                // values, and so uses real blurring throughout.
                for (var x = radius + 1; x < source.Width - radius; x++)
                {
                    acc += source[y, xSecondSrc++] - source[y, xFirstSrc++];
                    target[y, xTarget++] = acc * accNormalizationFactor;
                }

                // Finish the actual blurring as the accumulator advances off the
                // right-hand edge of the image.  Use the right-most value as the
                // artificial value to keep the accumulator working properly.
                for (var x = source.Width - radius; x < source.Width; x++)
                {
                    acc += lastValue - source[y, xFirstSrc++];
                    target[y, xTarget++] = acc * accNormalizationFactor;
                }
            }
        }
示例#4
0
        // Gets the exact river heights for the given rectangle. Does NOT produce a valley or canyon kernel.
        private IField2d <float> GetRiverFieldForRectangle(int newWidth, int newHeight, Rectangle sourceRect)
        {
            float metersPerPixel = this.args.metersPerPixel * sourceRect.Width / newWidth;

            var localSplines = GetSplinesInRectangle(sourceRect);

            var riverField = new Field2d <float>(new ConstantField <float>(newWidth, newHeight, float.PositiveInfinity));

            foreach (var s in localSplines)
            {
                var samples = s.GetSamplesPerControlPoint(1f * newWidth / sourceRect.Width);

                int priorX = int.MinValue;
                int priorY = int.MinValue;

                foreach (var p in samples)
                {
                    int x = (int)((p[0] - sourceRect.Left) * newWidth / sourceRect.Width);
                    int y = (int)((p[1] - sourceRect.Top) * newHeight / sourceRect.Height);

                    if (x == priorX && y == priorY)
                    {
                        continue;
                    }
                    else
                    {
                        priorX = x;
                        priorY = y;
                    }

                    if (0 <= x && x < newWidth && 0 <= y && y < newHeight)
                    {
                        float riverRadiusInPixels = this.args.riverCapacityToMetersWideFunc(p[3]) / metersPerPixel / 2f;
                        int   l = -(int)(riverRadiusInPixels + 0.5f);
                        int   r = (int)riverRadiusInPixels;

                        for (int j = l; j <= r; j++)
                        {
                            for (int i = l; i <= r; i++)
                            {
                                int xx = x + i;
                                int yy = y + j;

                                if (0 <= xx && xx < newWidth && 0 <= yy && yy < newHeight && riverField[yy, xx] > p[2])
                                {
                                    riverField[yy, xx] = p[2];
                                }
                            }
                        }
                    }
                }
            }

            return(riverField);
        }
示例#5
0
        public static float GetLineNormalizerForRadius(float radius)
        {
            int r     = (int)Math.Ceiling(radius);
            var field = new Field2d <float>(new ConstantField <float>(r * 2 + 1, r * 2 + 1, 0f));

            for (int x = 0; x < field.Width; x++)
            {
                field[r, x] = 1f;
            }
            return(1f / new BlurredField(field, radius)[r, r]);
        }
示例#6
0
 private static void RecursivelyBoxBlur(Field2d <float> source, Field2d <float> target, Queue <int> boxSizes)
 {
     if (boxSizes.Count == 0)
     {
         return;
     }
     else
     {
         BoxBlur(source, target, (boxSizes.Dequeue() - 1) / 2);
         RecursivelyBoxBlur(target, source, boxSizes);
     }
 }
示例#7
0
        public static void GaussBlur(IField2d <float> source, Field2d <float> target, float radius, int n = 3)
        {
            var             boxSizes = BoxRadiiForGaussBlur(radius, n);
            Field2d <float> buffer   = new Field2d <float>(source);

            RecursivelyBoxBlur(buffer, target, boxSizes);

            // If n was an even number, then the final blurring wound up
            // in the buffer; replicate it to the target.
            if (n % 2 == 0)
            {
                target.Replicate(buffer);
            }
        }
示例#8
0
        private static void BoxBlurVertical(Field2d <float> source, Field2d <float> target, int radius)
        {
            // See BoxBlurHorizontal for detailed commentary.  The process here is
            // identical, just in the Y axis instead of the X.
            float accNormalizationFactor = 1f / (radius + radius + 1f);

            for (var x = 0; x < source.Width; x++)
            {
                int yTarget    = 0;
                int yFirstSrc  = yTarget;
                int ySecondSrc = yTarget + radius;

                float firstValue = source[0, yTarget];
                float lastValue  = source[source.Height - 1, yTarget];
                float acc        = (radius + 1f) * firstValue;

                for (var y = 0; y < radius; y++)
                {
                    acc += source[y, x];
                }

                for (var y = 0; y <= radius; y++)
                {
                    acc += source[ySecondSrc++, x] - firstValue;
                    target[yTarget++, x] = acc * accNormalizationFactor;
                }

                for (var y = radius + 1; y < source.Height - radius; y++)
                {
                    acc += source[ySecondSrc++, x] - source[yFirstSrc++, x];
                    target[yTarget++, x] = acc * accNormalizationFactor;
                }

                for (var y = source.Height - radius; y < source.Height; y++)
                {
                    acc += lastValue - source[yFirstSrc++, x];
                    target[yTarget++, x] = acc * accNormalizationFactor;
                }
            }
        }
示例#9
0
 public void Replicate(Field2d <T> other)
 {
     System.Array.Copy(other.values, this.values, this.values.Length);
 }
示例#10
0
 public static void EnforceWaterwaysLegalForField(this List <TreeNode <Point2d> > waterways, Field2d <float> field, float minIncrement = 0f)
 {
     foreach (var waterway in waterways)
     {
         foreach (var node in waterway)
         {
             if (node.parent != null &&
                 field[node.value.y, node.value.x] < field[node.parent.value.y, node.parent.value.x])
             {
                 field[node.value.y, node.value.x] = field[node.parent.value.y, node.parent.value.x] + minIncrement;
             }
         }
     }
 }
示例#11
0
        public static void ConvertPreciseHeightmapToColorMap(string file = "C:\\Users\\Justin Murray\\Desktop\\heightmap.png", float metersPerPixel = 20f)
        {
            Random random = new Random();
            var    bmp    = new Bitmap(file);

            Field2d <float> heightmap = new FieldFromPreciseBitmap(bmp);

            //Field2d<vFloat> gradientmap = new Field2d<vFloat>(new FunctionField<vFloat>(bmp.Width, bmp.Height, (x, y) =>
            //{
            //    if (x == 0 || y == 0)
            //    {
            //        return new vFloat(0f, 0f);
            //    }
            //    return new vFloat(heightmap[y, x] - heightmap[y, x - 1], heightmap[y, x] - heightmap[y - 1, x]) / metersPerPixel;
            //}));

            Field2d <bool> isWater = new Field2d <bool>(new FunctionField <bool>(bmp.Width, bmp.Height, (x, y) =>
            {
                Color c = bmp.GetPixel(x, y);
                return(c.A != 255 || heightmap[y, x] < 5f);
            }));

            Color[] colors = new Color[]
            {
                Color.Khaki,
                Color.SandyBrown,
                Color.Salmon,
                Color.Magenta,
                Color.Maroon,
                Color.DarkGoldenrod,
                Color.DarkGreen,
                Color.SlateGray,
                Color.Purple,
                Color.Orange,
                Color.Orchid,
                Color.YellowGreen,
                Color.Violet,
            };
            //vFloat refDir = new vFloat(1, 1).norm();
            //const float CLIFF_THRESHOLD = 1.5f;
            //const float SNOW_THRESHOLD = -0.5f;
            //const float SHRUBS_THRESHOLD = -0.5f;
            //const float FOREST_THRESHOLD = -0.1f;

            for (int y = 0; y < bmp.Height; y++)
            {
                for (int x = 0; x < bmp.Width; x++)
                {
                    //float mag = gradientmap[y, x].mag();
                    //// Is related to the normal, in a slightly abstract way.  Think about it.
                    //float theta = mag > 0f ? gradientmap[y, x].norm().dot(refDir) : 0f;
                    //float t = (theta + 1f) / 2f;

                    Color c = Color.NavajoWhite;

                    /*if (random.NextDouble() < (heightmap[y, x] - 2000f) / 500f)
                     * {
                     *  if (mag < CLIFF_THRESHOLD && theta > SNOW_THRESHOLD)
                     *  {
                     *      c = Color.Snow;
                     *  }
                     *  else
                     *  {
                     *      c = Utils.Lerp(Color.SlateGray, Color.LightSlateGray, t);
                     *  }
                     * }
                     * else if (random.NextDouble() < (heightmap[y, x] - 1500f) / 500f)
                     * {
                     *  if (mag > CLIFF_THRESHOLD)
                     *  {
                     *      c = Utils.Lerp(Color.Khaki, Color.LightSlateGray, t);
                     *  }
                     *  else if (theta < SHRUBS_THRESHOLD)
                     *  {
                     *      c = Utils.Lerp(Color.DarkSlateGray, Color.LightSlateGray, t);
                     *  }
                     *  else
                     *  {
                     *      c = Utils.Lerp(Color.DarkGray, Color.LightGray, t);
                     *  }
                     * }
                     * else if (random.NextDouble() < (heightmap[y, x] - 1000f) / 500f)
                     * {
                     *  if (mag > CLIFF_THRESHOLD)
                     *  {
                     *      c = Utils.Lerp(Color.DarkKhaki, Color.Khaki, t);
                     *  }
                     *  else if (theta < FOREST_THRESHOLD)
                     *  {
                     *      c = Color.DarkGreen;
                     *  }
                     *  else
                     *  {
                     *      c = Utils.Lerp(Color.ForestGreen, Color.DarkGreen, t);
                     *  }
                     * }
                     * else if (random.NextDouble() < (heightmap[y, x] - 500f) / 500f)
                     * {
                     *  if (mag > CLIFF_THRESHOLD)
                     *  {
                     *      c = Color.DarkSlateGray;
                     *  }
                     *  else if (theta < FOREST_THRESHOLD)
                     *  {
                     *      c = Utils.Lerp(Color.LawnGreen, Color.DarkGreen, mag);
                     *  }
                     *  else
                     *  {
                     *      c = Utils.Lerp(Color.ForestGreen, Color.LawnGreen, t);
                     *  }
                     * }
                     * else if (random.NextDouble() < (heightmap[y, x] - 20f) / 500f)
                     * {
                     *  if (mag > CLIFF_THRESHOLD)
                     *  {
                     *      c = Color.DarkSlateGray;
                     *  }
                     *  else if (theta < FOREST_THRESHOLD)
                     *  {
                     *      c = Color.ForestGreen;
                     *  }
                     *  else
                     *  {
                     *      c = Color.DarkGreen;
                     *  }
                     * }*/

                    if (isWater[y, x])
                    {
                        c = Color.DodgerBlue;
                    }
                    else
                    {
                        c = colors[((int)heightmap[y, x] / 200) % colors.Length];
                    }

                    bmp.SetPixel(x, y, c);
                }
            }

            bmp.Save("C:\\Users\\Justin Murray\\Desktop\\colored.png");
        }
示例#12
0
        public static void RunZoomedInScenario()
        {
            // 32x32 up to 1024x1024 will, from the fifth-of-a-mile-per-pixel source, get us approximately 10m per pixel.
            // 16x16 would get us 5
            // 8x8 would get us 2.5
            // 4x4 would get us 1.25
            // 2x2 would get us .75
            // 1x1 would get us .375, which is slightly over 1 foot.
            // I think 16x16 is the sweet spot.  That's just over 9 square miles per small map.
            const int   SMALL_MAP_SIDE_LEN    = 64;
            const float STARTING_SCALE        = 0.005f * SMALL_MAP_SIDE_LEN / 32;
            const int   SMALL_MAP_RESIZED_LEN = 1024;

            Random random = new Random();

            WaterTableArgs args = new WaterTableArgs();
            Bitmap         bmp  = new Bitmap(args.inputPath + "rivers.png");

            IField2d <float> baseMap = new Utils.FieldFromBitmap(new Bitmap(args.inputPath + "base_heights.png"));

            baseMap = new ReResField(baseMap, (float)bmp.Width / baseMap.Width);

            var wtf = Utils.GenerateWaters(bmp, baseMap);

            Utils.OutputAsColoredMap(wtf, wtf.RiverSystems, bmp, args.outputPath + "colored_map.png");

            var hasWater     = new Transformation2d <HydrologicalField.LandType, float>(wtf.HydroField, t => t == HydrologicalField.LandType.Land ? 0f : 1f);
            var noiseDamping = new Transformation2d(new BlurredField(hasWater, 2f), v => 3.5f * v);

            // Create the spline map.
            SparseField2d <List <SplineTree> > relevantSplines = new SparseField2d <List <SplineTree> >(wtf.Width, wtf.Height, null);
            {
                //HashSet<TreeNode<Point2d>> relevantRivers = new HashSet<TreeNode<Point2d>>();
                foreach (var system in wtf.RiverSystems)
                {
                    SplineTree tree = null;

                    foreach (var p in system.value)
                    {
                        if (relevantSplines[p.value.y, p.value.x] == null)
                        {
                            relevantSplines[p.value.y, p.value.x] = new List <SplineTree>();
                        }
                        relevantSplines[p.value.y, p.value.x].Add(tree ?? (tree = new SplineTree(system.value, wtf, random)));
                    }
                }
            }

            Rectangle rect       = new Rectangle(518 + 15, 785 + 45, SMALL_MAP_SIDE_LEN, SMALL_MAP_SIDE_LEN);
            var       smallMap   = new SubField <float>(wtf, rect);
            var       scaledUp   = new BlurredField(new ReResField(smallMap, SMALL_MAP_RESIZED_LEN / smallMap.Width), SMALL_MAP_RESIZED_LEN / (4 * SMALL_MAP_SIDE_LEN));
            var       smallDamp  = new SubField <float>(noiseDamping, rect);
            var       scaledDamp = new BlurredField(new ReResField(smallDamp, SMALL_MAP_RESIZED_LEN / smallMap.Width), SMALL_MAP_RESIZED_LEN / (4 * SMALL_MAP_SIDE_LEN));

            // Do spline-y things.
            Field2d <float>   riverbeds;
            List <SplineTree> splines = new List <SplineTree>();
            {
                // Collect a comprehensive list of the spline trees for the local frame.
                for (int y = rect.Top - 1; y <= rect.Bottom + 1; y++)
                {
                    for (int x = rect.Left - 1; x <= rect.Right + 1; x++)
                    {
                        List <SplineTree> trees = relevantSplines[y, x];
                        if (trees != null)
                        {
                            splines.AddRange(trees);
                        }
                    }
                }

                // Crafts the actual river kernel.  Probably not the best way to go about this.
                riverbeds = new Field2d <float>(new ConstantField <float>(SMALL_MAP_RESIZED_LEN, SMALL_MAP_RESIZED_LEN, float.MaxValue));
                foreach (var s in splines)
                {
                    var samples = s.GetSamplesPerControlPoint(1f * SMALL_MAP_RESIZED_LEN / SMALL_MAP_SIDE_LEN);

                    int priorX = int.MinValue;
                    int priorY = int.MinValue;

                    foreach (var p in samples)
                    {
                        int x = (int)((p[0] - rect.Left) * SMALL_MAP_RESIZED_LEN / SMALL_MAP_SIDE_LEN);
                        int y = (int)((p[1] - rect.Top) * SMALL_MAP_RESIZED_LEN / SMALL_MAP_SIDE_LEN);

                        if (x == priorX && y == priorY)
                        {
                            continue;
                        }
                        else
                        {
                            priorX = x;
                            priorY = y;
                        }

                        if (0 <= x && x < SMALL_MAP_RESIZED_LEN && 0 <= y && y < SMALL_MAP_RESIZED_LEN)
                        {
                            const int r = 1024 / SMALL_MAP_SIDE_LEN;

                            for (int j = -r; j <= r; j++)
                            {
                                for (int i = -r; i <= r; i++)
                                {
                                    int xx = x + i;
                                    int yy = y + j;

                                    if (0 <= xx && xx < SMALL_MAP_RESIZED_LEN && 0 <= yy && yy < SMALL_MAP_RESIZED_LEN)
                                    {
                                        float dSq = i * i + j * j;
                                        riverbeds[yy, xx] = Math.Min(riverbeds[yy, xx], p[2] + dSq / (512f * 32 / SMALL_MAP_SIDE_LEN));
                                        //scaledDamp[yy, xx] = 1f;
                                        //scaledUp[yy, xx] = Math.Min(scaledUp[yy, xx], p[2] + (float)Math.Sqrt(xx * xx + yy * yy) / 1f);
                                    }
                                }
                            }
                        }
                    }
                }
                //Utils.OutputField(riverbeds, new Bitmap(riverbeds.Width, riverbeds.Height), args.outputPath + "river_field.png");
            }

            var mountainous  = new ScaleTransform(new MountainNoise(1024, 1024, STARTING_SCALE), 1f);
            var hilly        = new ScaleTransform(new Simplex2D(1024, 1024, STARTING_SCALE * 4), 0.1f);
            var terrainNoise = new Transformation2d <float, float, float>(mountainous, hilly, (x, y, m, h) =>
            {
                float a  = scaledUp[y, x];
                float sh = Math.Max(-2f * Math.Abs(a - 0.2f) / 3f + 1f, 0f);
                float sm = Math.Min(1.3f * a, 1f);
                return(h * sh + m * sm);
            });

            IField2d <float> combined =
                new NormalizedComposition2d <float>(
                    new Transformation2d <float, float, float>(riverbeds,
                                                               new Composition2d <float>(scaledUp,
                                                                                         new Transformation2d <float, float, float>(scaledDamp, terrainNoise, (s, m) => (1 - Math.Min(1, s)) * m)
                                                                                         ),
                                                               (r, c) => r == float.MaxValue ? c : Math.Min(r, c))
                    );

            Bitmap img = new Bitmap(combined.Width, combined.Height);

            Utils.OutputField(combined, img, args.outputPath + "combined.png");

            Utils.OutputAsOBJ(combined, splines, rect, img, args.outputPath);
        }
示例#13
0
        public static void RunPopulationScenario()
        {
            WaterTableArgs args = new WaterTableArgs();
            Bitmap         bmp  = new Bitmap(args.inputPath + "rivers.png");

            IField2d <float> baseMap = new Utils.FieldFromBitmap(new Bitmap(args.inputPath + "base_heights.png"));

            baseMap = new ReResField(baseMap, (float)bmp.Width / baseMap.Width);

            var wtf = Utils.GenerateWaters(bmp, baseMap);

            Utils.OutputAsColoredMap(wtf, wtf.RiverSystems, bmp, args.outputPath + "colored_map.png");

            IField2d <float> rainfall = new Utils.FieldFromBitmap(new Bitmap(args.inputPath + "rainfall.png"));

            rainfall = new ReResField(rainfall, (float)wtf.Width / rainfall.Width);

            IField2d <float> wateriness = Utils.GetWaterinessMap(wtf, rainfall);

            Utils.OutputField(new NormalizedComposition2d <float>(wateriness), bmp, args.outputPath + "wateriness.png");

            var locations = Utils.GetSettlementLocations(wtf, wateriness);
            SparseField2d <float> settlementMap = new SparseField2d <float>(wtf.Width, wtf.Height, 0f);

            foreach (var loc in locations)
            {
                settlementMap.Add(loc, wateriness[loc.y, loc.x]);
            }
            Utils.OutputField(settlementMap, bmp, args.outputPath + "settlements.png");

            TriangleNet.Geometry.InputGeometry pointSet = new TriangleNet.Geometry.InputGeometry();
            foreach (var loc in locations)
            {
                pointSet.AddPoint(loc.x, loc.y);
            }
            TriangleNet.Mesh mesh = new TriangleNet.Mesh();
            mesh.Triangulate(pointSet);
            //TriangleNet.Tools.AdjacencyMatrix mat = new TriangleNet.Tools.AdjacencyMatrix(mesh);

            Field2d <float> meshField = new Field2d <float>(settlementMap);

            foreach (var e in mesh.Edges)
            {
                var v0 = mesh.GetVertex(e.P0);
                var v1 = mesh.GetVertex(e.P1);

                float distance = (float)Math.Sqrt(Math.Pow(v0.X - v1.X, 2) + Math.Pow(v0.Y - v1.Y, 2));

                for (float t = 0f; t <= 1f; t += 0.5f / distance)
                {
                    int x = (int)Math.Round((1f - t) * v0.X + t * v1.X);
                    int y = (int)Math.Round((1f - t) * v0.Y + t * v1.Y);

                    meshField[y, x] = 0.5f;
                }

                meshField[(int)v0.Y, (int)v0.X] = 1f;
                meshField[(int)v1.Y, (int)v1.X] = 1f;
            }
            Utils.OutputField(meshField, bmp, args.outputPath + "mesh.png");
        }
示例#14
0
        public static void RunStreamedMapCombinerScenario(int cacheSize = 16)
        {
            ImageServer server = new ImageServer();

            string[] fileNames = Directory.GetFiles("C:\\Users\\Justin Murray\\Desktop\\egwethoon\\", "submap*.png");
            foreach (string fileName in fileNames)
            {
                server.AddImage(fileName);
            }

            StreamedChunkedPreciseHeightField streamedField = new StreamedChunkedPreciseHeightField(512 * 256 / 32, 512 * 256 / 32, cacheSize,
                                                                                                    (x, y) =>
            {
                var chunkToLoad = server.TryGetPathForPoint(x, y);
                if (chunkToLoad.path != null)
                {
                    return(new ChunkField <float> .Chunk(chunkToLoad.x, chunkToLoad.y, new FieldFromPreciseBitmap(new Bitmap(chunkToLoad.path))));
                }
                return(null);
            });

            Field2d <float> output = new Field2d <float>(new ConstantField <float>(streamedField.Width, streamedField.Height, 0f));

            foreach (var chunkToLoad in server.GetImages())
            {
                var chunk = new ChunkField <float> .Chunk(chunkToLoad.x, chunkToLoad.y, new FieldFromPreciseBitmap(new Bitmap(chunkToLoad.path)));

                for (int y = 0; y < chunk.Field.Height; y++)
                {
                    for (int x = 0; x < chunk.Field.Width; x++)
                    {
                        output[y + chunk.MinPoint.Y / 2, x + chunk.MinPoint.X / 2] = chunk.Field[y, x];
                    }
                }
            }
            output = new BlurredField(output, 1f);

            Bitmap bmp = new Bitmap(output.Width, output.Height);

            for (int y = 0; y < output.Height; y++)
            {
                for (int x = 0; x < output.Width; x++)
                {
                    float val = output[y, x];
                    if (val > 2250f)
                    {
                        bmp.SetPixel(x, y, Lerp(Color.Gray, Color.White, (val - 2250f) / 1750f));
                    }
                    else if (val > 1250f)
                    {
                        bmp.SetPixel(x, y, Lerp(Color.Red, Color.Gray, (val - 1250f) / 1000f));
                    }
                    else if (val > 750f)
                    {
                        bmp.SetPixel(x, y, Lerp(Color.Yellow, Color.Red, (val - 750f) / 500f));
                    }
                    else if (val > 250f)
                    {
                        bmp.SetPixel(x, y, Lerp(Color.Green, Color.Yellow, (val - 250f) / 500f));
                    }
                    else if (val > 5f)
                    {
                        bmp.SetPixel(x, y, Lerp(Color.DarkGreen, Color.Green, (val - 5f) / 245f));
                    }
                    else
                    {
                        bmp.SetPixel(x, y, Color.Aqua);
                    }
                }
            }

            bmp.Save("C:\\Users\\Justin Murray\\Desktop\\egwethoon\\bigmap.png");
        }