#![allow(dead_code)]
use std::f32::consts::PI;
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct SunPositionConfig {
pub azimuth_deg: f32,
pub elevation_deg: f32,
pub disk_size_px: f32,
pub sun_color: [f32; 4],
pub visible: bool,
pub show_arrow: bool,
}
impl Default for SunPositionConfig {
fn default() -> Self {
Self {
azimuth_deg: 180.0,
elevation_deg: 45.0,
disk_size_px: 20.0,
sun_color: [1.0, 0.95, 0.7, 1.0],
visible: true,
show_arrow: false,
}
}
}
#[allow(dead_code)]
pub fn new_sun_position_config() -> SunPositionConfig {
SunPositionConfig::default()
}
#[allow(dead_code)]
pub fn sun_direction_vector(cfg: &SunPositionConfig) -> [f32; 3] {
let az = cfg.azimuth_deg * PI / 180.0;
let el = cfg.elevation_deg * PI / 180.0;
let x = el.cos() * az.sin();
let y = el.sin();
let z = el.cos() * az.cos();
[x, y, z]
}
#[allow(dead_code)]
pub fn sp_set_azimuth(cfg: &mut SunPositionConfig, deg: f32) {
cfg.azimuth_deg = deg.rem_euclid(360.0);
}
#[allow(dead_code)]
pub fn sp_set_elevation(cfg: &mut SunPositionConfig, deg: f32) {
cfg.elevation_deg = deg.clamp(-90.0, 90.0);
}
#[allow(dead_code)]
pub fn sp_is_above_horizon(cfg: &SunPositionConfig) -> bool {
cfg.elevation_deg > 0.0
}
#[allow(dead_code)]
pub fn sp_sky_luminance(cfg: &SunPositionConfig) -> f32 {
let el = cfg.elevation_deg.clamp(-10.0, 90.0);
let t = (el + 10.0) / 100.0;
t.clamp(0.0, 1.0)
}
#[allow(dead_code)]
pub fn sun_position_to_json(cfg: &SunPositionConfig) -> String {
format!(
r#"{{"azimuth_deg":{:.2},"elevation_deg":{:.2},"visible":{}}}"#,
cfg.azimuth_deg, cfg.elevation_deg, cfg.visible
)
}
#[cfg(test)]
mod tests {
use super::*;
use std::f32::consts::PI;
#[test]
fn test_default() {
let c = SunPositionConfig::default();
assert!(sp_is_above_horizon(&c));
}
#[test]
fn test_sun_dir_normalized() {
let c = SunPositionConfig::default();
let d = sun_direction_vector(&c);
let len = (d[0] * d[0] + d[1] * d[1] + d[2] * d[2]).sqrt();
assert!((len - 1.0).abs() < 1e-5);
}
#[test]
fn test_set_azimuth_wrap() {
let mut c = SunPositionConfig::default();
sp_set_azimuth(&mut c, 370.0);
assert!((c.azimuth_deg - 10.0).abs() < 1e-4);
}
#[test]
fn test_set_elevation_clamp() {
let mut c = SunPositionConfig::default();
sp_set_elevation(&mut c, 200.0);
assert!((c.elevation_deg - 90.0).abs() < 1e-5);
}
#[test]
fn test_below_horizon() {
let c = SunPositionConfig {
elevation_deg: -5.0,
..Default::default()
};
assert!(!sp_is_above_horizon(&c));
}
#[test]
fn test_sky_luminance_zenith() {
let c = SunPositionConfig {
elevation_deg: 90.0,
..Default::default()
};
let l = sp_sky_luminance(&c);
assert!(l > 0.9);
}
#[test]
fn test_sky_luminance_night() {
let c = SunPositionConfig {
elevation_deg: -10.0,
..Default::default()
};
let l = sp_sky_luminance(&c);
assert!(l.abs() < 1e-5);
}
#[test]
fn test_sun_dir_at_zenith() {
let c = SunPositionConfig {
elevation_deg: 90.0,
azimuth_deg: 0.0,
..Default::default()
};
let d = sun_direction_vector(&c);
assert!((d[1] - 1.0).abs() < 1e-5);
}
#[test]
fn test_pi_used() {
let _pi = PI;
let c = SunPositionConfig {
azimuth_deg: 180.0,
elevation_deg: 0.0,
..Default::default()
};
let d = sun_direction_vector(&c);
assert!(d[0].abs() < 1e-5);
}
#[test]
fn test_to_json() {
let j = sun_position_to_json(&SunPositionConfig::default());
assert!(j.contains("azimuth_deg"));
assert!(j.contains("visible"));
}
}