use vedaksha_math::angle::{deg_to_rad, normalize_degrees, rad_to_deg};
use super::{HouseCusps, HouseSystem};
#[allow(clippy::cast_precision_loss)]
fn campanus_cusp(ramc_deg: f64, lat_deg: f64, eps_deg: f64, house_index: usize) -> f64 {
let ramc = deg_to_rad(ramc_deg);
let lat = deg_to_rad(lat_deg);
let eps = deg_to_rad(eps_deg);
let pv_angle = deg_to_rad((house_index as f64) * 30.0);
let cos_lat = libm::cos(lat);
let sin_lat = libm::sin(lat);
let cos_eps = libm::cos(eps);
let sin_eps = libm::sin(eps);
let sin_ramc = libm::sin(ramc);
let cos_ramc = libm::cos(ramc);
let sin_a = libm::sin(pv_angle);
let cos_a = libm::cos(pv_angle);
let u_x = cos_ramc * cos_lat;
let u_y = sin_ramc * cos_lat;
let u_z = sin_lat;
let e_x = -sin_ramc;
let e_y = cos_ramc;
let e_z = 0.0;
let n_x = u_y * e_z - u_z * e_y; let n_y = u_z * e_x - u_x * e_z; let n_z = u_x * e_y - u_y * e_x;
let p_x = cos_a * e_x + 0.0 * n_x + (-sin_a) * u_x;
let p_y = cos_a * e_y + 0.0 * n_y + (-sin_a) * u_y;
let p_z = cos_a * e_z + 0.0 * n_z + (-sin_a) * u_z;
let nh_x = n_x; let nh_y = n_y; let nh_z = n_z;
let hc_x = p_y * nh_z - p_z * nh_y;
let hc_y = p_z * nh_x - p_x * nh_z;
let hc_z = p_x * nh_y - p_y * nh_x;
let en_x = 0.0;
let en_y = -sin_eps;
let en_z = cos_eps;
let ix = en_y * hc_z - en_z * hc_y;
let iy = en_z * hc_x - en_x * hc_z;
let iz = en_x * hc_y - en_y * hc_x;
let ecl_x = ix;
let ecl_y = iy * cos_eps + iz * sin_eps;
let dot = ix * p_x + iy * p_y + iz * p_z;
let (fx, fy) = if dot < 0.0 {
(-ecl_x, -ecl_y)
} else {
(ecl_x, ecl_y)
};
normalize_degrees(rad_to_deg(libm::atan2(fy, fx)))
}
pub(super) fn compute(ramc: f64, latitude: f64, obliquity: f64) -> HouseCusps {
let (asc, mc) = super::compute_asc_mc(ramc, latitude, obliquity);
let dsc = normalize_degrees(asc + 180.0);
let ic = normalize_degrees(mc + 180.0);
let mut cusps = core::array::from_fn(|i| campanus_cusp(ramc, latitude, obliquity, i));
cusps[0] = asc; cusps[3] = ic; cusps[6] = dsc; cusps[9] = mc;
HouseCusps {
cusps,
asc,
mc,
system: HouseSystem::Campanus,
polar_fallback: false,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn all_cusps_in_range() {
let result = compute(30.0, 45.0, 23.44);
for (i, &c) in result.cusps.iter().enumerate() {
assert!((0.0..360.0).contains(&c), "cusp {i}: {c}");
}
}
#[test]
fn cusp1_reasonable() {
let result = compute(30.0, 45.0, 23.44);
let diff = normalize_degrees(result.cusps[0] - result.asc);
assert!(
diff < 15.0 || diff > 345.0,
"cusp1={} asc={} diff={diff}",
result.cusps[0],
result.asc
);
}
#[test]
fn system_tag() {
let result = compute(0.0, 0.0, 23.44);
assert_eq!(result.system, HouseSystem::Campanus);
}
}