textcanvas 3.4.0

Draw to the terminal like an HTML Canvas.
Documentation
//! Cool looking animated sine waves.
//!
//! ```text
//! ⠀⠀i:⠀107⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀Fixed⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
//! ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
//! ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
//! ⠉⠉⠢⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡠⠊⠉⠉⠢⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠔⠉⠉⠑⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠔⠉⠉⠑⢄⠀
//! ⠀⠀⠀⠘⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡰⠁⠀⠀⠀⠀⠀⢣⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠎⠀⠀⠀⠀⠀⢣⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⠊⠀⠀⠀⠀⠀⢣
//! ⠀⠀⠀⠀⢘⡔⠉⠉⠉⠉⠢⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡰⠁⠀⠀⠀⠀⠀⠀⠀⣣⠊⠉⠉⠉⠢⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⠃⠀⠀⠀⠀⠀⠀⠀⣣⠊⠉⠉⠉⠢⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⠃⠀⠀⠀⠀⠀⠀⠀
//! ⠀⠀⠀⡔⠁⠘⡄⠀⠀⠀⠀⠈⣢⡔⠒⠒⠒⠒⢄⡀⡰⠁⠀⠀⠀⠀⠀⠀⡠⠊⠀⢣⠀⠀⠀⠀⠈⣢⡔⠒⠒⠒⠒⢄⡀⢀⠎⠀⠀⠀⠀⠀⠀⡠⠊⠀⢣⠀⠀⠀⠀⠈⡢⡔⠒⠒⠒⠒⢄⡀⢀⠎⠀⠀⠀⠀⠀⢀⠔⠉
//! ⠀⢠⠊⠀⠀⠀⠱⡀⠀⠀⡠⠊⠀⠘⢄⠀⠀⠀⠀⢸⠣⡀⠀⠀⠀⠀⠀⡔⠁⠀⠀⠈⢆⠀⠀⡠⠊⠀⠘⢄⠀⠀⠀⠀⠈⡮⡀⠀⠀⠀⠀⠀⡔⠁⠀⠀⠈⢆⠀⢀⡠⠊⠀⠑⢄⠀⠀⠀⠀⠈⡮⡀⠀⠀⠀⠀⢠⠊⠀⠀
//! ⡰⠁⠀⠀⠀⠀⠀⢣⠔⠉⠀⠀⠀⠀⠈⢢⠀⠀⢠⠃⠀⠈⠑⢄⠀⢀⠎⠀⠀⠀⠀⠀⢘⡔⠉⠀⠀⠀⠀⠈⢢⠀⠀⠀⡜⠀⠈⠒⢄⠀⢠⠊⠀⠀⠀⠀⠀⢘⡔⠁⠀⠀⠀⠀⠈⢢⠀⠀⠀⡜⠀⠈⠒⢄⠀⡔⠁⠀⠀⠀
//! ⠣⣀⣀⣀⣀⡠⠒⠁⢣⠀⠀⠀⠀⠀⠀⠀⠑⢄⠎⠀⠀⠀⠀⢀⠕⠣⣀⣀⢀⣀⡠⠒⠁⠘⡄⠀⠀⠀⠀⠀⠀⠑⢄⡰⠁⠀⠀⠀⢀⠕⠥⣀⣀⣀⣀⡠⠒⠁⠘⡄⠀⠀⠀⠀⠀⠀⠑⢄⡸⠀⠀⠀⠀⡠⠛⠤⣀⣀⣀⣀
//! ⠀⠀⠀⠀⠀⠀⠀⠀⠀⢣⠀⠀⠀⠀⠀⠀⢀⠎⠑⠤⢄⠤⠔⠁⠀⠀⠀⠀⠁⠀⠀⠀⠀⠀⠘⡄⠀⠀⠀⠀⠀⠀⡰⠑⠤⠤⠤⠔⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠱⡀⠀⠀⠀⠀⠀⠀⡰⠑⠤⠤⠤⠊⠀⠀⠀⠀⠀⠀⠀
//! ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢣⠀⠀⠀⠀⢀⠎⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⡄⠀⠀⠀⠀⡰⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⡄⠀⠀⠀⠀⡰⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
//! ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⠤⠤⠔⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠢⢄⠤⠊⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠢⠤⠤⠊⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
//! ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
//! ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
//! ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
//! ```
//!
//! ```text
//! ⠀Δt:⠀0.003731337⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀Variable⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
//! acc:⠀122.2455052⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
//! ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
//! ⠀⢀⠔⠉⠉⠑⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠔⠉⠉⠑⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠖⠋⠉⠑⠢⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡰⠉⠉
//! ⢠⠃⠀⠀⠀⠀⠈⢢⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⠃⠀⠀⠀⠀⠈⢲⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠇⠀⠀⠀⠀⠀⠑⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡜⠀⠀⠀
//! ⢇⠀⠀⠀⠀⠀⠀⠀⢣⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡠⠊⠉⠉⠉⢲⢧⠀⠀⠀⠀⠀⠀⠀⢱⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡠⠎⠉⠉⠉⢱⠣⡀⠀⠀⠀⠀⠀⠀⠸⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠔⠉⠉⠉⠑⡾⡀⠀⠀⠀
//! ⠀⠱⡀⣀⠤⠔⠒⠒⠚⢦⢄⠀⠀⠀⠀⠀⠀⢀⠎⠀⠀⠀⠀⢠⠃⠀⠹⡀⣀⠤⠒⠒⠒⠒⢣⢄⠀⠀⠀⠀⠀⠀⢀⠎⠀⠀⠀⠀⢀⠇⠀⠈⢆⢀⡠⠒⠒⠒⠒⠳⣄⠀⠀⠀⠀⠀⠀⠀⡔⠁⠀⠀⠀⠀⡜⠀⠘⢄⢀⡠
//! ⠀⢀⠜⢇⠀⠀⠀⠀⠀⠘⡄⠑⠢⡀⠀⠀⡰⠁⠀⠀⠀⠀⢀⠎⠀⠀⢠⠊⢆⠀⠀⠀⠀⠀⠘⡄⠑⠤⡀⠀⠀⡰⠁⠀⠀⠀⠀⢀⠎⠀⠀⢀⠔⠱⡀⠀⠀⠀⠀⠀⢣⠑⠤⡀⠀⠀⢀⠎⠀⠀⠀⠀⠀⡸⠀⠀⢀⠔⠣⡀
//! ⠔⠁⠀⠀⠣⡀⠀⠀⠀⠀⠘⡄⠀⠑⢄⠜⠀⠀⠀⠀⠀⠀⡎⠀⣀⠔⠁⠀⠀⠣⡀⠀⠀⠀⠀⠘⡄⠀⠈⢢⡔⠁⠀⠀⠀⠀⠀⡎⠀⡠⠔⠁⠀⠀⠘⢄⠀⠀⠀⠀⠀⢇⠀⠈⠢⡠⠃⠀⠀⠀⠀⠀⢰⠁⡠⠔⠁⠀⠀⠘
//! ⠀⠀⠀⠀⠀⠘⢄⠀⠀⠀⠀⠱⡀⡠⠃⠑⠒⠤⣀⣀⣀⣜⠤⠊⠀⠀⠀⠀⠀⠀⠘⢄⠀⠀⠀⠀⠱⡀⡠⠇⠈⠒⠤⣀⣀⣀⣔⠥⠊⠀⠀⠀⠀⠀⠀⠈⠢⡀⠀⠀⠀⠈⢆⢀⠎⠈⠒⢄⣀⡀⣀⣠⠧⠊⠀⠀⠀⠀⠀⠀
//! ⠀⠀⠀⠀⠀⠀⠀⠑⠢⠤⠤⠔⠻⡀⠀⠀⠀⠀⠀⠀⡰⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠢⠤⠤⠔⠻⡀⠀⠀⠀⠀⠀⠀⡸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠢⠤⡠⠤⠚⢇⠀⠀⠀⠀⠀⠈⢀⠇⠀⠀⠀⠀⠀⠀⠀⠀
//! ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠱⡀⠀⠀⠀⠀⡔⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⡄⠀⠀⠀⠀⡔⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢆⠀⠀⠀⠀⢠⠊⠀⠀⠀⠀⠀⠀⠀⠀⠀
//! ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠢⢄⠤⠊⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⠢⠤⠤⠎⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠓⠤⠤⠔⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
//! ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
//! ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
//! ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
//! ```

use std::time;
use textcanvas::charts::Plot;
use textcanvas::utils::GameLoop;
use textcanvas::TextCanvas;

fn main() {
    for _ in 0..3 {
        with_fixed_time_step();
        with_variable_time_step();
    }
}

/// Will loop at a predefined rate.
fn with_fixed_time_step() {
    let mut canvas = TextCanvas::new(80, 16);

    let mut big_sine = TextCanvas::new(80, 10);
    let mut medium_sine = TextCanvas::new(80, 6);
    let mut small_sine = TextCanvas::new(80, 4);

    let f = |x: f64| x.sin();

    let mut i = 0;
    #[rustfmt::skip]
    GameLoop::loop_fixed(time::Duration::from_millis(30), &mut || {
        if i == 108 {
            // Break out of the loop.
            return None;
        }

        let big_offset = f64::from(i) / 350.0 * 37.0;
        let medium_offset = f64::from(i) / 250.0 * 37.0;
        let small_offset = f64::from(i) / 150.0 * 37.0;

        canvas.clear();

        big_sine.clear();
        medium_sine.clear();
        small_sine.clear();

        Plot::function(&mut big_sine, -10.0 + big_offset, 10.0 + big_offset, &f);
        Plot::function(&mut medium_sine, -10.0 + medium_offset, 10.0 + medium_offset, &f);
        Plot::function(&mut small_sine, -10.0 + small_offset, 10.0 + small_offset, &f);

        canvas.merge_canvas(&big_sine, 0, 12);
        canvas.merge_canvas(&medium_sine, 0, 20);
        canvas.merge_canvas(&small_sine, 0, 25);

        canvas.draw_text(&format!("  i: {i}"), 0, 0);
        canvas.draw_text("Fixed", 37, 0);

        i += 1;

        Some(canvas.to_string())
    });
}

/// Will loop as fast as possible.
fn with_variable_time_step() {
    let mut canvas = TextCanvas::new(80, 16);

    let mut big_sine = TextCanvas::new(80, 10);
    let mut medium_sine = TextCanvas::new(80, 6);
    let mut small_sine = TextCanvas::new(80, 4);

    let f = |x: f64| x.sin();

    let mut accumulator = 0.0;
    #[rustfmt::skip]
    GameLoop::loop_variable(&mut |delta_time| {
        if accumulator >= 333.33 {
            // Break out of the loop.
            return None;
        }

        let big_offset = accumulator / 350.0 * 12.0;
        let medium_offset = accumulator / 250.0 * 12.0;
        let small_offset = accumulator / 150.0 * 12.0;

        canvas.clear();

        big_sine.clear();
        medium_sine.clear();
        small_sine.clear();

        Plot::function(&mut big_sine, -10.0 + big_offset, 10.0 + big_offset, &f);
        Plot::function(&mut medium_sine, -10.0 + medium_offset, 10.0 + medium_offset, &f);
        Plot::function(&mut small_sine, -10.0 + small_offset, 10.0 + small_offset, &f);

        canvas.merge_canvas(&big_sine, 0, 12);
        canvas.merge_canvas(&medium_sine, 0, 20);
        canvas.merge_canvas(&small_sine, 0, 25);

        canvas.draw_text(&format!(" Δt: {delta_time:.9}"), 0, 0);
        canvas.draw_text(&format!("acc: {accumulator:>11.7}"), 0, 1);
        canvas.draw_text("Variable", 36, 0);

        accumulator += 100.0 * delta_time;

        Some(canvas.to_string())
    });
}