Earth and moon eclipse

This commit is contained in:
connor committed 2026-10-14 21:56:11 -04:00
1 parent ff2ef10804
commit 85896b17a4
16 files changed
+575 -24

No files matched your search

+4
View File
@@ -27,3 +27,7 @@ serde = { version = "1.0", features = ["derive"] }
toml = "1.1"
image = { version = "0.25", default-features = false, features = ["png"] }
hifitime = { version = "4.3", optional = true }
[dev-dependencies]
# JPEG too, so the tests can write a small stand-in for the Blue Marble images.
image = { version = "0.25", default-features = false, features = ["png", "jpeg"] }
+2 -2
View File
@@ -45,8 +45,8 @@ struct RenderArgs {
/// An 8-bit sRGB PNG for viewing: bias subtracted, saturation as white.
#[arg(long)]
preview: Option<PathBuf>,
/// Lighting fidelity tier: L0, or L1 (adds Earth's surface, shadows on targets and
/// light from Earth and the Moon; needs Blue Marble data).
/// Lighting fidelity tier: L0, or L1 (adds Earth's surface, shadows on targets,
/// light from Earth and the Moon, and eclipses on them; needs Blue Marble data).
#[arg(long, default_value = "L0")]
lighting: LightingTier,
/// Camera fidelity tier: C0.
+73 -1
View File
@@ -27,8 +27,37 @@ fn resolved(args: &[&str]) -> std::process::Output {
/// Renders `scene` on `adapter` to a 16-bit PNG and returns its pixels as DN (the
/// file holds 12-bit DN shifted up by 4).
fn render(scene: &str, adapter: &str) -> image::ImageBuffer<image::Rgb<u16>, Vec<u16>> {
render_at(scene, adapter, "L0")
}
/// A data directory whose Earth images are small and uniformly gray, for tests at
/// lighting L1 that do not need Blue Marble: loading the real 16384 x 8192 images takes
/// seconds in a debug build, and they are only there once fetched.
fn gray_earth() -> PathBuf {
static DIR: std::sync::OnceLock<PathBuf> = std::sync::OnceLock::new();
DIR.get_or_init(|| {
let dir = out_dir().join("gray-data");
std::fs::create_dir_all(dir.join("earth")).unwrap();
let image = image::RgbImage::from_pixel(64, 32, image::Rgb([128; 3]));
for month in 1..=12 {
image
.save(dir.join(format!("earth/bmng-2004-{month:02}.jpg")))
.unwrap();
}
dir
})
.clone()
}
/// [`render`] at lighting tier `lighting`, with Earth's images from [`gray_earth`].
fn render_at(
scene: &str,
adapter: &str,
lighting: &str,
) -> image::ImageBuffer<image::Rgb<u16>, Vec<u16>> {
let data = gray_earth();
let out = out_dir().join(format!(
"{}-{adapter}.png",
"{}-{lighting}-{adapter}.png",
Path::new(scene).file_stem().unwrap().to_str().unwrap()
));
let preview = out.with_extension("preview.png");
@@ -38,6 +67,10 @@ fn render(scene: &str, adapter: &str) -> image::ImageBuffer<image::Rgb<u16>, Vec
scene,
"--adapter",
adapter,
"--lighting",
lighting,
"--data",
data.to_str().unwrap(),
"--out",
out.to_str().unwrap(),
"--preview",
@@ -92,6 +125,45 @@ fn epoch_scene_renders() {
assert_eq!(frame.get_pixel(0, 0).0, [64; 3]);
}
#[cfg(feature = "astrokit")]
#[test]
fn lunar_eclipse_darkens_the_moon() {
// Mid-totality: at L1 the whole Moon is in Earth's umbra and reads the bias. At L0
// there are no eclipses on the bodies and it is a full Moon.
let scene = "examples/scenes/lunar-eclipse.toml";
let l0 = render_at(scene, "gpu", "L0");
let l1 = render_at(scene, "gpu", "L1");
assert!(
l0.get_pixel(960, 540).0.iter().all(|&v| v > 150),
"{:?}",
l0.get_pixel(960, 540)
);
assert!(
l1.pixels().all(|p| p.0 == [64; 3]),
"every pixel at the bias"
);
}
#[cfg(feature = "astrokit")]
#[test]
fn solar_eclipse_puts_the_umbra_at_greatest_eclipse() {
// The camera looks straight down at the point of greatest eclipse from NASA's
// bulletin (65.2 N 25.2 W, 17:46 UTC): at L1 it is in the Moon's umbra and reads the
// bias, and the image corner, about 3500 km away in the penumbra, is brighter. At L0
// the center is lit.
let scene = "examples/scenes/solar-eclipse.toml";
let l0 = render_at(scene, "gpu", "L0");
let l1 = render_at(scene, "gpu", "L1");
assert!(
l0.get_pixel(960, 540).0.iter().all(|&v| v > 100),
"{:?}",
l0.get_pixel(960, 540)
);
assert_eq!(l1.get_pixel(960, 540).0, [64; 3]);
let corner = l1.get_pixel(40, 1040).0;
assert!(corner.iter().all(|&v| v > 70), "{corner:?}");
}
#[test]
fn errors_are_reported() {
let missing = resolved(&["render", "--scene", "no/such.toml", "--preview", "x.png"]);