Terminal plotting library for using in Rust CLI applications

Overview

textplots Crates.io

Terminal plotting library for using in Rust CLI applications. Should work well in any unicode terminal with monospaced font.

It is inspired by TextPlots.jl which is inspired by Drawille.

Currently it features only drawing line charts on Braille canvas, but could be extended to support other canvas and chart types just like UnicodePlots.jl or another cool terminal plotting library.

Contributions are very much welcome!

Usage

Using as a library

use textplots::{Chart, Plot, Shape};

fn main() {
    println!("y = sin(x) / x");

    Chart::default()
        .lineplot(&Shape::Continuous(Box::new(|x| x.sin() / x)))
        .display();
}

Using as a binary

$ textplots '10*x + x^2 + 10*sin(x)*abs(x)' --xmin=-20 --xmax=20

Bonus! Colored plots (see examples)

Comments
  • switch to GitHub Actions

    switch to GitHub Actions

    No worries if you want to stick with Travis for CI, but thought I'd throw it up to see what you thought. Since it's coming from a fork you won't see the CI running, but see this PR on my fork for what it'd look like for future PRs if you merged.

    opened by TDHolmes 8
  • Scatter plots

    Scatter plots

    Hi

    I've started using this library in some test code I've been writing and I've reached a few places where I would like to be able to create a scatter plot.

    Would it be possible/easy to implement scatter plots into this library?

    Thanks

    opened by Skeletonxf 5
  • Add binary to the crate (#20)

    Add binary to the crate (#20)

    This PR adds a binary of the same name to the crate. It introduces a breaking change in the API. Resolves #21, resolves #20.

    Changes

    • Add src/main.rs.

    • Change Shape::Continous in src/lib.rs.

      Before:

      pub enum Shape<'a> {
         Continuous(fn(f32) -> f32),
         ...
      }
      

      After:

      pub enum Shape<'a> {
         Continuous(Box<dyn FnMut(f32) -> f32 + 'a>),
         ...
      }
      

      This is a breaking change in the API, so the minor version must be increased. I explained the motivation behind it in #21. I don't believe it is possible to add a dynamic math expression parser without introducing this change.

      I also added #![feature(box_syntax)] on top of every example and box before each closure, including the examples in documentation and README.md. If you do not wish to include this line each time you can switch to stable syntax: Box::new(...). Alternative solutions are provided in #21.

      This change is incompatible with #14 (no_std support) as it requires allocating Boxes. A possible solution is to use references or generic types in Shape. Another solution is to move drawing logic from Chart to shapes and introduce a Shape trait instead of an enum with predefined types in its fields.

    • Change version in Cargo.toml to 0.6.0.

    • Set edition = "2018" in Cargo.toml. Add bin and lib sections to Cargo.toml.

      This makes configuring a separate binary and library easier. I also removed each extern crate since they are not necessary in the 2018 edition.

    • Use clap to parse args and display pretty info about the binary.

    • Use meval to parse math expressions.

    Behavior

    Basic usage (prints the formula and displays the plot)

    $ textplots 'e ^ (1 / x)'
    

    Error info on no formula

    $ textplots
    error: The following required arguments were not provided:
        <FORMULA>
        
    USAGE:
        textplots <FORMULA>
            
    For more information try --help
    

    --help

    $ textplots --help
    textplots 0.6.0
    Alexey Suslov <[email protected]>
    Terminal plotting library.
    
    USAGE:
        textplots <FORMULA>
        
    FLAGS:
        -h, --help       Prints help information
        -V, --version    Prints version information
    
    ARGS:
        <FORMULA>    Formula to plot
    

    Error info on unknown variable

    $ textplots 'y'
    Evaluation error: unknown variable `y`.
    

    Error on unexpected token

    $ textplots `100x`
    Parse error: Unexpected token at byte 3.
    

    Possible future development

    • Custom math parsing errors.
    • Multiple plots.
    • Colors (#10).
    • Receiving input from pipes.
    opened by micouy 4
  • Add y_label option

    Add y_label option

    Just something quick to be able to throw a Y-axis label on the generated plot. Happy to change anything if you don't like the implementation, I went with the Option<String> to make it work safely with existing code.

    opened by ccakes 4
  • Cannot set ymin ymax

    Cannot set ymin ymax

    I cannot set these and the defaults aren't always pretty:

    ⡁⣾    ⣾    ⡀          ⣾  ⢀⢾       ⢰⠉⡇     204.3
    ⠄⣿   ⡸⢸   ⢸⡇         ⢠⢻  ⡎⢸       ⢸ ⡇    
    ⠂⣿   ⡇⢸   ⢸⡇  ⢀      ⡸⢸  ⡇⢸       ⢸ ⡇    
    ⡁⣿   ⡇⢸   ⢸⡇  ⣿      ⡇⢸  ⡇⢸       ⢸ ⡇    
    ⠄⣿   ⡇⢸   ⢸⡇  ⣿      ⡇⢸  ⡇⢸       ⢸ ⡇    
    ⠂⠏⡆  ⡇⢸   ⢸⢣  ⣿      ⡇⢸  ⡇⢸  ⡀    ⢸ ⢱    
    ⡁ ⡇  ⡇⢸   ⢸⢸ ⢀⢿      ⡇⢸  ⡇⢸ ⢸⡇    ⢸ ⢸    
    ⠄ ⡇  ⡇⢸   ⢸⢸ ⢸⢸      ⡇⢸  ⡇⢸ ⢸⡇    ⢸ ⢸    
    ⠂ ⡇  ⡇⢸   ⢸⢸ ⢸⢸      ⡇⢸  ⡇⢸ ⡇⡇    ⡎ ⢸    
    ⡁ ⡇  ⡇⢸ ⢀⡄⢸⢸ ⢸⢸      ⡇⢸  ⡇⢸ ⡇⡇   ⢰⠁ ⢸    
    ⠄ ⠑⠒⠒⠃⠈⠒⠃⠑⠚⠈⠒⠚⠘⠤⠒⠒⠒⠒⠒⠃⠈⠒⠒⠃ ⠉⠁⠑⠒⠒⠒⠚  ⠈⠒⠒⠒⠄
    ⠂                                        
    ⡁                                        
    ⠄                                        
    ⠂                                        
    ⡁                                        
    ⠄                                        
    ⠂                                        
    ⡁                                        
    ⠄                                        
    ⠁⠈ ⠁⠈ ⠁⠈ ⠁⠈ ⠁⠈ ⠁⠈ ⠁⠈ ⠁⠈ ⠁⠈ ⠁⠈ ⠁⠈ ⠁⠈ ⠁⠈ ⠁  10.0
    

    I would like to be able to set these as well as perhaps a method to shrink wrap to the min and max from the slice.

    opened by joedborg 3
  • feat: Add ability to get a raw string instead of printing to stdout

    feat: Add ability to get a raw string instead of printing to stdout

    This PR adds a new method, to_string(), which will return an owned String of the rendered plot. This String can then be used with custom logging or reporting methods instead of just being piped to stdout.

    Instead of impl'ing the From/Into trait(s) a to_string() method was used which is more consistent with the std library's conversion to strings.

    Issue: https://github.com/loony-bean/textplots-rs/issues/29

    opened by BroderickCarlin 2
  • crates.io lists textplots-rs as a command when it is a librar

    crates.io lists textplots-rs as a command when it is a librar

    Here https://crates.io/crates/textplots the project erroneously has the category "Command line utilities".

    Could you please remove it?

    Thanks, Yuri

    opened by yurivict 2
  • Plans on adding a binary to the crate?

    Plans on adding a binary to the crate?

    Hi! I really like this crate. Would you consider adding a binary so that it could be used as a standalone CLI tool? It would be perfect in a situation where I want to plot something quickly. I know the implementation would require parsing math expressions, handling input/output etc. which may be beyond the scope of this crate so I understand if that's not your goal.

    opened by micouy 2
  • Feature request: String output of Chart

    Feature request: String output of Chart

    Chart::display() output directly with println!()

    It would be nice to be able to return a String. That allows for example to transfer over a network or include a log file.

    It seems relatively staightforward to use format!() instead and allow this additional method. The issue might be more a consistent API pattern (display() vs display_string() etc) across the library for similiar situations.

    enhancement help wanted 
    opened by paulirotta 1
  • Incorrect plotting when using multiple lineplots

    Incorrect plotting when using multiple lineplots

    There's a nasty bug that renders badly any chart involving multiple lineplots.

    I made a simple example to illustrate the problem.

    All the lines should be parallel to each other, and there should be only one x axis displayed. But an axis gets displayed per lineplot, and all the lines converge to the same point.

    extern crate textplots;
    use textplots::{Chart, Plot, Shape};
    
    fn main() {
        let (mut l1, mut l2, mut l3) = (vec![], vec![], vec![]);
        for n in -2..=2 {
            l1.push((n as f32, n as f32));
            l2.push((n as f32, n as f32 - 1.));
            l3.push((n as f32, n as f32 - 2.));
        }
        println!("\nf(x)=x; f(x)=x-1; f(x)=x-2");
        Chart::new(120, 80, -2., 2.)
            .lineplot(Shape::Lines(l1.as_slice()))
            .lineplot(Shape::Lines(l2.as_slice()))
            .lineplot(Shape::Lines(l3.as_slice()))
            .nice();
    
        let (mut l4, mut l5, mut l6) = (vec![], vec![], vec![]);
        for n in -2..=2 {
            l4.push((n as f32, n as f32));
            l5.push((n as f32, n as f32 + 1.));
            l6.push((n as f32, n as f32 + 2.));
        }
        println!("\nf(x)=x; f(x)=x+1; f(x)=x+2");
        Chart::new(120, 80, -2., 2.)
            .lineplot(Shape::Lines(l4.as_slice()))
            .lineplot(Shape::Lines(l5.as_slice()))
            .lineplot(Shape::Lines(l6.as_slice()))
            .nice();
    }
    

    image

    Related: #8

    bug help wanted 
    opened by joseluis 1
  • Wrong graph produced

    Wrong graph produced

    They should only intersect at 0,0

    ⠑⢄                     ⣀⠤⠔⠒⠊⠉⠉⡉⠉⠉⠒⠒⠤⢄⡀                    ⢀⠔  100.0
      ⠑⢄⡀              ⢀⡠⠒⠉       ⠄      ⠈⠑⠢⣀               ⣀⠔⠁
        ⠈⠢⡀          ⡠⠔⠁          ⠂          ⠑⠤⡀          ⡠⠊
          ⠈⠑⢄⡀     ⡠⠊             ⡁            ⠈⠢⡀     ⣀⠔⠉
             ⠈⠒⢄⡀⡠⠊               ⠄              ⠈⠢⡀⣀⠔⠊
               ⢀⠎⠑⠢⢄⡀             ⠂             ⣀⠤⠒⠙⢄
              ⡔⠁    ⠈⠑⠢⠤⣀⡀        ⡁        ⣀⡠⠤⠒⠉     ⠱⡀
    ⠄⠠ ⠄⠠ ⠄⠠⢠⠎⠠ ⠄⠠ ⠄⠠ ⠄⠠ ⠌⠩⠑⠖⠲⠢⠤⠤⠤⠤⠤⠤⠤⠲⠒⠖⠩⠉⠄⠠ ⠄⠠ ⠄⠠ ⠄⠠⠈⢆⠠ ⠄⠠ ⠄⠠ ⠄
           ⡰⠁                     ⠂                     ⠣⡀
         ⢀⠜                       ⡁                      ⠑⡄
        ⢀⠎                        ⠄                       ⠘⢄
       ⢠⠃                         ⠂                        ⠈⢆
      ⡠⠃                          ⡁                         ⠈⢢
     ⡰⠁                           ⠄                           ⢣
    ⡰⠁                            ⠂                            ⠣
    ⠁                             ⠁                               -100.0
    -10.0                                                    10.0
    
    extern crate textplots;
    
    use textplots::{Chart, Plot, Shape};
    
    fn main() {
        Chart::default()
        .lineplot( Shape::Continuous( |x| { (-x.powf(2.0)) } ))
        .lineplot( Shape::Continuous( |x| { (x.powf(2.0)) } ))
        .display();
    }
    
    bug help wanted 
    opened by bazzb 1
  • Further Plot Customization—Hide Axis

    Further Plot Customization—Hide Axis

    Hello, I've poked around a little bit, but can tend to miss stuff, so sorry if I have.

    I'm looking to plot without the axis at all. I don't mind attempting to contribute to add this feature myself, but I wanted to check here first. Here is what my plots are looking like:

    image

    I would like both the axis, as well as the axis labels to be omitted. Is this possible? And if not, would it be desirable to add in to this library?

    opened by milespossing 0
  • added the option of displaying Points with colors

    added the option of displaying Points with colors

    I've added the option of using colors also when displaying points. This is done by using the set_colored from drawille. The change follows the same notation as for Continuous I've also added an example sparse_color to illustrate a minimal use.

    opened by apelloni 0
  • Add axes formatting callbacks

    Add axes formatting callbacks

    This adds the ability to specify a callback for formatting the min/max labels for both the x and y axis. This allows formatting the x axis as dates (#9) or adding units, and including colored strings.

    I have also included a commit that changes the rendering order so that the axes are printed behind the plots. This prevents the white of the axes resetting the color of the plots. If you want, I can split this up into multiple PRs.

    Example Code (from and to are chrono::NaiveDate):

            let mut chart = Chart::new(200, 40, 0.0, (to - from).num_days() as f32)
                .xaxis_formatter(move |x| format!("{}", from.clone() + Duration::days(x as i64)))
                .yaxis_formatter(move |y| format!("{:.1} {}", y, unit.clone().white()));
    

    Result (without reordering commit):

    Result (with reordering commit):

    opened by KoffeinFlummi 0
  • lib-version only without structopt dependency

    lib-version only without structopt dependency

    I'm using textplots as a lightweight lib only. Would it be possible to either split it into two crates, or use features, to have a structopt and meval dependencies free build?

    opened by dvtomas 9
  • Top is missing in Bars

    Top is missing in Bars

    ⡁                             ⡇                                                             3.0
    ⠄                             ⡇                                                            
    ⠂                             ⡇                                                            
    ⡁                             ⡇                                                            
    ⠄                             ⡇                                                            
    ⠂                             ⡇                                                            
    ⡁                             ⡇                                                            
    ⠄                             ⡇                                                            
    ⠂                             ⡇                                                            
    ⡁                             ⡇                                                            
    ⠄                             ⡏⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⡇                              
    ⠂                             ⡇                             ⡇                              
    ⡁                             ⡇                             ⡇                              
    ⠄                             ⡇                             ⡇                              
    ⠂                             ⡇                             ⡇                              
    ⡁                             ⡇                             ⡇                              
    ⠄                             ⡇                             ⡇                              
    ⠂                             ⡇                             ⡇                              
    ⡁                             ⡇                             ⡇                              
    ⠄                             ⡇                             ⡇                              
                                  ⠁                             ⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠁ 1.0
    0.0                                                                                    3.0
    
    

    The first bar is missing its roof.

    opened by ahcm 5
  • Plotting in a loop

    Plotting in a loop

    Hi

    I updated my dependencies recently and code which was previously working on textplots 0.5.0 is no longer working on textplots 0.5.4

    My problem is essentially that I want to break up the method chains with a loop.

    let lines: Vec<_> = points.iter().map(|p| Shape::Lines(p)).collect();
    for line in lines.iter() {
        chart.lineplot(line);
    }
    chart.display();
    

    This strategy doesn't compile anymore.

    error[E0499]: cannot borrow `chart` as mutable more than once at a time
       --> tests/bayesian_regression.rs:245:17
        |
    245 |                 chart.lineplot(line);
        |                 ^^^^^ mutable borrow starts here in previous iteration of loop
    
    error[E0499]: cannot borrow `chart` as mutable more than once at a time
       --> tests/bayesian_regression.rs:247:13
        |
    245 |                 chart.lineplot(line);
        |                 ----- first mutable borrow occurs here
    246 |             }
    247 |             chart.display();
        |             ^^^^^
        |             |
        |             second mutable borrow occurs here
        |             first borrow later used here
    

    I notice that the Chart used to take Shapes by value https://docs.rs/textplots/0.5.0/textplots/trait.Plot.html and it now borrows them https://docs.rs/textplots/0.5.4/textplots/trait.Plot.html

    Would it be possible for you to add a second plot method which didn't return the mutable reference from the method? https://docs.rs/textplots/0.5.4/src/textplots/lib.rs.html#334

    I think the returned mutable borrow while I still have the first is causing my problem.

    question 
    opened by Skeletonxf 4
Owner
Alexey Suslov
Alexey Suslov
create and test the style and formatting of text in your terminal applications

description: create and test the style and formatting of text in your terminal applications docs: https://docs.rs/termstyle termstyle is a library tha

Rett Berg 18 Jul 3, 2021
A tool for automating terminal applications in Unix.

expectrl A tool for automating terminal applications in Unix. Using the library you can: Spawn process Control process Expect/Verify responces It was

Maxim Zhiburt 132 Dec 14, 2022
Update informer for CLI applications written in Rust 🦀

Update-informer Update informer for CLI applications written in Rust ?? Usage Add to Cargo.toml: [dependencies] update-notifier = "0.1.0" To check the

Grachev Mikhail 166 Dec 18, 2022
CarLI is a framework for creating single-command and multi-command CLI applications in Rust

CarLI is a framework for creating single-command and multi-command CLI applications in Rust. The framework provides error and IO types better suited for the command line environment, especially in cases where unit testing is needed.

Kevin Herrera 3 Jan 21, 2022
CLI for self-bootstrapped Python applications

PyApp PyApp is a CLI wrapper for Python applications that bootstrap themselves at runtime. Each application is configured with environment variables a

Ofek Lev 6 May 10, 2023
A terminal ASCII media player. View images, gifs, videos, webcam, YouTube, etc.. directly in the terminal as ASCII art.

Terminal Media Player View images, videos (files or YouTube links), webcam, etc directly in the terminal as ASCII. All images you see below are just m

Max Curzi 36 May 8, 2023
ask.sh: AI terminal assistant that can read and write your terminal directly!

ask.sh: AI terminal assistant that read from & write to your terminal ask.sh is an AI terminal assistant based on OpenAI APIs such as GPT-3.5/4! What'

hmirin 5 Jun 20, 2023
A simple and efficient terminal UI implementation with ratatui.rs for getting quick insights from csv files right on the terminal

CSV-GREP csv-grep is an intuitive TUI application writting with ratatui.rs for reading, viewing and quickly analysing csv files right on the terminal.

Anthony Ezeabasili 16 Mar 10, 2024
argmax is a library that allows Rust applications to avoid Argument list too long errors (E2BIG) by providing a std::process::Command wrapper with a

argmax argmax is a library that allows Rust applications to avoid Argument list too long errors (E2BIG) by providing a std::process::Command wrapper w

David Peter 22 Nov 20, 2022
A library to provide abstractions to access common utilities when developing Dioxus applications.

?? Dioxus Standard Library ?? A platform agnostic library for supercharging your productivity with Dioxus. dioxus-std is a Dioxus standard library tha

Miles Murgaw 5 Nov 9, 2022
zigfi is an open-source stocks, commodities and cryptocurrencies price monitoring CLI app, written fully in Rust, where you can organize assets you're watching easily into watchlists for easy access on your terminal.

zigfi zigfi is an open-source stocks, commodities and cryptocurrencies price monitoring CLI app, written fully in Rust, where you can organize assets

Aldrin Zigmund Cortez Velasco 18 Oct 24, 2022
A simple CLI I made while practicing rust to easily make QR codes with just one command, all in your terminal.

Welcome to rust-qrcode-cli ?? A CLI I made while practicing rust to easily make QR codes with just one command, all in your terminal. Install git clon

Dhravya Shah 2 Mar 2, 2022
A CLI tool connected to GPT-3 to help find the right terminal command

Command Recall A CLI tool connected to GPT-3 to help find the right terminal command Install to install the cli: cargo install --git https://github.co

Camille Moatti 102 Feb 18, 2023
ChatGPT CLI - A minimal assistant in the terminal

ChatGPT CLI A lightweight ChatGPT CLI - Chat completion. Interact with ChatGPT from your terminal and save the conversation in a text file. Get starte

Imad E. 5 Mar 14, 2023
glicol cli: cross-platform music live coding in terminal

glicol-cli What's this? It's a command line interface that you can use for music live coding with Glicol. It watches a file changes and then update th

Glicol 70 Apr 14, 2023
A CLI tool to copy-paste different Ipsums from your terminal

Lorem clipsum Explore the docs » View Demo · Report Bug · Request Feature Table of Contents About The Project Built With Usage Contributing Contact Ab

Schrödinger Hat 9 May 30, 2023
Tiny CLI tool that helps to visualize iCal file content in the terminal.

Calio Calio is a tiny CLI tool that helps to visualize iCal file in the terminal. Installation You can either install it via cargo or download the bin

Oscar Cortez 5 Jun 12, 2023
A simple cli tool for generating quotes in your terminal from Kanye west. Start the day out strong.

Kanyey A simple cli tool for generating quotes in your terminal from Kanye West. Install Just do cargo install kanyey and be blessed. Bonus: throw it

null 3 Sep 29, 2023
Build terminal user interfaces and dashboards using Rust

tui-rs tui-rs is a Rust library to build rich terminal user interfaces and dashboards. It is heavily inspired by the Javascript library blessed-contri

Florian Dehau 9.3k Jan 4, 2023