#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use geo::Contains;
use crate::fp::MassAndBalance;
use crate::measurements::{Length, Mass};
#[derive(Copy, Clone, Eq, PartialEq, Debug, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct CGLimit {
mass: Mass,
distance: Length,
}
impl CGLimit {
pub fn new(mass: Mass, distance: Length) -> Self {
Self { mass, distance }
}
pub fn mass(&self) -> &Mass {
&self.mass
}
pub fn distance(&self) -> &Length {
&self.distance
}
}
#[derive(Clone, Eq, PartialEq, Debug, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct CGEnvelope {
limits: Vec<CGLimit>,
}
impl CGEnvelope {
pub fn new(limits: Vec<CGLimit>) -> Self {
Self { limits }
}
pub fn contains(&self, mb: &MassAndBalance) -> bool {
let coords: Vec<geo::Coord<f64>> = self
.limits
.iter()
.map(|limit| geo::Coord {
x: limit.distance.to_si() as f64,
y: limit.mass.to_si() as f64,
})
.collect();
let envelope = geo::Polygon::new(geo::LineString::from(coords), vec![]);
let point_within = |mass: &Mass, balance: &Length| -> bool {
let point = geo::Point::new(balance.to_si() as f64, mass.to_si() as f64);
envelope.contains(&point)
};
let on_ramp = point_within(mb.mass_on_ramp(), mb.balance_on_ramp());
let after_landing = point_within(mb.mass_after_landing(), mb.balance_after_landing());
on_ramp && after_landing
}
pub fn limits(&self) -> &[CGLimit] {
&self.limits
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::aircraft::{LoadedStation, Station};
#[test]
fn contains_point() {
let envelope = CGEnvelope::new(vec![
CGLimit {
mass: Mass::kg(0.0),
distance: Length::m(0.0),
},
CGLimit {
mass: Mass::kg(0.5),
distance: Length::m(0.0),
},
CGLimit {
mass: Mass::kg(1.0),
distance: Length::m(0.25),
},
CGLimit {
mass: Mass::kg(1.0),
distance: Length::m(1.0),
},
CGLimit {
mass: Mass::kg(0.0),
distance: Length::m(1.0),
},
]);
let balanced = MassAndBalance::new(&vec![LoadedStation {
station: Station::new(Length::m(0.5), None),
on_ramp: Mass::kg(0.5),
after_landing: Mass::kg(0.5),
}]);
let unbalanced = MassAndBalance::new(&vec![LoadedStation {
station: Station::new(Length::m(0.0), None),
on_ramp: Mass::kg(1.0),
after_landing: Mass::kg(1.0),
}]);
assert!(
envelope.contains(&balanced),
"envelope didn't contain the balanced M&B"
);
assert!(
!envelope.contains(&unbalanced),
"envelope contain the unbalanced M&B"
);
}
}