ballin 0.1.0

A colorful interactive physics simulator with thousands of balls, but in your terminal.
Documentation
//! Density-based background coloring.
//!
//! This module provides functions to calculate background colors based on
//! the number of balls in a cell. Higher density areas receive brighter
//! background colors to visually indicate ball concentration.

use ratatui::style::Color;

/// Calculates background color based on ball density in a cell.
///
/// Uses a gradient from transparent (no balls) to bright (many balls).
/// The color scheme uses blue-gray tones to complement the white Braille dots.
///
/// # Arguments
///
/// * `ball_count` - Number of balls in the cell
///
/// # Returns
///
/// - `None` for 0-1 balls (sparse, no background)
/// - `Some(Color)` for 2+ balls with intensity proportional to count
///
/// # Color Gradient
///
/// - 2-3 balls: Very dark blue-gray
/// - 4-6 balls: Dark blue-gray
/// - 7-10 balls: Medium blue-gray
/// - 11-15 balls: Light blue-gray
/// - 16-25 balls: Lighter
/// - 26+ balls: Very light (high density)
pub fn density_to_color(ball_count: u16) -> Option<Color> {
    match ball_count {
        0..=1 => None,
        2..=3 => Some(Color::Rgb(30, 30, 45)),
        4..=6 => Some(Color::Rgb(45, 45, 65)),
        7..=10 => Some(Color::Rgb(60, 60, 90)),
        11..=15 => Some(Color::Rgb(80, 80, 115)),
        16..=25 => Some(Color::Rgb(100, 100, 140)),
        26..=40 => Some(Color::Rgb(125, 125, 165)),
        41..=60 => Some(Color::Rgb(150, 150, 190)),
        _ => Some(Color::Rgb(175, 175, 210)),
    }
}

/// Calculates foreground color for Braille characters based on density.
///
/// Higher density areas get brighter foreground colors to improve visibility
/// against the darker backgrounds.
///
/// # Arguments
///
/// * `ball_count` - Number of balls in the cell
///
/// # Returns
///
/// A `Color` for the Braille character foreground.
pub fn density_to_foreground(ball_count: u16) -> Color {
    match ball_count {
        0..=1 => Color::White,
        2..=5 => Color::Rgb(220, 220, 255),
        6..=15 => Color::Rgb(240, 240, 255),
        _ => Color::Rgb(255, 255, 255),
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_no_background_for_sparse() {
        assert!(density_to_color(0).is_none());
        assert!(density_to_color(1).is_none());
    }

    #[test]
    fn test_background_for_dense() {
        assert!(density_to_color(2).is_some());
        assert!(density_to_color(10).is_some());
        assert!(density_to_color(100).is_some());
    }

    #[test]
    fn test_color_gradient_increasing() {
        // Higher counts should produce brighter colors
        let color_low = density_to_color(3).unwrap();
        let color_high = density_to_color(50).unwrap();

        if let (Color::Rgb(r1, g1, b1), Color::Rgb(r2, g2, b2)) = (color_low, color_high) {
            // Higher density should have higher RGB values
            assert!(r2 > r1);
            assert!(g2 > g1);
            assert!(b2 > b1);
        } else {
            panic!("Expected RGB colors");
        }
    }
}