jupiters 0.0.3

Jupiter celestial simulation crate for the MilkyWay SolarSystem workspace
Documentation
fn cloudtopvariation(lat: f64, lon: f64) -> f64 {
    let bandamplitude = 5000.0;
    let bandfreq = 6.0;
    let latrad = lat.to_radians();
    let lonrad = lon.to_radians();
    let bands = bandamplitude * (bandfreq * latrad).cos();
    let grslat = -22.0f64;
    let grslon = 300.0f64;
    let dlat = (lat - grslat) / 12.0;
    let dlon = shortestlondiff(lon, grslon) / 20.0;
    let grsd2 = dlat * dlat + dlon * dlon;
    let grs = 8000.0 * (-grsd2 * 0.5).exp();
    let ovalbalt = -33.0;
    let ovalbalon = 107.0;
    let dlat2 = (lat - ovalbalt) / 6.0;
    let dlon2 = shortestlondiff(lon, ovalbalon) / 10.0;
    let ovalba = 4000.0 * (-(dlat2 * dlat2 + dlon2 * dlon2) * 0.5).exp();
    let polarcap = if lat.abs() > 65.0 {
        2000.0 * ((lat.abs() - 65.0) / 25.0)
    } else {
        0.0
    };
    let zonalwave = 800.0 * (3.0 * lonrad).sin() * (4.0 * latrad).cos();
    bands + grs + ovalba + polarcap + zonalwave
}

fn shortestlondiff(a: f64, b: f64) -> f64 {
    let mut d = a - b;
    if d > 180.0 {
        d -= 360.0;
    }
    if d < -180.0 {
        d += 360.0;
    }
    d
}

pub fn jupitercloudtop(lat: f64, lon: f64) -> f64 {
    cloudtopvariation(lat, lon)
}

pub struct Heightmap {
    pub resolution: u32,
    pub data: Vec<f64>,
    pub minelevationm: f64,
    pub maxelevationm: f64,
}

impl Heightmap {
    pub fn new(resolution: u32) -> Self {
        let size = (resolution * resolution) as usize;
        Self {
            resolution,
            data: vec![0.0; size],
            minelevationm: -5000.0,
            maxelevationm: 12000.0,
        }
    }

    pub fn sample(&self, latdeg: f64, londeg: f64) -> f64 {
        let u = ((londeg + 180.0) / 360.0).clamp(0.0, 1.0);
        let v = ((latdeg + 90.0) / 180.0).clamp(0.0, 1.0);
        let x = (u * (self.resolution - 1) as f64) as usize;
        let y = (v * (self.resolution - 1) as f64) as usize;
        let idx = y * self.resolution as usize + x;
        self.data.get(idx).copied().unwrap_or(0.0)
    }

    pub fn radiusat(&self, latdeg: f64, londeg: f64) -> f64 {
        crate::JUPITEREQUATORIALRADIUS + self.sample(latdeg, londeg)
    }

    pub fn generatejupitercloudtopography(&mut self) {
        for y in 0..self.resolution {
            for x in 0..self.resolution {
                let lon = (x as f64 / (self.resolution - 1) as f64) * 360.0 - 180.0;
                let lat = (y as f64 / (self.resolution - 1) as f64) * 180.0 - 90.0;
                let idx = (y * self.resolution + x) as usize;
                self.data[idx] = jupitercloudtop(lat, lon);
            }
        }
    }

    pub fn generateprocedural(
        &mut self,
        seed: u64,
        octaves: u32,
        persistence: f64,
        lacunarity: f64,
    ) {
        let mut rngstate = seed;
        for y in 0..self.resolution {
            for x in 0..self.resolution {
                let mut amplitude = 1.0;
                let mut frequency = 1.0;
                let mut value = 0.0;
                for octave in 0..octaves {
                    rngstate = rngstate
                        .wrapping_mul(6364136223846793005)
                        .wrapping_add(frequency as u64 + octave as u64);
                    let noise = ((rngstate >> 33) as f64 / u32::MAX as f64) * 2.0 - 1.0;
                    value += noise * amplitude;
                    amplitude *= persistence;
                    frequency *= lacunarity;
                }
                let range = self.maxelevationm - self.minelevationm;
                let idx = (y * self.resolution + x) as usize;
                self.data[idx] = self.minelevationm + (value * 0.5 + 0.5) * range;
            }
        }
    }
}