Skip to content

Plot

plot 模块提供了构建自定义图表所需的底层能力,包括比例尺、图形和辅助工具。高层 Chart 组件也是基于这些原语实现的,适合需要完全控制图表绘制逻辑的场景。

导入

rust
use gpui_component::plot::{
    scale::{Scale, ScaleLinear, ScaleBand, ScalePoint, ScaleOrdinal},
    shape::{Bar, Stack, Line, Area, Pie, Arc},
    PlotAxis, AxisText
};

比例尺

比例尺用于把抽象数据映射成可视化坐标或样式。

ScaleLinear

rust
let scale = ScaleLinear::new(
    vec![0., 100.],
    vec![0., 500.]
);

scale.tick(&50.);

ScaleBand

rust
let scale = ScaleBand::new(
    vec!["A", "B", "C"],
    vec![0., 300.]
)
.padding_inner(0.1)
.padding_outer(0.1);

scale.band_width();
scale.tick(&"A");

ScalePoint

rust
let scale = ScalePoint::new(
    vec!["A", "B", "C"],
    vec![0., 300.]
);

scale.tick(&"A");

ScaleOrdinal

rust
let scale = ScaleOrdinal::new(
    vec!["A", "B", "C"],
    vec![color1, color2, color3]
);

scale.map(&"A");

图形

Bar

rust
Bar::new()
    .data(data)
    .band_width(30.)
    .x(|d| x_scale.tick(&d.category))
    .y0(|d| y_scale.tick(&0.).unwrap())
    .y1(|d| y_scale.tick(&d.value))
    .fill(|d| color_scale.map(&d.category))
    .paint(&bounds, window, cx);

Line

rust
Line::new()
    .data(data)
    .x(|d| x_scale.tick(&d.date))
    .y(|d| y_scale.tick(&d.value))
    .stroke(cx.theme().chart_1)
    .stroke_width(px(2.))
    .paint(&bounds, window);

带节点的折线

rust
Line::new()
    .data(data)
    .x(|d| x_scale.tick(&d.date))
    .y(|d| y_scale.tick(&d.value))
    .dot()
    .dot_size(px(4.))
    .paint(&bounds, window);

Area

rust
Area::new()
    .data(data)
    .x(|d| x_scale.tick(&d.date))
    .y0(height)
    .y1(|d| y_scale.tick(&d.value))
    .fill(cx.theme().chart_1.opacity(0.5))
    .stroke(cx.theme().chart_1)
    .paint(&bounds, window);

Pie 与 Arc

rust
let pie = Pie::new()
    .value(|d| Some(d.value))
    .pad_angle(0.05);

let arcs = pie.arcs(&data);

let arc_shape = Arc::new()
    .inner_radius(0.)
    .outer_radius(100.);

for arc_data in arcs {
    arc_shape.paint(
        &arc_data,
        color_scale.map(&arc_data.data.category),
        None,
        None,
        &bounds,
        window
    );
}

Stack

rust
let stack = Stack::new()
    .data(data)
    .keys(vec!["series1", "series2"])
    .value(|d, key| match key {
        "series1" => Some(d.val1),
        "series2" => Some(d.val2),
        _ => None
    });

let series = stack.series();

组件

PlotAxis

rust
PlotAxis::new()
    .x(height)
    .x_label(labels)
    .stroke(cx.theme().border)
    .paint(&bounds, window, cx);

示例

自定义堆叠柱状图

rust
struct StackedBarChart {
    data: Vec<DailyDevice>,
    series: Vec<StackSeries<DailyDevice>>,
}

impl StackedBarChart {
    pub fn new(data: Vec<DailyDevice>) -> Self {
        let series = Stack::new()
            .data(data.clone())
            .keys(vec!["desktop", "mobile"])
            .value(|d, key| match key {
                "desktop" => Some(d.desktop),
                "mobile" => Some(d.mobile),
                _ => None,
            })
            .series();

        Self { data, series }
    }
}

impl Plot for StackedBarChart {
    fn paint(&mut self, bounds: Bounds<Pixels>, window: &mut Window, cx: &mut App) {
        // 1. 准备比例尺
        let x = ScaleBand::new(
            self.data.iter().map(|v| v.date.clone()).collect(),
            vec![0., width],
        );

        let y = ScaleLinear::new(vec![0., max_value], vec![height, 0.]);

        // 2. 绘制坐标轴
        // ...(坐标轴绘制逻辑)

        // 3. 绘制堆叠柱
        let bar = Bar::new()
            .stack_data(&self.series)
            .band_width(x.band_width())
            .x(move |d| x.tick(&d.data.date))
            .fill(move |_| cx.theme().chart_1);

        bar.paint(&bounds, window, cx);
    }
}