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)); }
public static WaterTableField GenerateWaters(Bitmap bmp, IField2d <float> baseField = null, WaterTableArgs args = null, Random random = null) { args = args ?? new WaterTableArgs() { seed = System.DateTime.UtcNow.Ticks }; random = random ?? new Random((int)args.seed); baseField = baseField ?? new Simplex2D(bmp.Width, bmp.Height, args.baseNoiseScale, args.seed); Field2d <float> field = new FieldFromBitmap(bmp); baseField = new NormalizedComposition2d <float>(baseField, new ScaleTransform(new Simplex2D(baseField.Width, baseField.Height, args.baseNoiseScale, args.seed), args.baseNoiseScalar)); BrownianTree tree = BrownianTree.CreateFromOther(field, (x) => x > 0.5f ? BrownianTree.Availability.Available : BrownianTree.Availability.Unavailable, random); tree.RunDefaultTree(); HydrologicalField hydro = new HydrologicalField(tree, args.hydroSensitivity, args.hydroShoreThreshold); WaterTableField wtf = new WaterTableField(baseField, hydro, args.wtfShore, args.wtfIt, args.wtfLen, args.wtfGrade, () => { return((float)(args.wtfCarveAdd + random.NextDouble() * args.wtfCarveMul)); }); return(wtf); }
public static List <Point2d> GetSettlementLocations(WaterTableField wtf, IField2d <float> wateriness, float noiseScale = 0.1f, float noiseBlur = 3f, float noiseContribution = 0.15f) { IField2d <float> noise = new BlurredField(new MountainNoise(wtf.Width, wtf.Height, noiseScale), noiseBlur); Transformation2d <float, float, float> combination = new Transformation2d <float, float, float>(wateriness, noise, (w, n) => w + noiseContribution * n); List <Point2d> locations = new List <Point2d>(); for (int y = 1; y < combination.Height - 1; y++) { for (int x = 1; x < combination.Width - 1; x++) { if (wtf[y, x] > 0f && combination[y - 1, x - 1] < combination[y, x] && combination[y - 1, x + 0] < combination[y, x] && combination[y - 1, x + 1] < combination[y, x] && combination[y + 0, x + 1] < combination[y, x] && combination[y + 1, x + 1] < combination[y, x] && combination[y + 1, x + 0] < combination[y, x] && combination[y + 1, x - 1] < combination[y, x] && combination[y + 0, x - 1] < combination[y, x]) { locations.Add(new Point2d(x, y)); } } } return(locations); }
public MeterScaleMap(Args args) { this.args = args; Random random = new Random((int)this.args.seed); this.wtf = InitializeWaterTableField(this.args, random); this.distanceToWater = InitializeDistanceFromWater(this.wtf, this.args); this.splines = InitializeSplines(this.wtf, random); this.mountainNoise = InitializeMountainNoise(this.wtf, this.args.seed, this.args.metersPerPixel); }
private static WaterTableField InitializeWaterTableField(Args args, Random random) { BrownianTree tree = BrownianTree.CreateFromOther(args.watersDrawing, (x) => x > 0.5f ? BrownianTree.Availability.Available : BrownianTree.Availability.Unavailable, random); tree.RunDefaultTree(); HydrologicalField hydro = new HydrologicalField(tree, args.hydroSensitivity, args.hydroShoreThreshold); IField2d <float> scaledHeights = new ScaleTransform(args.heightsDrawing, args.baseHeightMaxInMeters); WaterTableField wtf = new WaterTableField(scaledHeights, hydro, args.wtfShore, args.wtfIt, args.wtfLen, args.wtfGrade, () => { return((float)(args.wtfCarveAdd + random.NextDouble() * args.wtfCarveMul)); }); return(wtf); }
private static ContinuousField InitializeDistanceFromWater(WaterTableField wtf, Args args) { var dists = new Transformation2d <Point2d, float>(wtf.DrainageField, (x, y, p) => { if (wtf.HydroField[y, x] != HydrologicalField.LandType.Land) { return(0f); } // We subtract 1 to compute the distance as water-adjacent as well as on-the-water. // This is a slight bit of a hack, but helps account for problems at varying resolutions. return(args.metersPerPixel * (Point2d.Distance(new Point2d(x, y), p) - 1f)); }); return(new ContinuousField(dists)); }
private static SparseField2d <List <SplineTree> > InitializeSplines(WaterTableField wtf, Random random) { var splines = new SparseField2d <List <SplineTree> >(wtf.Width, wtf.Height, null); foreach (var system in wtf.RiverSystems) { SplineTree tree = null; foreach (var p in system.value) { if (splines[p.value.y, p.value.x] == null) { splines[p.value.y, p.value.x] = new List <SplineTree>(); } splines[p.value.y, p.value.x].Add(tree ?? (tree = new SplineTree(system.value, wtf, random))); } } return(splines); }
public static void RunWaterHeightScenario(string simpleWatersMapName, string simpleAltitudesMapName) { WaterTableArgs args = new WaterTableArgs(); args.seed = System.DateTime.UtcNow.Ticks; Random random = new Random((int)args.seed); Bitmap jranjana = new Bitmap(args.inputPath + simpleWatersMapName); Field2d <float> field = new Utils.FieldFromBitmap(jranjana); IField2d <float> bf = new Utils.FieldFromBitmap(new Bitmap(args.inputPath + simpleAltitudesMapName)); bf = new NormalizedComposition2d <float>(bf, new ScaleTransform(new Simplex2D(bf.Width, bf.Height, args.baseNoiseScale, args.seed), args.baseNoiseScalar)); Utils.OutputField(bf, jranjana, args.outputPath + "basis.png"); BrownianTree tree = BrownianTree.CreateFromOther(field, (x) => x > 0.5f ? BrownianTree.Availability.Available : BrownianTree.Availability.Unavailable, random); tree.RunDefaultTree(); Utils.OutputField(new Transformation2d <BrownianTree.Availability, float>(tree, val => val == BrownianTree.Availability.Available ? 1f : 0f), jranjana, args.outputPath + "rivers.png"); HydrologicalField hydro = new HydrologicalField(tree, args.hydroSensitivity, args.hydroShoreThreshold); WaterTableField wtf = new WaterTableField(bf, hydro, args.wtfShore, args.wtfIt, args.wtfLen, args.wtfGrade, () => { return((float)(args.wtfCarveAdd + random.NextDouble() * args.wtfCarveMul)); }); Utils.OutputAsTributaryMap(wtf.GeographicFeatures, wtf.RiverSystems, wtf.DrainageField, jranjana, args.outputPath + "tributaries.png"); Utils.OutputField(new NormalizedComposition2d <float>(new Transformation2d <float, float, float>(bf, wtf, (b, w) => Math.Abs(b - w))), jranjana, args.outputPath + "errors.png"); Utils.SerializeMap(hydro, wtf, args.seed, args.outputPath + "serialization.bin"); Utils.OutputField(wtf, jranjana, args.outputPath + "heightmap.png"); Utils.OutputAsColoredMap(wtf, wtf.RiverSystems, jranjana, args.outputPath + "colored_map.png"); }
private static ContinuousMountainNoise InitializeMountainNoise(WaterTableField wtf, long seed, float metersPerPixel) { return(new ContinuousMountainNoise(wtf.Width, wtf.Height, GetMountainNoiseStartingScale(metersPerPixel), seed)); }