pub const TILE_SIZE: u32 = 65;
pub const MIN_ELEVATION: f64 = -1000.0;
pub const MAX_ELEVATION: f64 = 12107.0;
pub const SCALE: f64 = 5.0;
#[inline]
pub fn elevation_to_u16(elevation: f64) -> u16 {
let clamped = elevation.clamp(MIN_ELEVATION, MAX_ELEVATION);
((clamped - MIN_ELEVATION) * SCALE).round() as u16
}
#[inline]
pub fn u16_to_elevation(value: u16) -> f64 {
value as f64 / SCALE + MIN_ELEVATION
}
pub fn encode_heights_into(elevations: &[f64], width: u32, height: u32, out: &mut [u8]) {
let expected = (width as usize) * (height as usize);
assert!(
elevations.len() >= expected,
"expected at least {expected} elevations, got {}",
elevations.len()
);
assert_eq!(
out.len(),
expected * 2,
"out buffer length mismatch: expected {}, got {}",
expected * 2,
out.len()
);
for (&e, chunk) in elevations[..expected].iter().zip(out.chunks_exact_mut(2)) {
chunk.copy_from_slice(&elevation_to_u16(e).to_le_bytes());
}
}
pub fn encode_heights_to<W: std::io::Write>(
elevations: &[f64],
width: u32,
height: u32,
mut writer: W,
) -> std::io::Result<()> {
let expected = (width as usize) * (height as usize);
assert!(
elevations.len() >= expected,
"expected at least {expected} elevations, got {}",
elevations.len()
);
let mut buf = [0u8; 4096];
let mut len = 0;
for &e in &elevations[..expected] {
let bytes = elevation_to_u16(e).to_le_bytes();
buf[len] = bytes[0];
buf[len + 1] = bytes[1];
len += 2;
if len + 2 > buf.len() {
writer.write_all(&buf[..len])?;
len = 0;
}
}
if len > 0 {
writer.write_all(&buf[..len])?;
}
Ok(())
}
pub fn encode_heights(elevations: &[f64], width: u32, height: u32) -> Vec<u8> {
let expected = (width as usize) * (height as usize);
let mut out = vec![0u8; expected * 2];
encode_heights_into(elevations, width, height, &mut out);
out
}
pub fn iter_heights(data: &[u8]) -> impl Iterator<Item = f64> + '_ {
data.chunks_exact(2)
.map(|c| u16_to_elevation(u16::from_le_bytes([c[0], c[1]])))
}
pub fn decode_heights(data: &[u8]) -> Vec<f64> {
iter_heights(data).collect()
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct ChildTileMask {
pub southwest: bool,
pub southeast: bool,
pub northwest: bool,
pub northeast: bool,
}
impl ChildTileMask {
pub const fn all() -> Self {
Self {
southwest: true,
southeast: true,
northwest: true,
northeast: true,
}
}
pub const fn none() -> Self {
Self {
southwest: false,
southeast: false,
northwest: false,
northeast: false,
}
}
pub const fn to_byte(self) -> u8 {
let mut mask = 0u8;
if self.southwest {
mask |= 1;
}
if self.southeast {
mask |= 2;
}
if self.northwest {
mask |= 4;
}
if self.northeast {
mask |= 8;
}
mask
}
pub const fn from_byte(byte: u8) -> Self {
Self {
southwest: byte & 1 != 0,
southeast: byte & 2 != 0,
northwest: byte & 4 != 0,
northeast: byte & 8 != 0,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn elevation_roundtrip_within_range() {
for &e in &[-1000.0_f64, 0.0, 100.0, 8848.0, 12107.0] {
let back = u16_to_elevation(elevation_to_u16(e));
assert!((e - back).abs() < 0.2, "{e} → {back}");
}
}
#[test]
fn elevation_clamps_out_of_range() {
let low = u16_to_elevation(elevation_to_u16(-2000.0));
assert!((low - MIN_ELEVATION).abs() < 0.2);
let high = u16_to_elevation(elevation_to_u16(20000.0));
assert!((high - MAX_ELEVATION).abs() < 0.2);
}
#[test]
fn encode_decode_bulk() {
let elevations: Vec<f64> = vec![0.0, 100.0, -100.0, 1000.0];
let bytes = encode_heights(&elevations, 2, 2);
assert_eq!(bytes.len(), 8); let back = decode_heights(&bytes);
for (a, b) in elevations.iter().zip(&back) {
assert!((a - b).abs() < 0.2);
}
}
#[test]
fn child_mask_bits_match_spec() {
assert_eq!(ChildTileMask::all().to_byte(), 0b1111);
assert_eq!(ChildTileMask::none().to_byte(), 0);
let only_se = ChildTileMask {
southeast: true,
..Default::default()
};
assert_eq!(only_se.to_byte(), 0b0010);
assert_eq!(
ChildTileMask::from_byte(0b1010),
ChildTileMask {
southwest: false,
southeast: true,
northwest: false,
northeast: true,
}
);
}
}