use crate::prelude::*;
use bevy::prelude::*;
#[derive(Reflect, Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
#[reflect(Debug, PartialEq)]
pub enum CoefficientCombine {
Average = 1,
GeometricMean = 2,
Min = 3,
Multiply = 4,
Max = 5,
}
impl CoefficientCombine {
pub fn mix(&self, a: Scalar, b: Scalar) -> Scalar {
match self {
CoefficientCombine::Average => (a + b) * 0.5,
CoefficientCombine::GeometricMean => (a * b).sqrt(),
CoefficientCombine::Min => a.min(b),
CoefficientCombine::Multiply => a * b,
CoefficientCombine::Max => a.max(b),
}
}
}
#[derive(Resource, Clone, Copy, Debug, Default, Deref, DerefMut, PartialEq, Reflect)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
#[reflect(Debug, Default, PartialEq)]
pub struct DefaultFriction(pub Friction);
#[derive(Resource, Clone, Copy, Debug, Default, Deref, DerefMut, PartialEq, Reflect)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
#[reflect(Debug, Default, PartialEq)]
pub struct DefaultRestitution(pub Restitution);
#[cfg_attr(feature = "2d", doc = "# use avian2d::prelude::*;")]
#[cfg_attr(feature = "3d", doc = "# use avian3d::prelude::*;")]
#[derive(Reflect, Clone, Copy, Component, Debug, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
#[reflect(Debug, Component, PartialEq)]
pub struct Friction {
pub dynamic_coefficient: Scalar,
pub static_coefficient: Scalar,
pub combine_rule: CoefficientCombine,
}
impl Default for Friction {
fn default() -> Self {
Self {
dynamic_coefficient: 0.5,
static_coefficient: 0.5,
combine_rule: CoefficientCombine::Average,
}
}
}
impl Friction {
pub const ZERO: Self = Self {
dynamic_coefficient: 0.0,
static_coefficient: 0.0,
combine_rule: CoefficientCombine::Average,
};
pub fn new(friction_coefficient: Scalar) -> Self {
Self {
dynamic_coefficient: friction_coefficient,
static_coefficient: friction_coefficient,
..default()
}
}
pub fn with_combine_rule(&self, combine_rule: CoefficientCombine) -> Self {
Self {
combine_rule,
..*self
}
}
pub fn with_dynamic_coefficient(&self, coefficient: Scalar) -> Self {
Self {
dynamic_coefficient: coefficient,
..*self
}
}
pub fn with_static_coefficient(&self, coefficient: Scalar) -> Self {
Self {
static_coefficient: coefficient,
..*self
}
}
pub fn combine(&self, other: Self) -> Self {
let rule = self.combine_rule.max(other.combine_rule);
Self {
dynamic_coefficient: rule.mix(self.dynamic_coefficient, other.dynamic_coefficient),
static_coefficient: rule.mix(self.static_coefficient, other.static_coefficient),
combine_rule: rule,
}
}
}
impl From<Scalar> for Friction {
fn from(coefficient: Scalar) -> Self {
Self {
dynamic_coefficient: coefficient,
static_coefficient: coefficient,
..default()
}
}
}
#[cfg_attr(feature = "2d", doc = "# use avian2d::prelude::*;")]
#[cfg_attr(feature = "3d", doc = "# use avian3d::prelude::*;")]
#[doc(alias = "Bounciness")]
#[doc(alias = "Elasticity")]
#[derive(Reflect, Clone, Copy, Component, Debug, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
#[reflect(Debug, Component, PartialEq)]
pub struct Restitution {
pub coefficient: Scalar,
pub combine_rule: CoefficientCombine,
}
impl Default for Restitution {
fn default() -> Self {
Self {
coefficient: 0.0,
combine_rule: CoefficientCombine::Average,
}
}
}
impl Restitution {
pub const ZERO: Self = Self {
coefficient: 0.0,
combine_rule: CoefficientCombine::Average,
};
pub const PERFECTLY_INELASTIC: Self = Self {
coefficient: 0.0,
combine_rule: CoefficientCombine::Average,
};
pub const PERFECTLY_ELASTIC: Self = Self {
coefficient: 1.0,
combine_rule: CoefficientCombine::Average,
};
pub fn new(coefficient: Scalar) -> Self {
Self {
coefficient,
combine_rule: CoefficientCombine::Average,
}
}
pub fn with_combine_rule(&self, combine_rule: CoefficientCombine) -> Self {
Self {
combine_rule,
..*self
}
}
pub fn combine(&self, other: Self) -> Self {
let rule = self.combine_rule.max(other.combine_rule);
Self {
coefficient: rule.mix(self.coefficient, other.coefficient),
combine_rule: rule,
}
}
}
impl From<Scalar> for Restitution {
fn from(coefficient: Scalar) -> Self {
Self {
coefficient,
..default()
}
}
}
#[cfg(test)]
mod tests {
use crate::prelude::*;
use approx::assert_relative_eq;
#[test]
fn coefficient_combine_works() {
let r1 = Restitution::new(0.3).with_combine_rule(CoefficientCombine::Average);
let average_result =
r1.combine(Restitution::new(0.7).with_combine_rule(CoefficientCombine::Average));
let average_expected = Restitution::new(0.5).with_combine_rule(CoefficientCombine::Average);
assert_relative_eq!(
average_result.coefficient,
average_expected.coefficient,
epsilon = 0.0001
);
assert_eq!(average_result.combine_rule, average_expected.combine_rule);
let geometric_mean_result =
r1.combine(Restitution::new(0.7).with_combine_rule(CoefficientCombine::GeometricMean));
let geometric_mean_expected =
Restitution::new(0.458_257_56).with_combine_rule(CoefficientCombine::GeometricMean);
assert_relative_eq!(
geometric_mean_result.coefficient,
geometric_mean_expected.coefficient,
epsilon = 0.0001
);
assert_eq!(
geometric_mean_result.combine_rule,
geometric_mean_expected.combine_rule
);
assert_eq!(
r1.combine(Restitution::new(0.7).with_combine_rule(CoefficientCombine::Min)),
Restitution::new(0.3).with_combine_rule(CoefficientCombine::Min)
);
let multiply_result =
r1.combine(Restitution::new(0.7).with_combine_rule(CoefficientCombine::Multiply));
let multiply_expected =
Restitution::new(0.21).with_combine_rule(CoefficientCombine::Multiply);
assert_relative_eq!(
multiply_result.coefficient,
multiply_expected.coefficient,
epsilon = 0.0001
);
assert_eq!(multiply_result.combine_rule, multiply_expected.combine_rule);
assert_eq!(
r1.combine(Restitution::new(0.7).with_combine_rule(CoefficientCombine::Max)),
Restitution::new(0.7).with_combine_rule(CoefficientCombine::Max)
);
}
}