Skip to main content

proof_engine/weather/
mod.rs

1pub mod atmosphere;
2pub mod precipitation;
3pub mod climate;
4
5pub use atmosphere::*;
6pub use precipitation::*;
7pub use climate::*;
8
9// Shared utilities expected by submodules.
10
11/// Simple 3D vector used throughout weather simulation.
12#[derive(Clone, Copy, Debug, Default, PartialEq)]
13pub struct Vec3 {
14    pub x: f32,
15    pub y: f32,
16    pub z: f32,
17}
18
19impl Vec3 {
20    pub const ZERO: Self = Self { x: 0.0, y: 0.0, z: 0.0 };
21    pub fn new(x: f32, y: f32, z: f32) -> Self { Self { x, y, z } }
22    pub fn length(&self) -> f32 { (self.x * self.x + self.y * self.y + self.z * self.z).sqrt() }
23    pub fn normalize(&self) -> Self {
24        let len = self.length();
25        if len < 1e-12 { Self::ZERO } else { Self { x: self.x / len, y: self.y / len, z: self.z / len } }
26    }
27    pub fn dot(&self, other: &Self) -> f32 { self.x * other.x + self.y * other.y + self.z * other.z }
28    pub fn scale(&self, s: f32) -> Self { Self { x: self.x * s, y: self.y * s, z: self.z * s } }
29    pub fn lerp(self, other: Self, t: f32) -> Self {
30        Self { x: self.x + (other.x - self.x) * t, y: self.y + (other.y - self.y) * t, z: self.z + (other.z - self.z) * t }
31    }
32    pub fn add(self, other: Self) -> Self { Self { x: self.x + other.x, y: self.y + other.y, z: self.z + other.z } }
33    pub fn sub(self, other: Self) -> Self { Self { x: self.x - other.x, y: self.y - other.y, z: self.z - other.z } }
34}
35
36impl std::ops::Add for Vec3 {
37    type Output = Self;
38    fn add(self, rhs: Self) -> Self { Self { x: self.x + rhs.x, y: self.y + rhs.y, z: self.z + rhs.z } }
39}
40
41impl std::ops::Sub for Vec3 {
42    type Output = Self;
43    fn sub(self, rhs: Self) -> Self { Self { x: self.x - rhs.x, y: self.y - rhs.y, z: self.z - rhs.z } }
44}
45
46impl std::ops::Mul<f32> for Vec3 {
47    type Output = Self;
48    fn mul(self, rhs: f32) -> Self { Self { x: self.x * rhs, y: self.y * rhs, z: self.z * rhs } }
49}
50
51impl std::ops::AddAssign for Vec3 {
52    fn add_assign(&mut self, rhs: Self) { self.x += rhs.x; self.y += rhs.y; self.z += rhs.z; }
53}
54
55impl std::ops::MulAssign<f32> for Vec3 {
56    fn mul_assign(&mut self, rhs: f32) { self.x *= rhs; self.y *= rhs; self.z *= rhs; }
57}
58
59/// Linear interpolation.
60pub fn lerp(a: f32, b: f32, t: f32) -> f32 {
61    a + (b - a) * t
62}
63
64/// Hermite smooth-step.
65pub fn smoothstep(edge0: f32, edge1: f32, x: f32) -> f32 {
66    let t = ((x - edge0) / (edge1 - edge0)).clamp(0.0, 1.0);
67    t * t * (3.0 - 2.0 * t)
68}
69
70// Simple hash-based value noise for weather simulation (no external deps).
71fn hash_u32(mut x: u32) -> u32 {
72    x = x.wrapping_mul(0x85ebca6b);
73    x ^= x >> 13;
74    x = x.wrapping_mul(0xc2b2ae35);
75    x ^= x >> 16;
76    x
77}
78
79fn hash_2d(ix: i32, iy: i32) -> f32 {
80    let h = hash_u32((ix as u32).wrapping_mul(73856093) ^ (iy as u32).wrapping_mul(19349663));
81    (h & 0x00ff_ffff) as f32 / 0x00ff_ffff as f32
82}
83
84/// 2D value noise in [0, 1].
85pub fn value_noise_2d(x: f32, y: f32) -> f32 {
86    let ix = x.floor() as i32;
87    let iy = y.floor() as i32;
88    let fx = x - ix as f32;
89    let fy = y - iy as f32;
90    let u = fx * fx * (3.0 - 2.0 * fx);
91    let v = fy * fy * (3.0 - 2.0 * fy);
92
93    let c00 = hash_2d(ix, iy);
94    let c10 = hash_2d(ix + 1, iy);
95    let c01 = hash_2d(ix, iy + 1);
96    let c11 = hash_2d(ix + 1, iy + 1);
97
98    lerp(lerp(c00, c10, u), lerp(c01, c11, u), v)
99}
100
101/// FBM (fractional Brownian motion) from value noise.
102pub fn fbm_2d(x: f32, y: f32, octaves: u32, lacunarity: f32, gain: f32) -> f32 {
103    let mut sum = 0.0f32;
104    let mut amp = 1.0f32;
105    let mut freq = 1.0f32;
106    let mut max_amp = 0.0f32;
107    for _ in 0..octaves {
108        sum += value_noise_2d(x * freq, y * freq) * amp;
109        max_amp += amp;
110        amp *= gain;
111        freq *= lacunarity;
112    }
113    sum / max_amp
114}