pub fn olympus_mons_summit_m() -> f64 {
21_900.0
}
pub fn ascraeus_mons_summit_m() -> f64 {
18_200.0
}
pub fn pavonis_mons_summit_m() -> f64 {
14_000.0
}
pub fn arsia_mons_summit_m() -> f64 {
17_780.0
}
pub fn elysium_mons_summit_m() -> f64 {
14_028.0
}
pub fn alba_mons_summit_m() -> f64 {
6_800.0
}
pub fn hellas_floor_m() -> f64 {
-7_152.0
}
pub fn isidis_floor_m() -> f64 {
-3_800.0
}
pub fn argyre_floor_m() -> f64 {
-5_200.0
}
pub fn valles_marineris_floor_m() -> f64 {
-7_000.0
}
pub fn north_pole_m() -> f64 {
-5_000.0
}
pub fn south_pole_m() -> f64 {
2_500.0
}
pub fn tharsis_plateau_m() -> f64 {
10_000.0
}
pub fn northern_lowlands_mean_m() -> f64 {
-4_000.0
}
pub fn southern_highlands_mean_m() -> f64 {
1_500.0
}
pub fn dichotomy_boundary_lat_deg() -> f64 {
0.0
}
pub fn mars_elevation(lat_deg: f64, lon_deg: f64) -> f64 {
let lat = lat_deg.to_radians();
let lon = lon_deg.to_radians();
let dichotomy = if lat_deg > 0.0 { -4_000.0 } else { 1_500.0 };
let tharsis_lon = (-110.0_f64).to_radians();
let tharsis_dist = ((lat).powi(2) + (lon - tharsis_lon).powi(2)).sqrt();
let tharsis = 10_000.0 * (-tharsis_dist.powi(2) / (0.5_f64).powi(2)).exp();
let hellas_lat = (-42.0_f64).to_radians();
let hellas_lon = 70.0_f64.to_radians();
let hellas_dist = ((lat - hellas_lat).powi(2) + (lon - hellas_lon).powi(2)).sqrt();
let hellas = -7_000.0 * (-hellas_dist.powi(2) / (0.15_f64).powi(2)).exp();
let oly_lat = 18.65_f64.to_radians();
let oly_lon = (-134.0_f64).to_radians();
let oly_dist = ((lat - oly_lat).powi(2) + (lon - oly_lon).powi(2)).sqrt();
let olympus = 21_000.0 * (-oly_dist.powi(2) / (0.05_f64).powi(2)).exp();
dichotomy + tharsis + hellas + olympus
}
pub struct Heightmap {
pub data: Vec<f64>,
pub width: usize,
pub height: usize,
}
impl Heightmap {
pub fn generate(width: usize, height: usize) -> Self {
let mut data = vec![0.0; width * height];
for j in 0..height {
let lat = 90.0 - (j as f64 / height as f64) * 180.0;
for i in 0..width {
let lon = (i as f64 / width as f64) * 360.0 - 180.0;
data[j * width + i] = mars_elevation(lat, lon);
}
}
Self {
data,
width,
height,
}
}
pub fn sample(&self, lat_deg: f64, lon_deg: f64) -> f64 {
let u = ((lon_deg + 180.0) / 360.0).clamp(0.0, 1.0);
let v = ((90.0 - lat_deg) / 180.0).clamp(0.0, 1.0);
let x = u * (self.width - 1) as f64;
let y = v * (self.height - 1) as f64;
let ix = (x as usize).min(self.width - 2);
let iy = (y as usize).min(self.height - 2);
let fx = x - ix as f64;
let fy = y - iy as f64;
let v00 = self.data[iy * self.width + ix];
let v10 = self.data[iy * self.width + ix + 1];
let v01 = self.data[(iy + 1) * self.width + ix];
let v11 = self.data[(iy + 1) * self.width + ix + 1];
v00 * (1.0 - fx) * (1.0 - fy)
+ v10 * fx * (1.0 - fy)
+ v01 * (1.0 - fx) * fy
+ v11 * fx * fy
}
pub fn radius_at(&self, lat_deg: f64, lon_deg: f64) -> f64 {
crate::MARS_RADIUS + self.sample(lat_deg, lon_deg)
}
pub fn min_max(&self) -> (f64, f64) {
let min = self.data.iter().cloned().fold(f64::INFINITY, f64::min);
let max = self.data.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
(min, max)
}
}