moons 0.0.3

Moon celestial simulation crate for the MilkyWay SolarSystem workspace
Documentation
use moons::surface::heightmaps::{HeightProfile, polar_shadow_fraction};
use moons::surface::mapping::{SelenographicCoord, antipode, great_circle_distance_km};
use moons::surface::zones::{LunarZone, zone_from_lat_lon};

fn ensure_earth_binary() {
    let earth = moons::interactions::earths::ensure_earths_binary_or_simulate();
    assert!(earth.axial_tilt_deg > 20.0);
}

#[test]
fn near_side_and_polar_zones_are_detected() {
    ensure_earth_binary();
    assert_eq!(zone_from_lat_lon(0.0, 20.0).zone, LunarZone::NearSideMaria);
    assert_eq!(
        zone_from_lat_lon(-89.0, 10.0).zone,
        LunarZone::SouthPolarShadow
    );
}

#[test]
fn great_circle_distance_is_positive() {
    ensure_earth_binary();
    let a = SelenographicCoord::new(0.0, 0.0, 0.0);
    let b = SelenographicCoord::new(0.0, 90.0, 0.0);
    assert!(great_circle_distance_km(a, b) > 2_000.0);
}

#[test]
fn antipode_flips_latitude_and_elevation() {
    ensure_earth_binary();
    let coord = SelenographicCoord::new(12.0, 45.0, 100.0);
    let anti = antipode(coord);
    assert_eq!(anti.latitude_deg, -12.0);
    assert_eq!(anti.elevation_m, -100.0);
}

#[test]
fn roughness_increases_with_sample_variation() {
    ensure_earth_binary();
    let smooth = HeightProfile::new(vec![0.0, 1.0, 2.0, 3.0]);
    let rough = HeightProfile::new(vec![0.0, 10.0, -5.0, 20.0]);
    assert!(rough.roughness_index() > smooth.roughness_index());
}

#[test]
fn shadow_fraction_is_bounded() {
    ensure_earth_binary();
    let fraction = polar_shadow_fraction(1.0, 15.0);
    assert!((0.0..=1.0).contains(&fraction));
}