mirror of
https://github.com/ratatui-org/ratatui
synced 2024-11-10 07:04:17 +00:00
feat(chart): add GraphType::Bar (#1205)
![Demo](https://vhs.charm.sh/vhs-50v7I5n7lQF7tHCb1VCmFc.gif)
This commit is contained in:
parent
7bab9f0d80
commit
5b51018501
2 changed files with 121 additions and 19 deletions
|
@ -153,17 +153,18 @@ fn run_app<B: Backend>(
|
|||
fn ui(frame: &mut Frame, app: &App) {
|
||||
let area = frame.size();
|
||||
|
||||
let vertical = Layout::vertical([Constraint::Percentage(40), Constraint::Percentage(60)]);
|
||||
let horizontal = Layout::horizontal([Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)]);
|
||||
let [chart1, bottom] = vertical.areas(area);
|
||||
let [line_chart, scatter] = horizontal.areas(bottom);
|
||||
let [top, bottom] = Layout::vertical([Constraint::Fill(1); 2]).areas(area);
|
||||
let [animated_chart, bar_chart] =
|
||||
Layout::horizontal([Constraint::Fill(1), Constraint::Length(29)]).areas(top);
|
||||
let [line_chart, scatter] = Layout::horizontal([Constraint::Fill(1); 2]).areas(bottom);
|
||||
|
||||
render_chart1(frame, chart1, app);
|
||||
render_animated_chart(frame, animated_chart, app);
|
||||
render_barchart(frame, bar_chart);
|
||||
render_line_chart(frame, line_chart);
|
||||
render_scatter(frame, scatter);
|
||||
}
|
||||
|
||||
fn render_chart1(f: &mut Frame, area: Rect, app: &App) {
|
||||
fn render_animated_chart(f: &mut Frame, area: Rect, app: &App) {
|
||||
let x_labels = vec![
|
||||
Span::styled(
|
||||
format!("{}", app.window[0]),
|
||||
|
@ -189,7 +190,7 @@ fn render_chart1(f: &mut Frame, area: Rect, app: &App) {
|
|||
];
|
||||
|
||||
let chart = Chart::new(datasets)
|
||||
.block(Block::bordered().title("Chart 1".cyan().bold()))
|
||||
.block(Block::bordered())
|
||||
.x_axis(
|
||||
Axis::default()
|
||||
.title("X Axis")
|
||||
|
@ -208,6 +209,51 @@ fn render_chart1(f: &mut Frame, area: Rect, app: &App) {
|
|||
f.render_widget(chart, area);
|
||||
}
|
||||
|
||||
fn render_barchart(frame: &mut Frame, bar_chart: Rect) {
|
||||
let dataset = Dataset::default()
|
||||
.marker(symbols::Marker::HalfBlock)
|
||||
.style(Style::new().fg(Color::Blue))
|
||||
.graph_type(GraphType::Bar)
|
||||
// a bell curve
|
||||
.data(&[
|
||||
(0., 0.4),
|
||||
(10., 2.9),
|
||||
(20., 13.5),
|
||||
(30., 41.1),
|
||||
(40., 80.1),
|
||||
(50., 100.0),
|
||||
(60., 80.1),
|
||||
(70., 41.1),
|
||||
(80., 13.5),
|
||||
(90., 2.9),
|
||||
(100., 0.4),
|
||||
]);
|
||||
|
||||
let chart = Chart::new(vec![dataset])
|
||||
.block(
|
||||
Block::bordered().title(
|
||||
Title::default()
|
||||
.content("Bar chart".cyan().bold())
|
||||
.alignment(Alignment::Center),
|
||||
),
|
||||
)
|
||||
.x_axis(
|
||||
Axis::default()
|
||||
.style(Style::default().gray())
|
||||
.bounds([0.0, 100.0])
|
||||
.labels(vec!["0".bold(), "50".into(), "100.0".bold()]),
|
||||
)
|
||||
.y_axis(
|
||||
Axis::default()
|
||||
.style(Style::default().gray())
|
||||
.bounds([0.0, 100.0])
|
||||
.labels(vec!["0".bold(), "50".into(), "100.0".bold()]),
|
||||
)
|
||||
.hidden_legend_constraints((Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)));
|
||||
|
||||
frame.render_widget(chart, bar_chart);
|
||||
}
|
||||
|
||||
fn render_line_chart(f: &mut Frame, area: Rect) {
|
||||
let datasets = vec![Dataset::default()
|
||||
.name("Line from only 2 points".italic())
|
||||
|
|
|
@ -144,11 +144,15 @@ pub enum GraphType {
|
|||
/// Draw each point. This is the default.
|
||||
#[default]
|
||||
Scatter,
|
||||
|
||||
/// Draw a line between each following point.
|
||||
///
|
||||
/// The order of the lines will be the same as the order of the points in the dataset, which
|
||||
/// allows this widget to draw lines both left-to-right and right-to-left
|
||||
Line,
|
||||
|
||||
/// Draw a bar chart. This will draw a bar for each point in the dataset.
|
||||
Bar,
|
||||
}
|
||||
|
||||
/// Allow users to specify the position of a legend in a [`Chart`]
|
||||
|
@ -362,9 +366,10 @@ impl<'a> Dataset<'a> {
|
|||
|
||||
/// Sets how the dataset should be drawn
|
||||
///
|
||||
/// [`Chart`] can draw either a [scatter](GraphType::Scatter) or [line](GraphType::Line) charts.
|
||||
/// A scatter will draw only the points in the dataset while a line will also draw a line
|
||||
/// between them. See [`GraphType`] for more details
|
||||
/// [`Chart`] can draw [scatter](GraphType::Scatter), [line](GraphType::Line) or
|
||||
/// [bar](GraphType::Bar) charts. A scatter chart draws only the points in the dataset, a line
|
||||
/// char draws a line between each point, and a bar chart draws a line from the x axis to the
|
||||
/// point. See [`GraphType`] for more details
|
||||
///
|
||||
/// This is a fluent setter method which must be chained or used as it consumes self
|
||||
#[must_use = "method moves the value of self and returns the modified value"]
|
||||
|
@ -998,16 +1003,30 @@ impl WidgetRef for Chart<'_> {
|
|||
coords: dataset.data,
|
||||
color: dataset.style.fg.unwrap_or(Color::Reset),
|
||||
});
|
||||
if dataset.graph_type == GraphType::Line {
|
||||
for data in dataset.data.windows(2) {
|
||||
ctx.draw(&CanvasLine {
|
||||
x1: data[0].0,
|
||||
y1: data[0].1,
|
||||
x2: data[1].0,
|
||||
y2: data[1].1,
|
||||
color: dataset.style.fg.unwrap_or(Color::Reset),
|
||||
});
|
||||
match dataset.graph_type {
|
||||
GraphType::Line => {
|
||||
for data in dataset.data.windows(2) {
|
||||
ctx.draw(&CanvasLine {
|
||||
x1: data[0].0,
|
||||
y1: data[0].1,
|
||||
x2: data[1].0,
|
||||
y2: data[1].1,
|
||||
color: dataset.style.fg.unwrap_or(Color::Reset),
|
||||
});
|
||||
}
|
||||
}
|
||||
GraphType::Bar => {
|
||||
for (x, y) in dataset.data {
|
||||
ctx.draw(&CanvasLine {
|
||||
x1: *x,
|
||||
y1: 0.0,
|
||||
x2: *x,
|
||||
y2: *y,
|
||||
color: dataset.style.fg.unwrap_or(Color::Reset),
|
||||
});
|
||||
}
|
||||
}
|
||||
GraphType::Scatter => {}
|
||||
}
|
||||
})
|
||||
.render(graph_area, buf);
|
||||
|
@ -1194,12 +1213,14 @@ mod tests {
|
|||
fn graph_type_to_string() {
|
||||
assert_eq!(GraphType::Scatter.to_string(), "Scatter");
|
||||
assert_eq!(GraphType::Line.to_string(), "Line");
|
||||
assert_eq!(GraphType::Bar.to_string(), "Bar");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn graph_type_from_str() {
|
||||
assert_eq!("Scatter".parse::<GraphType>(), Ok(GraphType::Scatter));
|
||||
assert_eq!("Line".parse::<GraphType>(), Ok(GraphType::Line));
|
||||
assert_eq!("Bar".parse::<GraphType>(), Ok(GraphType::Bar));
|
||||
assert_eq!("".parse::<GraphType>(), Err(ParseError::VariantNotFound));
|
||||
}
|
||||
|
||||
|
@ -1460,4 +1481,39 @@ mod tests {
|
|||
chart.render(buffer.area, &mut buffer);
|
||||
assert_eq!(buffer, Buffer::with_lines(expected));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bar_chart() {
|
||||
let data = [
|
||||
(0.0, 0.0),
|
||||
(2.0, 1.0),
|
||||
(4.0, 4.0),
|
||||
(6.0, 8.0),
|
||||
(8.0, 9.0),
|
||||
(10.0, 10.0),
|
||||
];
|
||||
let chart = Chart::new(vec![Dataset::default()
|
||||
.data(&data)
|
||||
.marker(symbols::Marker::Dot)
|
||||
.graph_type(GraphType::Bar)])
|
||||
.x_axis(Axis::default().bounds([0.0, 10.0]))
|
||||
.y_axis(Axis::default().bounds([0.0, 10.0]));
|
||||
let area = Rect::new(0, 0, 11, 11);
|
||||
let mut buffer = Buffer::empty(area);
|
||||
chart.render(buffer.area, &mut buffer);
|
||||
let expected = Buffer::with_lines([
|
||||
" •",
|
||||
" • •",
|
||||
" • • •",
|
||||
" • • •",
|
||||
" • • •",
|
||||
" • • •",
|
||||
" • • • •",
|
||||
" • • • •",
|
||||
" • • • •",
|
||||
" • • • • •",
|
||||
"• • • • • •",
|
||||
]);
|
||||
assert_eq!(buffer, expected);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue