ballin 0.1.2

A colorful interactive physics simulator with thousands of balls, but in your terminal.
Documentation
//! Physics simulation configuration.
//!
//! Contains all tunable physics parameters that affect simulation behavior.
//! These values can be adjusted at runtime through the options menu.

use rapier2d::prelude::*;

/// Physics simulation parameters.
///
/// Encapsulates all physics constants and coefficients used by the simulation.
/// Uses sensible defaults but allows runtime adjustment via the options menu.
///
/// # Example
///
/// ```rust
/// use ballin::physics::PhysicsConfig;
///
/// let config = PhysicsConfig::default();
/// assert_eq!(config.gravity.y, -9.81);
/// ```
#[derive(Debug, Clone)]
pub struct PhysicsConfig {
    /// Gravity vector in physics units (m/s^2).
    ///
    /// Typically (0.0, -9.81) for Earth-like gravity.
    /// Negative Y pulls balls downward in physics space (Y-up convention).
    pub gravity: Vector<Real>,

    /// Ball radius in physics units.
    ///
    /// Kept small (0.15) to allow thousands of balls in the simulation
    /// without excessive overlap or computational cost.
    pub ball_radius: Real,

    /// Coefficient of restitution (bounciness).
    ///
    /// Range: 0.0 (perfectly inelastic) to 1.0 (perfectly elastic).
    /// A value of 0.7 provides satisfying bouncy behavior.
    pub restitution: Real,

    /// Friction coefficient for collisions.
    ///
    /// Affects ball-ball and ball-wall interactions.
    /// Higher values cause balls to slow down more on contact.
    pub friction: Real,

    /// Linear damping (simulates air resistance).
    ///
    /// Applied continuously to reduce ball velocity over time.
    /// Prevents perpetual motion and helps simulation stability.
    pub linear_damping: Real,

    /// Mass density for ball colliders.
    ///
    /// Affects momentum transfer during collisions.
    /// Higher density = more massive balls = harder to push.
    pub density: Real,

    /// Maximum velocity magnitude for balls.
    ///
    /// Prevents balls from achieving infinite or unreasonably high speeds.
    /// Velocity is clamped to this value after each physics step.
    pub max_velocity: Real,
}

impl Default for PhysicsConfig {
    /// Creates a default physics configuration with Earth-like gravity.
    ///
    /// These defaults provide a good starting point for the simulation:
    /// - Gravity: 9.81 m/s^2 downward
    /// - Ball radius: 0.15 units (small for density)
    /// - Restitution: 0.7 (fairly bouncy)
    /// - Friction: 0.3 (moderate)
    /// - Linear damping: 0.1 (light air resistance)
    /// - Density: 1.0 (standard)
    fn default() -> Self {
        Self {
            gravity: vector![0.0, -9.81],
            ball_radius: 0.15,
            restitution: 0.7,
            friction: 0.3,
            linear_damping: 0.1,
            density: 1.0,
            // Max velocity caps ball speed to prevent runaway physics
            // Reduced from 50 to 30 for better stability with high ball counts
            max_velocity: 30.0,
        }
    }
}

impl PhysicsConfig {
    /// Creates a new physics configuration with custom gravity.
    ///
    /// # Arguments
    ///
    /// * `gravity_strength` - Magnitude of gravity (positive value, will be negated for Y)
    ///
    /// # Returns
    ///
    /// A new `PhysicsConfig` with the specified gravity and default other values.
    pub fn with_gravity(gravity_strength: Real) -> Self {
        Self {
            gravity: vector![0.0, -gravity_strength],
            ..Default::default()
        }
    }

    /// Updates the gravity vector based on a magnitude value.
    ///
    /// # Arguments
    ///
    /// * `gravity_strength` - New gravity magnitude (positive, will be negated)
    pub fn set_gravity(&mut self, gravity_strength: Real) {
        self.gravity = vector![0.0, -gravity_strength];
    }
}