Allow customizing x and y axis bounds per dataset

pull/423/head
Tom Forbes 4 years ago
parent 23a9280db7
commit 637fde0fa5
No known key found for this signature in database
GPG Key ID: AEC0662FF91ADD01

@ -2,6 +2,10 @@
## To be released
### Features
* Add `Dataset.x_axis_bounds` and `Dataset.y_axis_bounds` to allow setting per-dataset axis bounds.
## v0.13.0 - 2020-11-14
### Features

@ -34,34 +34,69 @@ struct App {
signal2: SinSignal,
data2: Vec<(f64, f64)>,
window: [f64; 2],
tick: usize,
signal3: SinSignal,
data3: Vec<(f64, f64)>,
signal4: SinSignal,
data4: Vec<(f64, f64)>,
window4: [f64; 2],
}
fn update_data(data: &mut Vec<(f64, f64)>, signal: &mut SinSignal, amount: usize) {
for _ in 0..amount {
data.remove(0);
}
data.extend(signal.by_ref().take(amount));
}
impl App {
fn new() -> App {
let mut signal1 = SinSignal::new(0.2, 3.0, 18.0);
let mut signal2 = SinSignal::new(0.1, 2.0, 10.0);
let mut signal3 = SinSignal::new(0.2, 3.0, 18.0);
let mut signal4 = SinSignal::new(0.1, 2.0, 10.0);
let data1 = signal1.by_ref().take(200).collect::<Vec<(f64, f64)>>();
let data2 = signal2.by_ref().take(200).collect::<Vec<(f64, f64)>>();
let data3 = signal3.by_ref().take(200).collect::<Vec<(f64, f64)>>();
let data4 = signal4.by_ref().take(200).collect::<Vec<(f64, f64)>>();
App {
signal1,
data1,
signal2,
data2,
window: [0.0, 20.0],
tick: 0,
signal3,
data3,
signal4,
data4,
window4: [0.0, 20.0],
}
}
fn update(&mut self) {
for _ in 0..5 {
self.data1.remove(0);
}
self.data1.extend(self.signal1.by_ref().take(5));
for _ in 0..10 {
self.data2.remove(0);
}
self.data2.extend(self.signal2.by_ref().take(10));
self.tick += 1;
update_data(&mut self.data1, &mut self.signal1, 5);
update_data(&mut self.data2, &mut self.signal2, 10);
update_data(&mut self.data3, &mut self.signal3, 5);
self.window[0] += 1.0;
self.window[1] += 1.0;
if self.tick == 5 {
self.tick = 0;
self.window4[0] += 1.0;
self.window4[1] += 1.0;
update_data(&mut self.data4, &mut self.signal4, 10);
}
}
}
@ -92,6 +127,12 @@ fn main() -> Result<(), Box<dyn Error>> {
.as_ref(),
)
.split(size);
let bottom_chunks = Layout::default()
.direction(Direction::Horizontal)
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
.split(chunks[2]);
let x_labels = vec![
Span::styled(
format!("{}", app.window[0]),
@ -227,7 +268,45 @@ fn main() -> Result<(), Box<dyn Error>> {
Span::styled("5", Style::default().add_modifier(Modifier::BOLD)),
]),
);
f.render_widget(chart, chunks[2]);
f.render_widget(chart, bottom_chunks[0]);
let datasets = vec![
Dataset::default()
.name("data3")
.marker(symbols::Marker::Braille)
.style(Style::default().fg(Color::Yellow))
.data(&app.data3).x_axis_bounds(app.window),
Dataset::default()
.name("data3")
.marker(symbols::Marker::Braille)
.style(Style::default().fg(Color::Yellow))
.data(&app.data4).x_axis_bounds(app.window4),
];
let chart = Chart::new(datasets)
.block(
Block::default()
.title(Span::styled(
"Chart 4",
Style::default()
.fg(Color::Cyan)
.add_modifier(Modifier::BOLD),
))
.borders(Borders::ALL),
)
.y_axis(
Axis::default()
.title("Y Axis")
.style(Style::default().fg(Color::Gray))
.labels(vec![
Span::styled("-20", Style::default().add_modifier(Modifier::BOLD)),
Span::raw("0"),
Span::styled("20", Style::default().add_modifier(Modifier::BOLD)),
])
.bounds([-20.0, 20.0]),
);
f.render_widget(chart, bottom_chunks[1]);
})?;
match events.next()? {

@ -95,6 +95,9 @@ pub struct Dataset<'a> {
graph_type: GraphType,
/// Style used to plot this dataset
style: Style,
///
x_axis_bounds: Option<[f64; 2]>,
y_axis_bounds: Option<[f64; 2]>,
}
impl<'a> Default for Dataset<'a> {
@ -105,6 +108,8 @@ impl<'a> Default for Dataset<'a> {
marker: symbols::Marker::Dot,
graph_type: GraphType::Scatter,
style: Style::default(),
x_axis_bounds: None,
y_axis_bounds: None
}
}
}
@ -137,6 +142,16 @@ impl<'a> Dataset<'a> {
self.style = style;
self
}
pub fn x_axis_bounds(mut self, bounds: [f64; 2]) -> Dataset<'a> {
self.x_axis_bounds = Some(bounds);
self
}
pub fn y_axis_bounds(mut self, bounds: [f64; 2]) -> Dataset<'a> {
self.y_axis_bounds = Some(bounds);
self
}
}
/// A container that holds all the infos about where to display each elements of the chart (axis,
@ -442,8 +457,8 @@ impl<'a> Widget for Chart<'a> {
for dataset in &self.datasets {
Canvas::default()
.background_color(self.style.bg.unwrap_or(Color::Reset))
.x_bounds(self.x_axis.bounds)
.y_bounds(self.y_axis.bounds)
.x_bounds(dataset.x_axis_bounds.unwrap_or(self.x_axis.bounds))
.y_bounds(dataset.y_axis_bounds.unwrap_or(self.y_axis.bounds))
.marker(dataset.marker)
.paint(|ctx| {
ctx.draw(&Points {

@ -126,6 +126,46 @@ fn widgets_chart_can_have_empty_datasets() {
.unwrap();
}
#[test]
fn widgets_chart_disjoint_axis() {
let backend = TestBackend::new(100, 100);
let mut terminal = Terminal::new(backend).unwrap();
let dataset_1 = Dataset::default().data(&[(0.0, 0.0), (1.0, 1.0), (2.0, 2.0)]).x_axis_bounds([1.0, 2.0]).graph_type(Line);
let dataset_2 = Dataset::default().data(&[(0.0, 0.0), (1.0, 1.0)]).x_axis_bounds([0.0, 1.0]).graph_type(Line);
terminal
.draw(|f| {
let datasets = vec![dataset_1, dataset_2];
let chart = Chart::new(datasets)
.block(
Block::default()
.title("Empty Dataset With Line")
.borders(Borders::ALL),
)
.x_axis(
Axis::default()
.bounds([0.0, 2.0])
.labels(create_labels(&["0.0", "3.0"])),
)
.y_axis(
Axis::default()
.bounds([0.0, 3.0])
.labels(create_labels(&["0.0", "3.0"])),
);
f.render_widget(
chart,
Rect {
x: 0,
y: 0,
width: 100,
height: 100,
},
);
})
.unwrap();
}
#[test]
fn widgets_chart_can_have_a_legend() {
let backend = TestBackend::new(60, 30);

Loading…
Cancel
Save