private static void BoxBlur(Field2d <float> source, Field2d <float> target, int radius) { target.Replicate(source); BoxBlurHorizontal(target, source, radius); BoxBlurVertical(source, target, radius); }
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)); }
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; } } }
// 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); }
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]); }
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); } }
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); } }
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; } } }
public void Replicate(Field2d <T> other) { System.Array.Copy(other.values, this.values, this.values.Length); }
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; } } } }
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"); }
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); }
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"); }
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"); }