pub use ring360::*;
#[derive(Debug, Clone, Copy)]
pub struct AspectResult(pub f64, pub f64, pub f64, pub bool, pub f64);
impl AspectResult {
pub fn calculate(target: f64, angle: f64, orb: f64) -> Self {
let mut distance = target.angle_360(angle);
if target < Ring360::half_turn() && target > 0f64 {
let inverse_target = Ring360::BASE - target;
let distance_2 = inverse_target.angle_360(angle);
if distance_2.abs() < distance.abs() {
distance = distance_2;
}
}
let neg_orb = 0f64 - orb;
let distance_abs = distance.abs();
let in_range = distance_abs >= neg_orb && distance_abs <= orb;
AspectResult(angle, target, distance, in_range, orb)
}
pub fn aspect(&self) -> f64 {
self.0
}
pub fn target(&self) -> f64 {
self.1
}
pub fn distance(&self) -> f64 {
self.2
}
pub fn divergence(&self) -> f64 {
self.2.abs()
}
pub fn matched(&self) -> bool {
self.3
}
pub fn orb(&self) -> f64 {
self.4
}
}
#[derive(Debug, Clone, Copy)]
pub struct AspectOrb(pub f64, pub f64);
impl AspectOrb {
pub fn target(&self) -> f64 {
self.0
}
pub fn orb(&self) -> f64 {
self.1
}
}
pub trait Aspect360 {
fn calc_aspect(&self, other: &Ring360, target: f64, orb: f64) -> AspectResult;
fn find_aspect(&self, other: &Ring360, targets: &[AspectOrb]) -> Option<AspectResult> {
for aspect_orb in targets {
let aspect = self.calc_aspect(other, aspect_orb.target(), aspect_orb.orb());
if aspect.matched() {
return Some(aspect);
}
}
None
}
fn find_aspects(&self, other: &Ring360, targets: &[AspectOrb]) -> Vec<AspectResult> {
let mut matched_aspects: Vec<AspectResult> = Vec::new();
for aspect_orb in targets {
let aspect = self.calc_aspect(other, aspect_orb.target(), aspect_orb.orb());
if aspect.matched() {
matched_aspects.push(aspect);
}
}
matched_aspects
}
fn find_best_aspect(&self, other: &Ring360, targets: &[AspectOrb]) -> Option<AspectResult> {
let mut matched_aspects = self.find_aspects(other, targets);
if matched_aspects.is_empty() {
None
} else {
matched_aspects.sort_by(|a, b| a.divergence().partial_cmp(&b.divergence()).unwrap());
matched_aspects.first().map(|ar| *ar)
}
}
fn calc_aspect_f64(&self, other: f64, target: f64, orb: f64) -> AspectResult {
self.calc_aspect(&other.to_360(), target, orb)
}
fn is_aspected(&self, other: &Ring360, target: f64, orb: f64) -> bool {
self.calc_aspect(other, target, orb).matched()
}
fn is_aspected_f64(&self, other: f64, target: f64, orb: f64) -> bool {
self.calc_aspect(&other.to_360(), target, orb).matched()
}
}
impl Aspect360 for Ring360 {
fn calc_aspect(&self, other: &Ring360, target: f64, orb: f64) -> AspectResult {
let angle = self.angle(*other);
AspectResult::calculate(target, angle, orb)
}
}
pub trait ToAspectOrbs {
fn to_aspect_orbs(&self) -> Vec<AspectOrb>;
}
impl ToAspectOrbs for [(f64, f64)] {
fn to_aspect_orbs(&self) -> Vec<AspectOrb> {
self.into_iter().map(|(aspect, orb)| AspectOrb(*aspect, *orb)).collect()
}
}