Skip to content

Commit

Permalink
refactor(barchart): replace remove_invisible_groups_and_bars by group…
Browse files Browse the repository at this point in the history
…_ticks

group_ticks calculates the visible bar length in ticks. (A cell contains 8 ticks).

It is used for 2 purposes:
1. to get the bar length in ticks for rendering
2. since it delivers only the values of the visible bars, If we zip these values
   with the groups and bars, then we will filter out the invisible groups and bars

Signed-off-by: Ben Fekih, Hichem <hichem.f@live.de>
  • Loading branch information
karthago1 committed Sep 28, 2023
1 parent 11076d0 commit 2d9e609
Showing 1 changed file with 54 additions and 69 deletions.
123 changes: 54 additions & 69 deletions src/widgets/barchart.rs
Expand Up @@ -290,26 +290,43 @@ struct LabelInfo {
}

impl<'a> BarChart<'a> {
/// Check the bars, which fits inside the available space and removes
/// the bars and the groups, which are outside of the available space.
fn remove_invisible_groups_and_bars(&mut self, mut width: u16) {
for group_index in 0..self.data.len() {
let n_bars = self.data[group_index].bars.len() as u16;
let group_width = n_bars * self.bar_width + n_bars.saturating_sub(1) * self.bar_gap;

if width > group_width {
width = width.saturating_sub(group_width + self.group_gap + self.bar_gap);
} else {
let max_bars = (width + self.bar_gap) / (self.bar_width + self.bar_gap);
if max_bars == 0 {
self.data.truncate(group_index);
} else {
self.data[group_index].bars.truncate(max_bars as usize);
self.data.truncate(group_index + 1);
/// Returns the visible bars length in ticks. A cell contains 8 ticks.
/// `available_space` used to calculate how many bars can fit in the space
/// `bar_max_length` is the maximal length a bar can take.
fn group_ticks(&self, available_space: u16, bar_max_length: u16) -> Vec<Vec<u64>> {
let max: u64 = self.maximum_data_value();
self.data
.iter()
.scan(available_space, |space, group| {
if *space == 0 {
return None;
}
break;
}
}
let n_bars = group.bars.len() as u16;
let group_width = n_bars * self.bar_width + n_bars.saturating_sub(1) * self.bar_gap;

let n_bars = if *space > group_width {
*space = space.saturating_sub(group_width + self.group_gap + self.bar_gap);
Some(n_bars)
} else {
let max_bars = (*space + self.bar_gap) / (self.bar_width + self.bar_gap);
if max_bars > 0 {
*space = 0;
Some(max_bars)
} else {
None

Check warning on line 316 in src/widgets/barchart.rs

View check run for this annotation

Codecov / codecov/patch

src/widgets/barchart.rs#L316

Added line #L316 was not covered by tests
}
};

n_bars.map(|n| {
group
.bars
.iter()
.take(n as usize)
.map(|bar| bar.value * u64::from(bar_max_length) * 8 / max)
.collect()
})
})
.collect()
}

/// Get label information.
Expand Down Expand Up @@ -360,7 +377,7 @@ impl<'a> BarChart<'a> {
}
}

fn render_horizontal_bars(self, buf: &mut Buffer, bars_area: Rect, max: u64) {
fn render_horizontal(self, buf: &mut Buffer, area: Rect) {
// get the longest label
let label_size = self
.data
Expand All @@ -371,35 +388,25 @@ impl<'a> BarChart<'a> {
.max()
.unwrap_or(0) as u16;

let label_x = bars_area.x;
let label_x = area.x;
let bars_area = {
let margin = if label_size == 0 { 0 } else { 1 };
Rect {
x: bars_area.x + label_size + margin,
width: bars_area.width - label_size - margin,
..bars_area
x: area.x + label_size + margin,
width: area.width - label_size - margin,
..area
}
};

// convert the bar values to ratatui::symbols::bar::Set
let groups: Vec<Vec<u16>> = self
.data
.iter()
.map(|group| {
group
.bars
.iter()
.map(|bar| (bar.value * u64::from(bars_area.width) / max) as u16)
.collect()
})
.collect();
let group_ticks = self.group_ticks(bars_area.height, bars_area.width);

// print all visible bars, label and values
let mut bar_y = bars_area.top();
for (group_data, mut group) in groups.into_iter().zip(self.data) {
for (ticks_vec, mut group) in group_ticks.into_iter().zip(self.data) {
let bars = std::mem::take(&mut group.bars);

for (bar_length, bar) in group_data.into_iter().zip(bars) {
for (ticks, bar) in ticks_vec.into_iter().zip(bars) {
let bar_length = (ticks >> 3) as u16;
let bar_style = self.bar_style.patch(bar.style);

for y in 0..self.bar_width {
Expand Down Expand Up @@ -451,39 +458,27 @@ impl<'a> BarChart<'a> {
}
}

fn render_vertical(self, buf: &mut Buffer, area: Rect, max: u64) {
fn render_vertical(self, buf: &mut Buffer, area: Rect) {
let label_info = self.label_info(area.height - 1);

let bars_area = Rect {
height: area.height - label_info.height,
..area
};

// convert the bar values to ratatui::symbols::bar::Set
let group_ticks: Vec<Vec<u64>> = self
.data
.iter()
.map(|group| {
group
.bars
.iter()
.map(|bar| bar.value * u64::from(bars_area.height) * 8 / max)
.collect()
})
.collect();

let group_ticks = self.group_ticks(bars_area.width, bars_area.height);
self.render_vertical_bars(bars_area, buf, &group_ticks);
self.render_labels_and_values(area, buf, label_info, &group_ticks);
}

fn render_vertical_bars(&self, area: Rect, buf: &mut Buffer, group_ticks: &[Vec<u64>]) {
// print all visible bars (without labels and values)
let mut bar_x = area.left();
for (ticks, group) in group_ticks.iter().zip(&self.data) {
for (d, bar) in ticks.iter().zip(&group.bars) {
let mut d = *d;
for (ticks_vec, group) in group_ticks.iter().zip(&self.data) {
for (ticks, bar) in ticks_vec.iter().zip(&group.bars) {
let mut ticks = *ticks;
for j in (0..area.height).rev() {
let symbol = match d {
let symbol = match ticks {
0 => self.bar_set.empty,
1 => self.bar_set.one_eighth,
2 => self.bar_set.one_quarter,
Expand All @@ -503,7 +498,7 @@ impl<'a> BarChart<'a> {
.set_style(bar_style);
}

d = d.saturating_sub(8);
ticks = ticks.saturating_sub(8);
}
bar_x += self.bar_gap + self.bar_width;
}
Expand Down Expand Up @@ -578,19 +573,9 @@ impl<'a> Widget for BarChart<'a> {
return;
}

let max = self.maximum_data_value();

match self.direction {
Direction::Horizontal => {
// remove invisible groups and bars, since we don't need to print them
self.remove_invisible_groups_and_bars(area.height);
self.render_horizontal_bars(buf, area, max);
}
Direction::Vertical => {
// remove invisible groups and bars, since we don't need to print them
self.remove_invisible_groups_and_bars(area.width);
self.render_vertical(buf, area, max);
}
Direction::Horizontal => self.render_horizontal(buf, area),
Direction::Vertical => self.render_vertical(buf, area),
}
}
}
Expand Down

0 comments on commit 2d9e609

Please sign in to comment.