Skip to main content

linear_sim/
component.rs

1//! Components for use by entities.
2
3use std;
4use enumflags2;
5use derive_more::From;
6#[cfg(feature = "derive_serdes")]
7use serde::{Deserialize, Serialize};
8
9use crate::{force, geometry, math};
10
11#[expect(clippy::excessive_precision)]
12#[expect(clippy::unreadable_literal)]
13pub const MATERIAL_WOOD    : Material  = Material {
14  restitution: math::Normalized::noisy_f64 (0.6),
15  friction:    math::NonNegative::noisy_f64 (0.63245553203367586639)
16  //restitution: 0.6, friction: 0.4
17};
18#[expect(clippy::excessive_precision)]
19#[expect(clippy::unreadable_literal)]
20pub const MATERIAL_STONE   : Material = Material {
21  restitution: math::Normalized::noisy_f64 (0.8),
22  friction:    math::NonNegative::noisy_f64 (0.77459666924148337703)
23  //restitution: 0.8, friction: 0.6
24};
25#[expect(clippy::excessive_precision)]
26#[expect(clippy::unreadable_literal)]
27pub const MATERIAL_COPPER  : Material = Material {
28  restitution: math::Normalized::noisy_f64 (0.6),
29  friction:    math::NonNegative::noisy_f64 (0.63245553203367586639)
30  //restitution: 0.15, friction: 0.4
31};
32#[expect(clippy::excessive_precision)]
33#[expect(clippy::unreadable_literal)]
34pub const MATERIAL_BRONZE  : Material = Material {
35  restitution: math::Normalized::noisy_f64 (0.6),
36  friction:    math::NonNegative::noisy_f64 (0.54772255750516611345)
37  //restitution: 0.6, friction: 0.3
38};
39pub const MATERIAL_LEATHER : Material = Material {
40  restitution: math::Normalized::noisy_f64 (0.1),
41  friction:    math::NonNegative::noisy_f64 (std::f64::consts::FRAC_1_SQRT_2)
42  //restitution: 0.1, friction: 0.5
43};
44
45/// A point in 3D space
46#[cfg_attr(feature = "derive_serdes", derive(Deserialize, Serialize))]
47#[derive(Clone, Copy, Debug, PartialEq, From)]
48pub struct Position (pub math::Point3 <f64>);
49
50/// First (velocity) and second (acceleration) time derivatives of position
51#[cfg_attr(feature = "derive_serdes", derive(Deserialize, Serialize))]
52#[derive(Clone, Copy, Debug, PartialEq)]
53pub struct Derivatives {
54  /// First time derivative of position
55  pub velocity     : math::Vector3 <f64>,
56  /// Second time derivative of position
57  pub acceleration : math::Vector3 <f64>,
58  /// First time derivative of momentum.
59  ///
60  /// This vector is intended to be cleared and then accumulated each step.
61  pub force        : math::Vector3 <f64>,
62  pub force_flags  : enumflags2::BitFlags <force::Flag>
63}
64
65/// A drag coefficient for dynamic objects
66#[cfg_attr(feature = "derive_serdes", derive(Deserialize, Serialize))]
67#[derive(Clone, Copy, Debug, PartialEq)]
68pub struct Drag (pub f64);
69
70/// A bounding volume
71#[cfg_attr(feature = "derive_serdes", derive(Deserialize, Serialize))]
72#[derive(Clone, Debug, PartialEq, From)]
73pub struct Bound (pub geometry::shape::Variant <f64>);
74
75/// A point mass; does *not* currently enforce that mass is nonzero or positive
76#[cfg_attr(feature = "derive_serdes", derive(Deserialize, Serialize))]
77#[derive(Clone, Copy, Debug, PartialEq)]
78pub struct Mass {
79  mass            : f64,
80  mass_reciprocal : f64
81}
82
83/// Surface material properties
84#[cfg_attr(feature = "derive_serdes", derive(Deserialize, Serialize))]
85#[derive(Clone, Copy, Debug, PartialEq)]
86pub struct Material {
87  /// A generic restitution is in [0.0,1.0] with 1.0 being perfectly elastic and
88  /// 0.0 perfectly inelastic.
89  ///
90  /// When a pair of material objects collide, their respective restitution
91  /// coefficients are multiplied to give the restitution coefficient for that
92  /// collision.
93  pub restitution : math::Normalized <f64>,
94  /// A generic friction coefficient is in [0.0,\infty] with 0.0 being perfectly
95  /// frictionless and 1.0 being about equivalent to concrete on rubber.
96  ///
97  /// When a pair of material objects have contact, the average of their
98  /// respective friction coefficients is computed to give the friction
99  /// coefficient for that contact.
100  pub friction    : math::NonNegative <f64>
101}
102
103impl Position {
104  #[inline]
105  pub fn origin() -> Self {
106    Position (math::Point::origin())
107  }
108}
109
110// NOTE: calling the macro with non snake-case param causes a non_snake_case warning;
111// the `expect` attribute cannot be applied at the call site so we created this private
112// module to apply it to the module
113#[expect(non_snake_case)]
114mod bound {
115  use super::*;
116  macro impl_from_try_into {
117    ($shape:ident, $variant:ident) => {
118      impl From <geometry::shape::$shape <f64>> for Bound {
119        fn from ($shape : geometry::shape::$shape <f64>) -> Self {
120          Bound (geometry::shape::Variant::$variant ($shape.into()))
121        }
122      }
123      impl TryInto <geometry::shape::$shape <f64>> for Bound {
124        type Error = Self;
125        fn try_into (self) -> Result <geometry::shape::$shape <f64>, Self> {
126          geometry::shape::$variant::try_from (self.0).map_err (|err| Bound (err.input))
127            .and_then (|variant| geometry::shape::$shape::try_from (variant)
128              .map_err (|err| Bound (geometry::shape::Variant::$variant (err.input))))
129        }
130      }
131    }
132  }
133  impl_from_try_into!{Sphere, Bounded}
134  impl_from_try_into!{Capsule, Bounded}
135  impl_from_try_into!{Cylinder, Bounded}
136  impl_from_try_into!{Cone, Bounded}
137  impl_from_try_into!{Cube, Bounded}
138  impl_from_try_into!{Cuboid, Bounded}
139  impl_from_try_into!{Hull, Bounded}
140  impl_from_try_into!{Halfspace, Unbounded}
141  //impl_from_try_into!{Orthant, Unbounded}
142  // Orthant is not generic
143  impl From <geometry::shape::Orthant> for Bound {
144    fn from (Orthant : geometry::shape::Orthant) -> Self {
145      Bound (geometry::shape::Variant::Unbounded (Orthant.into()))
146    }
147  }
148  impl TryInto <geometry::shape::Orthant> for Bound {
149    type Error = Self;
150    fn try_into (self) -> Result <geometry::shape::Orthant, Self> {
151      geometry::shape::Unbounded::try_from (self.0).map_err (|err| Bound (err.input))
152        .and_then (|variant| geometry::shape::Orthant::try_from (variant)
153          .map_err (|err| Bound (geometry::shape::Variant::Unbounded (err.input))))
154    }
155  }
156}
157
158impl Mass {
159  #[inline]
160  pub fn new (mass : f64) -> Self {
161    let mass_reciprocal = 1.0 / mass;
162    Self { mass, mass_reciprocal }
163  }
164  #[inline]
165  pub const fn mass (&self) -> f64 {
166    self.mass
167  }
168  #[inline]
169  pub const fn mass_reciprocal (&self) -> f64 {
170    self.mass_reciprocal
171  }
172  #[inline]
173  pub fn set_mass (&mut self, mass : f64) {
174    self.mass = mass;
175    self.mass_reciprocal = 1.0 / self.mass;
176  }
177}
178impl std::ops::Add <Self> for Mass {
179  type Output = Self;
180  fn add (self, rhs : Self) -> Self {
181    let mass = self.mass + rhs.mass;
182    let mass_reciprocal = 1.0 / mass;
183    Mass { mass, mass_reciprocal }
184  }
185}
186
187impl Derivatives {
188  #[inline]
189  pub fn zero() -> Self {
190    let velocity     = math::Vector3::zero();
191    let acceleration = math::Vector3::zero();
192    let force        = math::Vector3::zero();
193    let force_flags  = enumflags2::BitFlags::all();
194    Derivatives { velocity, acceleration, force, force_flags }
195  }
196}
197
198impl Drag {
199  #[inline]
200  pub const fn zero() -> Self {
201    Drag (0.0)
202  }
203}