use std::f64::consts::TAU;
use crate::{Etype, Vec3};
use crate::errors::RocheError;
#[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));
}
return 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
}