///|
pub struct SolarAngle {
priv angle : Double
priv rise_name : String
priv set_name : String
} derive(Eq, Debug)
///|
/// Config is immutable to callers; constructor copies the extra-angle array.
pub struct SolarConfig {
priv height : Double
priv extra : Array[SolarAngle]
} derive(Eq, Debug)
///|
fn standard_angles() -> Array[(Double, String, String)] {
[
(-0.833, "sunrise", "sunset"),
(-0.3, "sunriseEnd", "sunsetStart"),
(-6.0, "dawn", "dusk"),
(-12.0, "nauticalDawn", "nauticalDusk"),
(-18.0, "nightEnd", "night"),
(6.0, "goldenHourEnd", "goldenHour"),
]
}
///|
/// A geometric solar-centre angle, not the refracted altitude of sun_position.
pub fn solar_angle(
angle : Double,
rise_name : String,
set_name : String,
) -> Result[SolarAngle, InputError] {
if !(angle > -90.0 && angle < 90.0) ||
rise_name.length() == 0 ||
set_name.length() == 0 ||
rise_name.length() > 64 ||
set_name.length() > 64 ||
rise_name == set_name {
return Err(InvalidAngle)
}
Ok({ angle, rise_name, set_name, })
}
///|
/// Height above the visible horizon, 0..10000 m; up to 32 extra angle pairs.
/// Duplicate/reserved names are rejected instead of overwriting standard results.
pub fn solar_config(
height_m : Double,
extra : Array[SolarAngle],
) -> Result[SolarConfig, InputError] {
if !(height_m >= 0.0 && height_m <= 10000.0) {
return Err(InvalidHeight)
}
if extra.length() > 32 {
return Err(TooManyEvents)
}
let names : Array[String] = ["solarNoon", "nadir", "alwaysUp", "alwaysDown"]
for row in standard_angles() {
names.push(row.1)
names.push(row.2)
}
for e in extra {
if names.contains(e.rise_name) || names.contains(e.set_name) {
return Err(InvalidAngle)
}
names.push(e.rise_name)
names.push(e.set_name)
}
Ok({ height: height_m, extra: extra.copy(), })
}