use earth::biosphere::ecosystems::{boreal_forest, coral_reef, tropical_rainforest};
use earth::biosphere::fauna::{PredatorPrey, african_elephant, blue_whale};
use earth::biosphere::vegetation::{
beer_lambert_light_extinction, coniferous_forest, temperate_grassland, tropical_broadleaf,
};
use earth::geodata::bathymetry::{BathymetryData, OceanBasin};
use earth::geodata::coordinates::{
EARTH_FLATTENING, EARTH_SEMI_MAJOR_M, EARTH_SEMI_MINOR_M, LatLon,
};
use earth::geodata::elevation::ElevationProvider;
use earth::geodata::regions::{RegionDatabase, RegionType};
use earth::lighting::day_night::{DayNightCycle, DaylightState};
use earth::lighting::seasons::{
AXIAL_TILT_DEG as SEASONS_TILT, Season, TROPICAL_YEAR_DAYS, VERNAL_EQUINOX_JD, season_at,
subsolar_point,
};
use earth::lighting::solar_position::{EARTH_AXIAL_TILT_DEG, SolarPosition};
use earth::physics::collisions::{chicxulub_equivalent, tunguska_equivalent};
use earth::physics::orbit::{
ARGUMENT_PERIHELION_DEG, ECCENTRICITY, EarthOrbit, INCLINATION_DEG,
LONGITUDE_ASCENDING_NODE_DEG, SEMI_MAJOR_AXIS,
};
use earth::physics::rotation::{
AXIAL_TILT_DEG, AXIAL_TILT_RAD, EarthRotation, PRECESSION_PERIOD_YEARS, SIDEREAL_DAY_S,
SOLAR_DAY_S,
};
use earth::physics::tides::{
TidalForce, lunar_to_solar_tide_ratio, neap_tide_amplitude, spring_tide_amplitude,
};
use earth::rendering::atmosphere_scattering::AtmosphereParams;
use earth::rendering::clouds::{CloudLayer, CloudSystem, CloudType};
use earth::rendering::materials::PbrMaterial;
use earth::rendering::ocean_rendering::OceanParams;
use earth::rendering::shaders::{ShaderData, UniformValue};
use earth::satellites::artificial::{ArtificialSatellite, Constellation, OrbitType};
use earth::satellites::moon::{
EARTH_MOON_DISTANCE, LUNAR_MASS, LUNAR_ORBITAL_PERIOD, LUNAR_RADIUS, MoonSource, MoonState,
};
use earth::temporal::calendar::{DateTime, J2000_JD, SECONDS_PER_DAY, UNIX_EPOCH_JD};
use earth::temporal::epoch::{Epoch, J1950_EPOCH, J2000_EPOCH, MJD_OFFSET};
use earth::temporal::time_scale::TimeScale;
use earth::terrain::heightmap::Heightmap;
use earth::terrain::lod::{Face, LodConfig, LodTerrain};
use earth::terrain::mesh::TerrainMesh;
use earth::terrain::texturing::{Biome, BiomeClassifier};
fn main() {
let orbit = EarthOrbit::new();
let period_days = orbit.orbital_period_days();
let v_perihelion = orbit.velocity_at_distance(orbit.perihelion_m());
let v_aphelion = orbit.velocity_at_distance(orbit.aphelion_m());
let energy = orbit.specific_orbital_energy();
let ang_mom = orbit.specific_angular_momentum();
let v_esc = EarthOrbit::escape_velocity_at_surface();
let f_sun = orbit.gravitational_force_sun();
let r = orbit.current_radius();
let v_mean = orbit.mean_orbital_velocity();
println!(
"Orbit: P={:.2}d v_peri={:.0}m/s v_aph={:.0}m/s v_mean={:.0}m/s",
period_days, v_perihelion, v_aphelion, v_mean
);
println!(
" E={:.2e}J/kg L={:.2e}m²/s v_esc={:.0}m/s F_sun={:.2e}N r={:.3e}m",
energy, ang_mom, v_esc, f_sun, r
);
println!(
" a={:.3e}m e={} i={}° Ω={}° ω={}°",
SEMI_MAJOR_AXIS,
ECCENTRICITY,
INCLINATION_DEG,
LONGITUDE_ASCENDING_NODE_DEG,
ARGUMENT_PERIHELION_DEG
);
let rot = EarthRotation::new();
let v_equator = rot.surface_velocity_at_latitude(0.0);
let v_45 = rot.surface_velocity_at_latitude(45.0);
let accel = rot.centripetal_acceleration_at_latitude(0.0);
let coriolis = rot.coriolis_parameter(45.0);
let moi = rot.moment_of_inertia();
let ke = rot.rotational_kinetic_energy();
let l_rot = rot.angular_momentum();
let prec = rot.precession_rate_rad_per_year();
let daylen_summer = rot.day_length_variation_due_to_tilt(172, 60.0);
let daylen_winter = rot.day_length_variation_due_to_tilt(355, 60.0);
println!(
"Rotation: v_eq={:.0}m/s v_45={:.0}m/s a_c={:.4}m/s² f_45={:.5e}",
v_equator, v_45, accel, coriolis
);
println!(
" I={:.3e}kg·m² KE={:.3e}J L={:.3e}kg·m²/s prec={:.2e}rad/yr",
moi, ke, l_rot, prec
);
println!(
" day@60°N: summer={:.1}h winter={:.1}h",
daylen_summer, daylen_winter
);
println!(
" sidereal={:.4}s solar={}s tilt={}°={:.4}rad prec_period={}yr",
SIDEREAL_DAY_S, SOLAR_DAY_S, AXIAL_TILT_DEG, AXIAL_TILT_RAD, PRECESSION_PERIOD_YEARS
);
let moon_tide = TidalForce::from_moon();
let sun_tide = TidalForce::from_sun();
let a_moon = moon_tide.tidal_acceleration();
let a_sun = sun_tide.tidal_acceleration();
let pot = moon_tide.tidal_potential(0.0);
let bulge_moon = moon_tide.tidal_bulge_height();
let bulge_sun = sun_tide.tidal_bulge_height();
let grav = moon_tide.gravitational_attraction();
let spring = spring_tide_amplitude();
let neap = neap_tide_amplitude();
let ratio = lunar_to_solar_tide_ratio();
println!(
"Tides: a_moon={:.2e} a_sun={:.2e} ratio={:.2}",
a_moon, a_sun, ratio
);
println!(
" bulge_moon={:.3}m bulge_sun={:.3}m spring={:.3}m neap={:.3}m",
bulge_moon, bulge_sun, spring, neap
);
println!(" potential(0)={:.2e} F_grav={:.2e}N", pot, grav);
let chix = chicxulub_equivalent();
let tung = tunguska_equivalent();
let ke_chix = chix.kinetic_energy_mt();
let crater = chix.crater_diameter_m(2700.0);
let fireball = chix.fireball_radius_m();
let ejecta = chix.ejecta_volume_m3(2700.0);
let v_imp = chix.impact_velocity();
let ke_tung = tung.kinetic_energy_mt();
println!(
"Chicxulub: {:.2e}Mt crater={:.0}m fireball={:.0}m ejecta={:.2e}m³ v_imp={:.0}m/s",
ke_chix, crater, fireball, ejecta, v_imp
);
println!("Tunguska: {:.2e}Mt", ke_tung);
let mut moon = MoonState::new();
let source = match moon.source.get() {
MoonSource::Binary => "binary",
MoonSource::Simulation => "simulated",
};
let pos0 = moon.position();
moon.step(3600.0);
let pos1 = moon.position();
let g_moon = moon.gravity_at(EARTH_MOON_DISTANCE);
println!(
"Moon({}): pos0=({:.0},{:.0}) pos1h=({:.0},{:.0}) g@dist={:.4e}m/s²",
source, pos0.0, pos0.1, pos1.0, pos1.1, g_moon
);
println!(
" M={:.3e}kg R={:.4e}m d={:.3e}m P={:.0}s",
LUNAR_MASS, LUNAR_RADIUS, EARTH_MOON_DISTANCE, LUNAR_ORBITAL_PERIOD
);
let iss = ArtificialSatellite::leo("ISS", 420_000.0, 408_000.0);
let geo = ArtificialSatellite::geo("GEO-Sat", 300.0);
let custom = ArtificialSatellite::new("Molniya", 1500.0, 500_000.0, 0.7, 1.1);
let p_iss = iss.orbital_period_s();
let v_iss = iss.orbital_velocity_ms();
let p_geo = geo.orbital_period_s();
let g_surf = iss.gravity_at_surface();
let pos_iss = iss.position();
let pos_geo = geo.position();
let orbit_type = |sat: &ArtificialSatellite| -> &str {
match sat.orbit_type {
OrbitType::LEO => "LEO",
OrbitType::MEO => "MEO",
OrbitType::GEO => "GEO",
OrbitType::HEO => "HEO",
OrbitType::Custom => "Custom",
}
};
println!(
"ISS({}): P={:.0}s v={:.0}m/s g_surf={:.2} pos=({:.0},{:.0},{:.0})",
orbit_type(&iss),
p_iss,
v_iss,
g_surf,
pos_iss.0,
pos_iss.1,
pos_iss.2
);
println!(
"GEO({}): P={:.0}s pos=({:.0},{:.0},{:.0})",
orbit_type(&geo),
p_geo,
pos_geo.0,
pos_geo.1,
pos_geo.2
);
println!(
"Custom({}): e={:.1} i={:.1}rad",
orbit_type(&custom),
custom.eccentricity,
custom.inclination_rad
);
let mut constellation = Constellation::new("Starlink-test");
constellation.add(ArtificialSatellite::leo("S1", 260.0, 550_000.0));
constellation.add(ArtificialSatellite::leo("S2", 260.0, 550_000.0));
let mut sat_stepped = iss;
sat_stepped.step(60.0);
constellation.step_all(60.0);
let positions = constellation.positions();
println!(
"Constellation: {} sats positions={}",
positions.len(),
positions.len()
);
let dt = DateTime::new(2024, 6, 21, 12, 0, 0.0);
let jd = dt.to_julian_date();
let dt_back = DateTime::from_julian_date(jd);
let unix = dt.to_unix_timestamp();
let dt_unix = DateTime::from_unix_timestamp(unix);
println!(
"DateTime: {}-{}-{} {}:{}:{:.0} JD={:.4} unix={:.0}",
dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, jd, unix
);
println!(
" roundtrip JD: {}-{}-{} roundtrip unix: {}-{}-{}",
dt_back.year, dt_back.month, dt_back.day, dt_unix.year, dt_unix.month, dt_unix.day
);
println!(
" J2000_JD={} UNIX_JD={} SPD={}",
J2000_JD, UNIX_EPOCH_JD, SECONDS_PER_DAY
);
let mut epoch = Epoch::j2000();
let epoch_mjd = Epoch::from_mjd(51544.5);
let centuries = epoch.centuries_since_j2000();
let days = epoch.days_since_j2000();
let gmst = epoch.gmst_degrees();
let mjd = epoch.to_mjd();
epoch.advance_days(365.25);
let centuries_1yr = epoch.centuries_since_j2000();
let mut epoch2 = Epoch::from_jd(J2000_JD);
epoch2.advance_seconds(86400.0);
println!(
"Epoch: T0={:.4} days={:.1} GMST={:.2}° MJD={:.1}",
centuries, days, gmst, mjd
);
println!(
" +1yr: T={:.6} from_mjd.jd={:.1} epoch2.days={:.1}",
centuries_1yr,
epoch_mjd.julian_date,
epoch2.days_since_j2000()
);
println!(
" J2000={} J1950={} MJD_OFF={}",
J2000_EPOCH, J1950_EPOCH, MJD_OFFSET
);
let mut ts = TimeScale::realtime();
ts.step(1.0);
let sim_dt = ts.simulation_dt(1.0);
let hours = ts.sim_hours();
let days_ts = ts.sim_days();
let years = ts.sim_years();
ts.pause();
ts.resume();
ts.toggle_pause();
ts.set_speed(100.0);
let mut ff = TimeScale::fast_forward(10.0);
ff.step(1.0);
let sm = TimeScale::slow_motion(2.0);
println!(
"TimeScale: dt={:.1} h={:.6} d={:.8} yr={:.10} ff_sim={:.1}s sm_speed={:.1}",
sim_dt, hours, days_ts, years, ff.simulation_time_s, sm.speed_multiplier
);
let sun_pos = SolarPosition::compute(jd, 48.8, 2.3);
let above = sun_pos.is_above_horizon();
let dist_sun = sun_pos.distance_m();
println!(
"Solar: el={:.1}° az={:.1}° above={} dist={:.3e}m tilt={}°",
sun_pos.elevation_deg, sun_pos.azimuth_deg, above, dist_sun, EARTH_AXIAL_TILT_DEG
);
let dnc = DayNightCycle::new(jd);
let state_paris = dnc.state_at(48.8, 2.3);
let state_text = match state_paris {
DaylightState::Day => "day",
DaylightState::Night => "night",
DaylightState::CivilTwilight => "civil_twilight",
DaylightState::NauticalTwilight => "nautical_twilight",
DaylightState::AstronomicalTwilight => "astro_twilight",
};
let terminator = dnc.terminator_points(36);
let ambient = dnc.ambient_light(48.8, 2.3);
println!(
"DayNight: Paris={} ambient={:.2} terminator_pts={}",
state_text,
ambient,
terminator.len()
);
let season_state = season_at(jd, 48.8);
let season_name = match season_state.season_north {
Season::Spring => "spring",
Season::Summer => "summer",
Season::Autumn => "autumn",
Season::Winter => "winter",
};
let south_name = match season_state.season_south {
Season::Spring => "spring",
Season::Summer => "summer",
Season::Autumn => "autumn",
Season::Winter => "winter",
};
let (sub_lat, sub_lon) = subsolar_point(jd);
println!(
"Season: N={} S={} decl={:.2}° day_h={:.2} subsolar=({:.1},{:.1})",
season_name,
south_name,
season_state.solar_declination_deg,
season_state.day_length_hours,
sub_lat,
sub_lon
);
println!(
" tilt={}° trop_year={}d vernal_jd={}",
SEASONS_TILT, TROPICAL_YEAR_DAYS, VERNAL_EQUINOX_JD
);
let paris = LatLon::new(48.8566, 2.3522, 35.0);
let tokyo = LatLon::new(35.6762, 139.6503, 40.0);
let ecef_paris = paris.to_ecef();
let cart = paris.to_cartesian_simple();
let dist_pt = paris.distance_to(&tokyo);
let latlon_back = ecef_paris.to_latlon();
println!(
"Paris->ECEF: ({:.0},{:.0},{:.0}) cart=({:.0},{:.0},{:.0})",
ecef_paris.x, ecef_paris.y, ecef_paris.z, cart[0], cart[1], cart[2]
);
println!(
" dist(Paris-Tokyo)={:.0}m roundtrip_lat={:.4}°",
dist_pt, latlon_back.lat_deg
);
println!(
" f={:.9} a={:.0}m b={:.3}m",
EARTH_FLATTENING, EARTH_SEMI_MAJOR_M, EARTH_SEMI_MINOR_M
);
let regions = RegionDatabase::continents();
let region = regions.point_in_region(48.8, 2.3);
if let Some(r) = region {
let rtype = match r.region_type {
RegionType::Continent => "continent",
RegionType::Ocean => "ocean",
RegionType::Sea => "sea",
RegionType::Country => "country",
RegionType::Island => "island",
};
println!(
"Region(48.8,2.3): {} ({}) area={:.0}km²",
r.name, rtype, r.area_km2
);
}
let elev_prov = ElevationProvider::global(30.0);
let elev_everest = elev_prov.sample(27.988, 86.925);
let notables = ElevationProvider::notable_elevations();
println!(
"Elevation(Everest)={:.0}m notables={}",
elev_everest,
notables.len()
);
let bathy = BathymetryData::global(60.0);
let depth_mariana = bathy.sample(11.35, 142.2);
let is_ocean = bathy.is_ocean(0.0, -30.0);
let stats = BathymetryData::ocean_stats();
let basins = OceanBasin::major_basins();
println!(
"Bathy(Mariana)={:.0}m is_ocean(0,-30)={} basins={} avg_depth={:.0}m",
depth_mariana,
is_ocean,
basins.len(),
stats.avg_depth_m
);
let mut hmap = Heightmap::new(64);
hmap.generate_procedural(42, 6, 0.5, 2.0);
let h_sample = hmap.sample(45.0, 10.0);
let r_at = hmap.radius_at(45.0, 10.0);
println!(
"Heightmap(64): sample(45,10)={:.1}m radius={:.0}m",
h_sample, r_at
);
let config = LodConfig::default();
let mut lod = LodTerrain::new(config);
lod.update([6_371_000.0 + 1000.0, 0.0, 0.0]);
let mesh = TerrainMesh::from_region(0.0, 10.0, 0.0, 10.0, 8, &|lat, lon| hmap.sample(lat, lon));
println!(
"Mesh: {} vertices {} triangles",
mesh.vertex_count(),
mesh.triangle_count()
);
let classifier = BiomeClassifier::default();
let biome = classifier.classify(2000.0, 45.0, 0.6);
let biome_name = match biome {
Biome::Ocean => "ocean",
Biome::Beach => "beach",
Biome::Desert => "desert",
Biome::Grassland => "grassland",
Biome::Forest => "forest",
Biome::Tundra => "tundra",
Biome::Snow => "snow",
Biome::Mountain => "mountain",
Biome::Volcanic => "volcanic",
Biome::Taiga => "taiga",
};
let splat = classifier.splat(2000.0, 45.0, 0.6);
println!(
"Biome(2000m,45°,0.6)={} splat_top={:.2}",
biome_name, splat.weights[0].1
);
let face = Face::PosZ;
println!("LOD face={:?}", face);
let mat_ocean = PbrMaterial::ocean();
let mat_grass = PbrMaterial::grassland();
let mat_desert = PbrMaterial::desert();
let mat_snow = PbrMaterial::snow();
let mat_rock = PbrMaterial::rock();
let mat_volc = PbrMaterial::volcanic();
let mat_forest = PbrMaterial::forest();
let mat_ice = PbrMaterial::ice();
println!(
"Materials: ocean_rough={:.2} grass_rough={:.2} desert_metal={:.2} \
snow_rough={:.2} rock={:.2} volc={:.2} forest={:.2} ice={:.2}",
mat_ocean.roughness,
mat_grass.roughness,
mat_desert.metallic,
mat_snow.roughness,
mat_rock.roughness,
mat_volc.roughness,
mat_forest.roughness,
mat_ice.roughness
);
let sh_terrain = ShaderData::terrain();
let sh_atm = ShaderData::atmosphere();
let sh_ocean = ShaderData::ocean();
println!(
"Shaders: terrain={} uniforms atm={} ocean={}",
sh_terrain.uniforms.len(),
sh_atm.uniforms.len(),
sh_ocean.uniforms.len()
);
for u in &sh_terrain.uniforms {
let val = match &u.value {
UniformValue::Float(f) => format!("f{:.2}", f),
UniformValue::Vec3(v) => format!("v3({:.1},{:.1},{:.1})", v[0], v[1], v[2]),
UniformValue::Vec4(v) => format!("v4({:.1},{:.1},{:.1},{:.1})", v[0], v[1], v[2], v[3]),
UniformValue::Mat4(_) => "mat4".to_string(),
UniformValue::Int(i) => format!("i{}", i),
};
println!(" {}={}", u.name, val);
}
let atm_params = AtmosphereParams::default();
let ray_dens = atm_params.rayleigh_density(4000.0);
let mie_dens = atm_params.mie_density(600.0);
let scat_r = atm_params.scatter_rayleigh(0.5);
let scat_m = atm_params.scatter_mie(0.5);
let sky = atm_params.sky_color([0.0, 1.0, 0.0], [0.0, 0.5, 0.866], 0.0);
println!(
"Atmosphere: ρ_ray(4km)={:.4} ρ_mie(600m)={:.4} scat_r={:.4} scat_m={:.4}",
ray_dens, mie_dens, scat_r, scat_m
);
println!(" sky=({:.4},{:.4},{:.4})", sky[0], sky[1], sky[2]);
let ocean_rdr = OceanParams::default();
let phillips = ocean_rdr.phillips_spectrum(0.1, 0.05);
let spectrum = ocean_rdr.generate_spectrum();
let disp = ocean_rdr.dispersion(0.1);
println!(
"Ocean render: phillips(0.1,0.05)={:.6} spectrum_size={} disp={:.4}",
phillips,
spectrum.heights.len(),
disp
);
let cumulus = CloudLayer::cumulus();
let stratus = CloudLayer::stratus();
let cirrus = CloudLayer::cirrus();
let cb = CloudLayer::cumulonimbus();
let cloud_type = |ct: &CloudType| match ct {
CloudType::Cumulus => "Cu",
CloudType::Stratus => "St",
CloudType::Cirrus => "Ci",
CloudType::Cumulonimbus => "Cb",
CloudType::Altocumulus => "Ac",
};
println!(
"Clouds: {} base={:.0}m {} base={:.0}m {} base={:.0}m {} base={:.0}m",
cloud_type(&cumulus.cloud_type),
cumulus.base_altitude_m,
cloud_type(&stratus.cloud_type),
stratus.base_altitude_m,
cloud_type(&cirrus.cloud_type),
cirrus.base_altitude_m,
cloud_type(&cb.cloud_type),
cb.base_altitude_m
);
let mut clouds = CloudSystem::earth_default();
clouds.step(3600.0);
let density = clouds.sample_density(5000.0, 45.0, 10.0);
println!("CloudSystem: density@5km={:.4}", density);
let rainforest = tropical_rainforest();
let boreal = boreal_forest();
let reef = coral_reef();
println!("Ecosystems:");
for eco in [&rainforest, &boreal, &reef] {
let sp = eco.total_species();
let ind = eco.total_individuals();
let shannon = eco.shannon_index();
let simpson = eco.simpson_diversity();
let area_sp = eco.expected_species_from_area(0.25, 100.0);
let npp = eco.total_npp_gc_yr();
let turnover = eco.biomass_turnover_time_yr(15_000.0);
println!(
" {} species {} ind H'={:.2} D={:.4} SAR={:.0} NPP={:.0} τ={:.1}yr",
sp, ind, shannon, simpson, area_sp, npp, turnover
);
}
let tb = tropical_broadleaf();
let tg = temperate_grassland();
let cf = coniferous_forest();
let photo = tb.photosynthesis_rate(400.0, 1500.0, 28.0);
let canopy = tb.canopy_photosynthesis(photo);
let transp = tg.transpiration_mm_day(1.5, 0.3);
let npp_veg = cf.npp_kgc_m2_yr(15.0);
let crt = cf.carbon_residence_time_yr(npp_veg);
let light = beer_lambert_light_extinction(2000.0, 4.0, 0.5);
println!(
"Vegetation: photo={:.2} canopy={:.2} transp={:.2}mm/d NPP={:.3} CRT={:.1}yr light={:.1}",
photo, canopy, transp, npp_veg, crt, light
);
let elephant = african_elephant();
let whale = blue_whale();
let gr_el = elephant.growth_rate();
let proj = elephant.project_forward(10.0);
let metab = whale.metabolic_rate_w();
let range = elephant.home_range_km2();
let gen_time = whale.generation_time_years();
let lifespan = elephant.max_lifespan_years();
println!(
"Elephant: r={:.3} N(+10yr)={:.0} range={:.1}km² lifespan={:.0}yr",
gr_el, proj, range, lifespan
);
println!("Whale: metab={:.0}W gen={:.1}yr", metab, gen_time);
let mut pp = PredatorPrey {
prey: african_elephant(),
predator: blue_whale(),
attack_rate: 0.01,
conversion_efficiency: 0.1,
predator_death_rate: 0.05,
};
let prey_r = pp.prey_growth_rate();
let pred_r = pp.predator_growth_rate();
pp.step(0.1);
println!(
"Lotka-Volterra: prey_r={:.1} pred_r={:.1} -> prey={:.1} pred={:.2}",
prey_r, pred_r, pp.prey.count, pp.predator.count
);
}