#plot #svg #css #chart #css-class #themes

poloto

简单的二维绘图库,输出 SVG 并可以使用 CSS 进行样式化

166 个稳定版本 (19 个主要版本)

19.1.2 2023年7月9日
18.1.1 2023年6月4日
18.0.1 2023年3月31日
17.2.1 2023年3月31日
0.12.1 2021年3月7日

#8 in 可视化

Download history 306/week @ 2024-04-26 587/week @ 2024-05-03 928/week @ 2024-05-10 1030/week @ 2024-05-17 501/week @ 2024-05-24 620/week @ 2024-05-31 606/week @ 2024-06-07 501/week @ 2024-06-14 714/week @ 2024-06-21 675/week @ 2024-06-28 565/week @ 2024-07-05 623/week @ 2024-07-12 738/week @ 2024-07-19 835/week @ 2024-07-26 668/week @ 2024-08-02 493/week @ 2024-08-09

2,830 每月下载量
用于 8 crates

MIT 许可证

135KB
3K SLoC

您可以在 githubcrates.io 上找到 poloto。文档位于 docs.rs

一个简单的二维绘图库,将图表输出为 SVG,可以使用 CSS 进行样式化。

Poloto 图表可以使用 CSS 进行样式化,既可以直接在 SVG 中进行,也可以在嵌入 SVG 的 HTML 中进行。后者允许用户将 SVG 与其网站主题动态匹配。用户可以充分利用 CSS,添加悬停高亮、动画、阴影、描边等。请查看 github 示例 了解详情。示例的最新图表输出可以在 assets 文件夹中找到。

您可以在这本 Rust 书中看到它的实际应用 broccoli-book

还可以在 githubcrates.io 上查看 poloto-chrono。它允许绘制 Unix 时间戳。

高斯示例

use poloto::build;
// PIPE me to a file!
fn main() {
    // See https://en.wikipedia.org/wiki/Gaussian_function
    let gau = |sigma: f64, mu: f64| {
        use std::f64::consts::TAU;
        let s = sigma.powi(2);
        let k = (sigma * TAU).sqrt().recip();
        move |x: f64| [x, (-0.5 * (x - mu).powi(2) / s).exp() * k]
    };

    let xs = poloto::util::range_iter([-5.0, 5.0], 200);

    let plots = poloto::plots!(
        build::plot("σ=1.0").line(xs.clone().map(gau(1.0, 0.0))),
        build::plot("σ=0.5").line(xs.clone().map(gau(0.5, 0.0))),
        build::plot("σ=0.3").line(xs.clone().map(gau(0.3, 0.0)))
    );

    poloto::frame_build()
        .data(poloto::plots!(build::origin(), plots))
        .build_and_label(("gaussian", "x", "y"))
        .append_to(poloto::header().light_theme())
        .render_stdout();
}

输出

demo

柯勒茨示例

use poloto::build;
use tagu::prelude::*;

// PIPE me to a file!
fn main() {
    let collatz = |mut a: i128| {
        std::iter::from_fn(move || {
            if a == 1 {
                None
            } else {
                a = if a % 2 == 0 { a / 2 } else { 3 * a + 1 };
                Some(a)
            }
        })
        .fuse()
    };

    let svg = poloto::header().with_viewbox_width(1200.0);

    let style = poloto::render::Theme::dark().append(tagu::build::raw(
        ".poloto_line{stroke-dasharray:2;stroke-width:2;}",
    ));

    let a = (1000..1006).map(|i| build::plot(format!("c({})", i)).line((0..).zip(collatz(i))));

    poloto::frame()
        .with_tick_lines([true, true])
        .with_viewbox(svg.get_viewbox())
        .build()
        .data(poloto::plots!(poloto::build::origin(), a))
        .build_and_label(("collatz", "x", "y"))
        .append_to(svg.append(style))
        .render_stdout();
}

输出

demo

自定义刻度示例

use tagu::format_move;
use poloto::build;
fn main() {
    // hourly trend over one day.
    let trend = vec![
        0, 0, 0, 0, 0, 3, 5, 5, 10, 20, 50, 60, 70, 50, 40, 34, 34, 20, 10, 20, 10, 4, 2, 0,
    ];

    let plots = poloto::plots!(
        build::plot("").histogram((0..).zip(trend)),
        build::markers([24], [])
    );

    let data = poloto::frame_build().data(plots);

    let ticks =
        poloto::ticks::from_iter((0..).step_by(6)).with_tick_fmt(|&v| format_move!("{} hr", v));

    data.map_xticks(|_| ticks)
        .build_and_label(("title", "x", "y"))
        .append_to(poloto::header().light_theme())
        .render_stdout();
}

输出

demo

柱形图示例

fn main() {
    let data = [
        (20, "potato"),
        (14, "broccoli"),
        (53, "pizza"),
        (30, "avocado"),
    ];

    poloto::build::bar::gen_simple("", data, [0])
        .label(("Comparison of Food Tastiness", "Tastiness", "Foods"))
        .append_to(poloto::header().light_theme())
        .render_stdout();
}

输出

demo

样式化示例

use poloto::{build, prelude::OutputZip};
use tagu::prelude::*;
fn main() {
    let theme = poloto::render::Theme::light();

    // Style the first plot and its legend image if it is a histogram.
    let theme = theme.append(tagu::build::raw(
        ".poloto0.poloto_histo.poloto_imgs{fill:red;stroke:black;stroke-width:2px}",
    ));

    // Some attributes have to accessed directly , so use >* to select the rects directly.
    let theme = theme.append(tagu::build::raw(
        ".poloto0.poloto_histo.poloto_imgs>*{rx:20px;ry:20px}",
    ));

    // Style the text of the first legend
    let theme = theme.append(tagu::build::raw(
        ".poloto0.poloto_legend.poloto_text{fill:blue;}",
    ));

    // Style all line plots but not legend img.
    let theme = theme.append(tagu::build::raw(".poloto_line.poloto_imgs.poloto_plot{stroke:purple;stroke-width:20px;stroke-dasharray:40px}"));

    // Style all line plot legend imgs.
    let theme = theme.append(tagu::build::raw(".poloto_line.poloto_imgs.poloto_legend{stroke:purple;stroke-width:10px;stroke-dasharray:10px}"));

    // Style the scatter plots but not legend img
    let theme = theme.append(tagu::build::raw(
        ".poloto_scatter.poloto_plot{fill:purple;stroke-width:20px;}",
    ));

    // Style the scatter plots but not legend img
    let theme = theme.append(tagu::build::raw(
        ".poloto_scatter.poloto_plot{fill:purple;stroke-width:20px;}",
    ));

    // Style the xaxis name
    let theme = theme.append(tagu::build::raw(
        ".poloto_name.poloto_x{fill:orange;stroke-width:20px;font-size:30px;font-style: italic;}",
    ));

    // Style the background
    let theme = theme.append(tagu::build::raw(".poloto_background{fill:darkslategray;}"));

    // Style the text
    let theme = theme.append(tagu::build::raw(".poloto_text{fill: peru;}"));

    // Style the ticks
    let theme = theme.append(tagu::build::raw(
        ".poloto_imgs.poloto_ticks{stroke:springgreen;}",
    ));

    let x = (0..50).map(|x| (x as f64 / 50.0) * 10.0);

    let data = poloto::plots!(
        build::plot("sin-10").histogram(x.clone().step_by(3).zip_output(|x| x.sin() - 10.)),
        build::plot("cos").line(x.clone().zip_output(|x| x.cos())),
        build::plot("sin-5").scatter(x.clone().step_by(3).zip_output(|x| x.sin() - 5.))
    );

    poloto::frame_build()
        .data(data)
        .build_and_label((
            "Demo: you can change the style of the svg file itself!",
            "x axis",
            "y axis",
        ))
        .append_to(poloto::header().append(theme))
        .render_stdout();
}

输出

demo

用例

Poloto 将每个图表转换为类似于圆圈的 SVG 元素。因此,它并不适合有大量图表的图表。对于这些情况,您可能需要使用允许您直接将图表绘制到 png/jpg 图像的库。当然,您可以将生成的 SVG 图像转换为位图,但如果有很多图表,生成和显示 SVG 的效率不会很高。

clonedbuffered 绘图迭代器

poloto 会遍历绘图迭代器两次。一次是为了获取最小/最大边界,另一次是为了根据这些边界缩放所有图表。这有两种方法。一种是将迭代器克隆并消费两次。另一种方法是将一个迭代器的项目累积到一个 Vec 中,然后只遍历这个 vec。默认情况下,poloto 将使用基于 Vec 的缓冲区。但是,您可以配置它来克隆迭代器。

//Uses vec backed buffer
let data=[[1.0,2.0],[2.0,3.0]];
poloto::build::plot("").line(data);


//Cloned the iterator
let it=(0..).take(10).zip(5..);
poloto::build::plot("").line(poloto::build::cloned(it));

使用克隆方法有优点和缺点。用户有更多的控制权,可以减少内存使用。但是,他们可能会不小心增加内存使用。

更多示例

以下是使用evcxr在jupyter笔记本中展示的更多示例。https://nbviewer.org/github/tiby312/poloto-project/blob/master/poloto-evcxr/float_int_test.ipynb https://nbviewer.org/github/tiby312/poloto-project/blob/master/poloto-evcxr/test_simple.ipynb

笔记本的源代码可以在这里找到:https://github.com/tiby312/poloto-project/tree/master/poloto-evcxr

转义保护

如果用户尝试通过title/xname/yname/tick格式或绘图名称注入HTML,HTML转义将被转换为它们的编码值。这种保护由tagu依赖库提供。

CSS使用示例

请参阅本报告中的图表:broccoli_book

CSS类

  • poloto[n]fill - 如果第n个绘图需要填充(例如,linefill或histogram)。
  • poloto[n]stroke - 如果第n个绘图需要描边(例如,line或scatter)。

我可以更改绘图样式吗?

是的!您可以在svg中或在html中的嵌入式svg中利用CSS的强大功能。您可以做一些事情

  • 更改颜色方案以适应您的html主题。
  • 突出显示一个绘图,使其断开,或添加悬停效果
  • 使用@keyframes动画化事物

Plotter结构体记录了您可以修改的整个图表的CSS类。每个绘图函数记录了您可以修改以更改特定绘图的CSS类。

散点图是通过由零长度线组成的SVG路径完成的。这允许您通过更改描边宽度来更改散点圆点的半径。

格式化刻度间隔

Poloto首先以所需的精度以正常十进制打印间隔,以捕捉间隔之间步长的差异。如果检测到数字的幅度太大或太小,则可能切换到科学计数法,仍需以所需的精度。它只有在科学计数法版本实际上比正常十进制格式少字符时才会切换,而您在考虑可能需要捕获的步长精度时,这并不总是情况。

即使有上述系统,也存在所有数字都具有很大幅度但彼此非常接近(小步长)的情况。在这种情况下,实际上并没有很好的格式化方式。在这些情况下,poloto将回退到将数字相对于第一个数字进行格式化。

如何渲染为png?

您可以使用resvg。安装它,然后运行类似以下命令的命令

resvg -w 1200 target/assets/collatz.svg target/assets/collatz.png

输出

demo

依赖关系