use crate::_bevy::*;
use crate::components::aero_coeff::AeroCoeff;
use avian3d::math::Scalar;
use avian3d::prelude::Collider;
use serde::{Deserialize, Serialize};
#[derive(Component, Reflect, Serialize, Deserialize, Clone, Debug)]
#[reflect(Component, Serialize, Deserialize)]
#[require(crate::components::zone_force::ZoneForce)]
pub struct AeroZone {
pub airfoil_name: String,
pub cl: AeroCoeff,
pub cd: AeroCoeff,
pub cy: AeroCoeff,
pub cm: AeroCoeff,
pub croll: AeroCoeff,
pub cn: AeroCoeff,
pub ac_offset: Vec3,
pub control_role: Option<ControlSurfaceRole>,
pub damage_drag_coeff: Option<Scalar>,
pub area_m2: Scalar,
pub chord_m: Scalar,
}
impl AeroZone {
pub fn with_post_stall_extension(mut self) -> Self {
if self.chord_m <= 0.0 || self.area_m2 <= 0.0 {
return self;
}
let ar = self.area_m2 / (self.chord_m * self.chord_m);
self.cl = self.cl.with_post_stall_lift(ar);
self.cd = self.cd.with_post_stall_drag(ar);
self.cy = self.cy.with_post_stall_lift(ar);
self
}
pub fn validate(&self, zone_name: &str) -> Vec<String> {
let mut problems = Vec::new();
let fields: &[(&str, &AeroCoeff)] = &[
("cl", &self.cl),
("cd", &self.cd),
("cy", &self.cy),
("cm", &self.cm),
("croll", &self.croll),
("cn", &self.cn),
];
for (field, coeff) in fields {
let label = format!("{zone_name}.{field}");
problems.extend(coeff.validate(&label));
}
if self.area_m2 < 0.0 {
problems.push(format!(
"{zone_name}: area_m2 is negative ({:.4})",
self.area_m2
));
}
if self.chord_m < 0.0 {
problems.push(format!(
"{zone_name}: chord_m is negative ({:.4})",
self.chord_m
));
}
problems
}
}
#[derive(Reflect, Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
#[reflect(Serialize, Deserialize)]
pub enum ControlSurfaceRole {
Elevator,
AileronLeft,
AileronRight,
Rudder,
}
#[derive(Bundle, Default)]
pub struct AeroZoneBundle {
pub zone: AeroZone,
pub collider: Collider,
pub transform: Transform,
pub global_transform: GlobalTransform,
}
impl Default for AeroZone {
fn default() -> Self {
Self {
cl: AeroCoeff::Placeholder,
cd: AeroCoeff::Placeholder,
cy: AeroCoeff::Absent,
cm: AeroCoeff::Absent,
croll: AeroCoeff::Absent,
cn: AeroCoeff::Absent,
ac_offset: Vec3::ZERO,
airfoil_name: String::new(),
control_role: None,
damage_drag_coeff: None,
area_m2: 0.0,
chord_m: 0.0,
}
}
}