Skip to main content

goud_engine/ecs/components/rigidbody/
physics_ops.rs

1//! Physics operations, sleep management, and trait impls for [`RigidBody`].
2
3use crate::core::math::Vec2;
4use crate::ecs::Component;
5
6use super::body::RigidBody;
7
8// =============================================================================
9// Physics Operations
10// =============================================================================
11
12impl RigidBody {
13    /// Applies a force to the body (affects acceleration).
14    ///
15    /// Force is accumulated and applied during physics integration.
16    /// Only affects dynamic bodies.
17    ///
18    /// # Arguments
19    ///
20    /// * `_force` - Force vector in Newtons (kg·m/s²)
21    ///
22    /// # Note
23    ///
24    /// This is a placeholder for the public API. Forces will be accumulated
25    /// in a separate `Forces` component or applied directly during physics
26    /// integration in future implementation.
27    pub fn apply_force(&mut self, _force: Vec2) {
28        if !self.is_dynamic() {
29            return;
30        }
31        // Forces will be accumulated in a separate Forces component
32        // or applied directly during integration
33        // This is a placeholder for the public API
34        self.wake();
35    }
36
37    /// Applies an impulse to the body (instant velocity change).
38    ///
39    /// Impulse directly modifies velocity: Δv = impulse / mass
40    /// Only affects dynamic bodies.
41    ///
42    /// # Arguments
43    ///
44    /// * `impulse` - Impulse vector in kg·m/s
45    pub fn apply_impulse(&mut self, impulse: Vec2) {
46        if !self.is_dynamic() {
47            return;
48        }
49        self.linear_velocity = self.linear_velocity + impulse * self.inverse_mass;
50        self.wake();
51    }
52
53    /// Applies an angular impulse to the body.
54    ///
55    /// # Arguments
56    ///
57    /// * `impulse` - Angular impulse in kg·m²/s
58    pub fn apply_angular_impulse(&mut self, impulse: f32) {
59        if !self.is_dynamic() || self.has_fixed_rotation() {
60            return;
61        }
62        self.angular_velocity += impulse * self.inverse_inertia;
63        self.wake();
64    }
65
66    /// Applies damping to velocity (called by physics system).
67    ///
68    /// # Arguments
69    ///
70    /// * `dt` - Delta time in seconds
71    pub fn apply_damping(&mut self, dt: f32) {
72        // Exponential damping: v *= exp(-damping * dt) ≈ v *= (1 - damping * dt)
73        let linear_factor = 1.0 - self.linear_damping * dt;
74        let angular_factor = 1.0 - self.angular_damping * dt;
75
76        self.linear_velocity = self.linear_velocity * linear_factor.max(0.0);
77        self.angular_velocity *= angular_factor.max(0.0);
78    }
79
80    // =========================================================================
81    // Sleep Management
82    // =========================================================================
83
84    /// Puts the body to sleep (optimization).
85    ///
86    /// Sleeping bodies are excluded from physics updates until woken.
87    pub fn sleep(&mut self) {
88        self.set_sleeping(true);
89        self.linear_velocity = Vec2::zero();
90        self.angular_velocity = 0.0;
91        self.reset_sleep_time();
92    }
93
94    /// Wakes the body from sleep.
95    pub fn wake(&mut self) {
96        self.set_sleeping(false);
97        self.reset_sleep_time();
98    }
99
100    /// Updates sleep time based on current motion (called by physics system).
101    ///
102    /// # Arguments
103    ///
104    /// * `dt` - Delta time in seconds
105    /// * `linear_threshold` - Linear velocity threshold for sleep (pixels/s)
106    /// * `angular_threshold` - Angular velocity threshold for sleep (radians/s)
107    ///
108    /// # Returns
109    ///
110    /// True if the body should sleep.
111    pub fn update_sleep_time(
112        &mut self,
113        dt: f32,
114        linear_threshold: f32,
115        angular_threshold: f32,
116    ) -> bool {
117        if !self.can_sleep() || !self.is_dynamic() {
118            self.reset_sleep_time();
119            return false;
120        }
121
122        // Check if motion is below thresholds
123        let below_threshold = self.linear_speed_squared() < linear_threshold * linear_threshold
124            && self.angular_velocity.abs() < angular_threshold;
125
126        if below_threshold {
127            self.increment_sleep_time(dt);
128            true
129        } else {
130            self.reset_sleep_time();
131            false
132        }
133    }
134}
135
136// =============================================================================
137// Trait Implementations
138// =============================================================================
139
140/// ECS component trait implementation.
141impl Component for RigidBody {}
142
143impl std::fmt::Display for RigidBody {
144    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
145        write!(
146            f,
147            "RigidBody({}, vel: {:?}, mass: {}, sleeping: {})",
148            self.body_type,
149            self.linear_velocity,
150            self.mass,
151            self.is_sleeping()
152        )
153    }
154}