#[derive(Debug, Clone)]
pub struct StabilityPoint {
pub heel: f64,
pub draft: f64,
pub trim: f64,
pub value: f64,
pub is_flooding: bool,
pub flooded_openings: Vec<String>,
pub cog: Option<[f64; 3]>,
pub vessel_cog: Option<[f64; 3]>,
pub freeboard: Option<f64>,
}
#[derive(Debug, Clone)]
pub struct StabilityCurve {
pub curve_type: String,
pub displacement: f64,
pub cog: Option<[f64; 3]>,
pub points: Vec<StabilityPoint>,
}
impl StabilityCurve {
pub fn new_kn(displacement: f64, points: Vec<StabilityPoint>) -> Self {
Self {
curve_type: "KN".to_string(),
displacement,
cog: None,
points,
}
}
pub fn new_gz(displacement: f64, cog: [f64; 3], points: Vec<StabilityPoint>) -> Self {
Self {
curve_type: "GZ".to_string(),
displacement,
cog: Some(cog),
points,
}
}
pub fn heels(&self) -> Vec<f64> {
self.points.iter().map(|p| p.heel).collect()
}
pub fn values(&self) -> Vec<f64> {
self.points.iter().map(|p| p.value).collect()
}
pub fn max_value(&self) -> Option<&StabilityPoint> {
self.points
.iter()
.max_by(|a, b| a.value.partial_cmp(&b.value).unwrap())
}
pub fn interpolate(&self, heel: f64) -> Option<f64> {
if self.points.is_empty() {
return None;
}
for i in 0..self.points.len() - 1 {
if self.points[i].heel <= heel && heel <= self.points[i + 1].heel {
let t =
(heel - self.points[i].heel) / (self.points[i + 1].heel - self.points[i].heel);
return Some(
self.points[i].value + t * (self.points[i + 1].value - self.points[i].value),
);
}
}
None
}
}
#[derive(Debug, Clone)]
pub struct StabilityCurveWithWind {
pub curve_type: String,
pub displacement: f64,
pub cog: [f64; 3],
pub points: Vec<StabilityPoint>,
pub wind_pressure: f64,
pub wind_lever_lw1: f64,
pub wind_lever_lw2: f64,
pub wind_area: f64,
pub wind_center_z: f64,
}
impl StabilityCurveWithWind {
pub fn new(
displacement: f64,
cog: [f64; 3],
points: Vec<StabilityPoint>,
wind_pressure: f64,
wind_lever_lw1: f64,
wind_area: f64,
wind_center_z: f64,
) -> Self {
Self {
curve_type: "GZ".to_string(),
displacement,
cog,
points,
wind_pressure,
wind_lever_lw1,
wind_lever_lw2: wind_lever_lw1 * 1.5,
wind_area,
wind_center_z,
}
}
pub fn heels(&self) -> Vec<f64> {
self.points.iter().map(|p| p.heel).collect()
}
pub fn gz_values(&self) -> Vec<f64> {
self.points.iter().map(|p| p.value).collect()
}
pub fn gz_corrected(&self) -> Vec<f64> {
self.points
.iter()
.map(|p| p.value - self.wind_lever_lw1)
.collect()
}
pub fn min_heel(&self) -> Option<f64> {
self.points
.iter()
.map(|p| p.heel)
.min_by(|a, b| a.partial_cmp(b).unwrap())
}
pub fn max_heel(&self) -> Option<f64> {
self.points
.iter()
.map(|p| p.heel)
.max_by(|a, b| a.partial_cmp(b).unwrap())
}
pub fn max_gz(&self) -> Option<&StabilityPoint> {
self.points
.iter()
.max_by(|a, b| a.value.partial_cmp(&b.value).unwrap())
}
}