/// <summary> /// Generates a new set of precipitation and snowfall map images. /// </summary> /// <param name="planet">The planet being mapped.</param> /// <param name="winterTemperatures">A winter temperature map.</param> /// <param name="summerTemperatues">A summer temperature map.</param> /// <param name="resolution">The vertical resolution.</param> /// <param name="steps"> /// The number of maps to generate internally (representing evenly spaced "seasons" during a year, /// starting and ending at the winter solstice in the northern hemisphere). /// </param> /// <param name="temperatureProjection"> /// <para> /// The map projection of the temperature maps. They must be the same. /// </para> /// <para> /// If left <see langword="null"/> an equirectangular projection of the full globe is /// assumed. /// </para> /// </param> /// <param name="projection"> /// <para> /// The map projection options used. /// </para> /// <para> /// If left <see langword="null"/> an equirectangular projection of the full globe is /// produced. /// </para> /// </param> /// <returns> /// A set of precipitation and snowfall map images. Pixel luminosity indicates /// precipitation in mm/hr, relative to the <see cref="Atmosphere.MaxPrecipitation"/> of /// this planet's <see cref="Atmosphere"/>. /// </returns> public static (Image <L16>[] precipitationMaps, Image <L16>[] snowfallMaps) GetPrecipitationAndSnowfallMaps( this Planetoid planet, Image <L16> winterTemperatures, Image <L16> summerTemperatues, int resolution, int steps, MapProjectionOptions?temperatureProjection = null, MapProjectionOptions?projection = null) { var options = projection ?? MapProjectionOptions.Default; var precipitationMaps = new Image <L16> [steps]; var snowMaps = new Image <L16> [steps]; if (planet.Atmosphere.MaxPrecipitation.IsNearlyZero()) { var xResolution = (int)Math.Floor(resolution * options.AspectRatio); for (var i = 0; i < steps; i++) { precipitationMaps[i] = new Image <L16>(xResolution, resolution); snowMaps[i] = new Image <L16>(xResolution, resolution); } return(precipitationMaps, snowMaps); } var noise1 = new FastNoise(planet.Seed4, 1.0, FastNoise.NoiseType.Simplex); var noise2 = new FastNoise(planet.Seed5, 3.0, FastNoise.NoiseType.SimplexFractal, octaves: 3); var proportionOfYear = 1f / steps; var proportionOfYearAtMidpoint = 0f; var trueAnomaly = planet.WinterSolsticeTrueAnomaly; var trueAnomalyPerSeason = DoubleConstants.TwoPi / steps; for (var i = 0; i < steps; i++) { var solarDeclination = planet.GetSolarDeclination(trueAnomaly); (precipitationMaps[i], snowMaps[i]) = SurfaceMapImage.GenerateMapImages( new[] { winterTemperatures, summerTemperatues }, (lat, lon, temperature) => { var precipitation = planet.GetPrecipitationNoise( noise1, noise2, planet.LatitudeAndLongitudeToDoubleVector(lat, lon), lat, Planetoid.GetSeasonalLatitudeFromDeclination(lat, solarDeclination), temperature * SurfaceMapImage.TemperatureScaleFactor, out var snow); return( precipitation / planet.Atmosphere.MaxPrecipitation, snow / planet.Atmosphere.MaxSnowfall); },
/// <summary> /// Calculates the atmospheric density for the given conditions, in kg/m³. /// </summary> /// <param name="planet">The mapped planet.</param> /// <param name="winterTemperatures">A winter temperature map.</param> /// <param name="summerTemperatures">A summer temperature map.</param> /// <param name="proportionOfYear"> /// The proportion of a full year at which the map is to be generated, assuming a year /// begins and ends at the winter solstice in the northern hemisphere. /// </param> /// <param name="latitude">The latitude of the object.</param> /// <param name="longitude">The longitude of the object.</param> /// <param name="altitude">The altitude of the object.</param> /// <param name="surface"> /// If <see langword="true"/> the determination is made for a location /// on the surface of the planetoid at the given elevation. Otherwise, the calculation is /// made for an elevation above the surface. /// </param> /// <param name="options">The map projection used.</param> /// <returns>The atmospheric density for the given conditions, in kg/m³.</returns> public static double GetAtmosphericDensity( this Planetoid planet, Image <L16> winterTemperatures, Image <L16> summerTemperatures, double proportionOfYear, double latitude, double longitude, double altitude, bool surface = true, MapProjectionOptions?options = null) { var surfaceTemp = SurfaceMapImage.GetSurfaceTemperature(winterTemperatures, summerTemperatures, proportionOfYear, latitude, longitude, options); var tempAtElevation = planet.GetTemperatureAtElevation(surfaceTemp, altitude, surface); return(planet.Atmosphere.GetAtmosphericDensity(planet, tempAtElevation, altitude)); }
/// <summary> /// Generates an elevation map image for this planet. /// </summary> /// <param name="planet">The mapped planet.</param> /// <param name="resolution">The vertical resolution of the map.</param> /// <param name="options"> /// <para> /// The map projection options used. /// </para> /// <para> /// If left <see langword="null"/> an equirectangular projection of the full globe is /// produced. /// </para> /// </param> /// <returns>An elevation map image for this planet.</returns> public static Image <L16> GetElevationMap( this Planetoid planet, int resolution, MapProjectionOptions?options = null) { if (planet.MaxElevation.IsNearlyZero()) { return(SurfaceMapImage.GenerateZeroMapImage(resolution, options, true)); } var noise1 = new FastNoise(planet.Seed1, 0.8, FastNoise.NoiseType.SimplexFractal, octaves: 6); var noise2 = new FastNoise(planet.Seed2, 0.6, FastNoise.NoiseType.SimplexFractal, FastNoise.FractalType.Billow, octaves: 6); var noise3 = new FastNoise(planet.Seed3, 1.2, FastNoise.NoiseType.Simplex); return(SurfaceMapImage.GenerateMapImage( (lat, lon) => GetElevationNoise(noise1, noise2, noise3, planet.LatitudeAndLongitudeToDoubleVector(lat, lon)), resolution, options, true)); }
public void EarthlikePlanet() { // First run to ensure timed runs do not include any one-time initialization costs. _ = Planetoid.GetPlanetForSunlikeStar(out _); var stopwatch = new Stopwatch(); stopwatch.Start(); var planet = Planetoid.GetPlanetForSunlikeStar(out _); stopwatch.Stop(); Assert.IsNotNull(planet); Console.WriteLine($"Planet generation time: {stopwatch.Elapsed:s'.'FFF} s"); Console.WriteLine($"Radius: {planet!.Shape.ContainingRadius / 1000:N0} km"); Console.WriteLine($"Surface area: {planet!.Shape.ContainingRadius.Square() * HugeNumberConstants.FourPi / 1000000:N0} km²"); stopwatch.Restart(); using (var elevationMap = planet.GetElevationMap(MapResolution)) { var(winterTemperatureMap, summerTemperatureMap) = planet.GetTemperatureMaps(elevationMap, MapResolution); var(precipitationMaps, snowfallMaps) = planet .GetPrecipitationAndSnowfallMaps(winterTemperatureMap, summerTemperatureMap, MapResolution, Seasons); for (var i = 0; i < snowfallMaps.Length; i++) { snowfallMaps[i].Dispose(); } using var precipitationMap = SurfaceMapImage.AverageImages(precipitationMaps); for (var i = 0; i < precipitationMaps.Length; i++) { precipitationMaps[i].Dispose(); } _ = new WeatherMaps( planet, elevationMap, winterTemperatureMap, summerTemperatureMap, precipitationMap, MapResolution, MapProjectionOptions.Default); winterTemperatureMap.Dispose(); summerTemperatureMap.Dispose(); } stopwatch.Stop(); Console.WriteLine($"Equirectangular surface map generation time: {stopwatch.Elapsed:s'.'FFF} s"); var projection = new MapProjectionOptions(equalArea: true); stopwatch.Restart(); using var elevationMapEA = planet.GetElevationMap(MapResolution, projection); var(winterTemperatureMapEA, summerTemperatureMapEA) = planet.GetTemperatureMaps(elevationMapEA, MapResolution, projection, projection); using var temperatureMapEA = SurfaceMapImage.AverageImages(winterTemperatureMapEA, summerTemperatureMapEA); var(precipitationMapsEA, snowfallMapsEA) = planet .GetPrecipitationAndSnowfallMaps(winterTemperatureMapEA, summerTemperatureMapEA, MapResolution, Seasons, projection, projection); for (var i = 0; i < snowfallMapsEA.Length; i++) { snowfallMapsEA[i].Dispose(); } using var precipitationMapEA = SurfaceMapImage.AverageImages(precipitationMapsEA); for (var i = 0; i < precipitationMapsEA.Length; i++) { precipitationMapsEA[i].Dispose(); } var climateMapsEA = new WeatherMaps( planet, elevationMapEA, winterTemperatureMapEA, summerTemperatureMapEA, precipitationMapEA, MapResolution, projection); winterTemperatureMapEA.Dispose(); summerTemperatureMapEA.Dispose(); stopwatch.Stop(); Console.WriteLine($"Cylindrical equal-area surface map generation time: {stopwatch.Elapsed:s'.'FFF} s"); var normalizedSeaLevel = planet.SeaLevel / planet.MaxElevation; var elevationRange = planet.GetElevationRange(elevationMapEA); var landCoords = 0; if (planet.Hydrosphere?.IsEmpty == false) { for (var x = 0; x < elevationMapEA.Width; x++) { for (var y = 0; y < elevationMapEA.Height; y++) { var value = (2.0 * elevationMapEA[x, y].PackedValue / ushort.MaxValue) - 1; if (value - normalizedSeaLevel > 0) { landCoords++; } } } } var sb = new StringBuilder(); AddTempString(sb, temperatureMapEA); sb.AppendLine(); AddTerrainString(sb, planet, elevationMapEA, landCoords); sb.AppendLine(); AddClimateString(sb, elevationMapEA, normalizedSeaLevel, landCoords, climateMapsEA); sb.AppendLine(); AddPrecipitationString(sb, planet, elevationMapEA, precipitationMapEA, normalizedSeaLevel, landCoords, climateMapsEA); Console.WriteLine(sb.ToString()); }