use crate::errors::RocheError;
use crate::{Etype, Vec3};
use std::f64::consts::TAU;
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum Circle {
Above,
Inside,
Outside,
Separate,
Crossing,
}
pub fn disc_eclipse(
iangle: f64,
rdisc1: f64,
rdisc2: f64,
beta: f64,
height: f64,
r: &Vec3,
) -> Result<Etype, RocheError> {
if beta <= 1.0 {
return Err(RocheError::ParameterError(
"beta must be >= 1.0".to_string(),
));
}
let sini: f64;
let cosi: f64;
(sini, cosi) = iangle.to_radians().sin_cos();
let mut temp = Etype::new();
let h_out: f64 = height * rdisc2.powf(beta);
if r.z >= h_out {
return Ok(temp);
}
if cosi == 0.0 {
if r.z.abs() < h_out {
let rxy: f64 = (r.x * r.x + r.y * r.y).sqrt();
if rxy <= rdisc2 {
temp.push((0.0, 1.0));
}
}
return Ok(temp);
}
let rxy: f64 = (r.x * r.x + r.y * r.y).sqrt();
if rdisc1 < rxy && rxy < rdisc2 && r.z.abs() < height * rxy.powf(beta) {
temp.push((0.0, 1.1));
return Ok(temp);
}
let tani: f64 = sini / cosi;
let mut result: Circle;
let mut phase: f64 = 0.0;
let mut ingress: f64;
let mut egress: f64;
if rxy < rdisc2 && r.z >= height * rdisc1.max(rxy).powf(beta) {
result = circle_eclipse(rxy, r.z, h_out, rdisc2, tani, &mut phase);
if result == Circle::Outside {
temp.push((0.0, 1.1));
} else if result == Circle::Crossing {
let phi0: f64 = r.y.atan2(r.x) / TAU;
ingress = phi0 + phase;
ingress -= ingress.floor();
egress = ingress + 1.0 - 2.0 * phase;
temp.push((ingress, egress));
}
return Ok(temp);
}
let rcone_lo: f64 = 0.0_f64.max(tani * (-h_out - r.z));
if rcone_lo >= rxy + rdisc2 {
return Ok(temp);
}
let rcone_hi: f64 = tani * (h_out - r.z);
if rxy >= rcone_hi + rdisc2 {
return Ok(temp);
}
let eclipse_phase: f64;
if rxy + rcone_lo <= rdisc2 {
eclipse_phase = 0.5;
} else if rxy <= rdisc2 {
eclipse_phase = cut_phase(rxy, rcone_lo, rdisc2);
} else {
if rcone_hi * rcone_hi + rdisc2 * rdisc2 >= rxy * rxy
&& rcone_lo * rcone_lo + rdisc2 * rdisc2 <= rxy * rxy
{
eclipse_phase = (rdisc2 / rxy).asin() / TAU;
} else if rcone_hi * rcone_hi + rdisc2 * rdisc2 < rxy * rxy {
eclipse_phase = cut_phase(rxy, rcone_hi, rdisc2);
} else {
eclipse_phase = cut_phase(rxy, rcone_lo, rdisc2);
}
}
let h_in: f64 = height * rdisc1.powf(beta);
let mut appear_phase: f64 = -1.0;
if r.z < -h_out {
result = circle_eclipse(rxy, r.z, -h_out, rdisc2, tani, &mut phase);
if result == Circle::Inside {
appear_phase = 0.5;
} else if result == Circle::Crossing {
appear_phase = appear_phase.min(phase);
}
if appear_phase > 0.0 {
result = circle_eclipse(rxy, r.z, -h_in, rdisc1, tani, &mut phase);
if result == Circle::Crossing {
appear_phase = appear_phase.min(phase);
} else if result != Circle::Inside {
appear_phase = -1.0;
}
}
if appear_phase > 0.0 {
result = circle_eclipse(rxy, r.z, h_out, rdisc2, tani, &mut phase);
if result == Circle::Crossing {
appear_phase = appear_phase.min(phase);
} else if result != Circle::Inside {
appear_phase = -1.0;
}
}
} else if rxy < rdisc1 {
if r.z < -h_in {
result = circle_eclipse(rxy, r.z, -h_in, rdisc1, tani, &mut phase);
if result == Circle::Inside {
appear_phase = 0.5;
} else if result == Circle::Crossing {
appear_phase = phase;
}
if appear_phase > 0.0 {
result = circle_eclipse(rxy, r.z, h_in, rdisc1, tani, &mut phase);
if result == Circle::Crossing {
appear_phase = appear_phase.min(phase);
} else if result != Circle::Inside {
appear_phase = -1.0;
}
}
if appear_phase > 0.0 {
result = circle_eclipse(rxy, r.z, h_out, rdisc2, tani, &mut phase);
if result == Circle::Crossing {
appear_phase = appear_phase.min(phase);
} else if result != Circle::Inside {
appear_phase = -1.0;
}
}
} else if r.z < h_in {
result = circle_eclipse(rxy, r.z, h_in, rdisc1, tani, &mut phase);
if result == Circle::Inside {
appear_phase = 0.0;
} else if result == Circle::Crossing {
appear_phase = phase;
}
if appear_phase > 0.0 {
result = circle_eclipse(rxy, r.z, h_out, rdisc2, tani, &mut phase);
if result == Circle::Crossing {
appear_phase = appear_phase.min(phase);
} else if result != Circle::Inside {
appear_phase = -1.0;
}
}
}
}
let phi0: f64 = r.y.atan2(-r.x / TAU);
if appear_phase <= 0.0 {
ingress = phi0 - eclipse_phase;
ingress -= ingress.floor();
egress = ingress + 2.0 * eclipse_phase;
temp.push((ingress, egress));
} else if appear_phase < eclipse_phase {
ingress = phi0 - eclipse_phase;
ingress -= ingress.floor();
egress = ingress + (eclipse_phase - appear_phase);
temp.push((ingress, egress));
ingress = phi0 + appear_phase;
ingress -= ingress.floor();
egress = ingress + (eclipse_phase - appear_phase);
temp.push((ingress, egress));
}
Ok(temp)
}
pub fn circle_eclipse(
rxy: f64,
z: f64,
zcirc: f64,
radius: f64,
tani: f64,
phase: &mut f64,
) -> Circle {
if z >= zcirc {
return Circle::Above;
}
let rcone: f64 = tani * (zcirc - z);
if rcone >= rxy + radius {
return Circle::Outside;
}
if rxy >= rcone + radius {
return Circle::Separate;
}
if rxy + rcone <= radius {
return Circle::Inside;
}
*phase = cut_phase(rxy, rcone, radius);
Circle::Crossing
}
pub fn cut_phase(rxy: f64, rcone: f64, radius: f64) -> f64 {
if rxy + rcone <= radius {
panic!("rxy + rcone <= radius");
}
if rxy >= radius + rcone {
panic!("rxy >= radius + rcone");
}
if rcone >= radius + rxy {
panic!("rcone >= radius + rxy");
}
((rxy * rxy + rcone * rcone - radius * radius) / (2.0 * rcone * rxy)).acos() / TAU
}