use crate::alignment::Alignment;
use crate::color::ColorMode;
use crate::lines::Lines;
#[derive(Clone, Debug)]
pub struct Interval {
start: i32, end: i32,
plot_start: usize,
plot_end: usize,
label: String,
}
impl Interval {
pub fn new(start: i32, end: i32, label: String) -> Self {
Interval {
start, end, label,
plot_start: 0,
plot_end: 0,
}
}
pub fn is_valid(&self) -> bool {
self.end >= self.start
}
pub fn label_to_v16(&self) -> Vec<u16> {
self.label.encode_utf16().map(
|c| c.max(' ' as u16) ).collect()
}
pub fn adjust_coordinate(&mut self, graph_width: usize, data_size: usize) {
let start = self.start.max(0).min(data_size as i32 * 2) as usize;
let end = self.end.max(0).min(data_size as i32 * 2) as usize;
self.plot_start = start * graph_width / data_size;
self.plot_end = end * graph_width / data_size;
}
pub fn render_full(&self) -> Vec<u16> {
let label = self.label_to_v16();
let len = self.plot_end - self.plot_start + 1;
if len >= label.len() + 4 {
let rem = len - label.len() - 2;
let left = rem / 2;
let right = rem / 2 + rem % 2;
vec![
vec!['<' as u16],
vec!['─' as u16; left],
label,
vec!['─' as u16; right],
vec!['>' as u16],
].concat()
}
else if label.len() > 8 && len > 7 {
vec![
vec!['<' as u16, '─' as u16],
label[0..(len - 7)].to_vec(),
vec!['.' as u16; 3],
vec!['─' as u16, '>' as u16],
].concat()
}
else if len > 1 {
vec![
vec!['<' as u16],
vec!['─' as u16; len - 2],
vec!['>' as u16],
].concat()
}
else {
vec![]
}
}
pub fn render_half(&self, left_side: bool, graph_width: usize) -> Vec<u16> {
let mut label = self.label_to_v16();
let len = if left_side {
self.plot_end + 1
} else {
graph_width - self.plot_start
};
if len < label.len() + 2 {
if len > 5 {
label = vec![
label[..(len - 5)].to_vec(),
vec!['.' as u16; 3],
].concat()
}
else {
label = vec![];
}
}
let label_len = label.len();
vec![
if left_side {
vec![]
} else {
vec![
vec!['<' as u16],
vec!['─' as u16; len - label_len - 1],
].concat()
},
label,
if left_side {
vec![
vec!['─' as u16; len - label_len - 1],
vec!['>' as u16],
].concat()
} else {
vec![]
},
].concat()
}
}
pub fn draw_labeled_intervals(intervals: &Vec<Interval>, graph_width: usize) -> Lines {
let mut masks = vec![vec![false; graph_width]];
let mut rows = vec![vec![]];
'outer: for interval in intervals.iter() {
if interval.end < 0 || interval.plot_start >= graph_width {
continue;
}
for (index, mask) in masks.iter_mut().enumerate() {
if can_push(mask, interval) {
push(mask, interval);
rows[index].push(interval);
continue 'outer;
}
}
let mut new_mask = vec![false; graph_width];
let new_row = vec![interval];
push(&mut new_mask, interval);
masks.push(new_mask);
rows.push(new_row);
}
let mut result = Lines::new(graph_width, rows.len());
for (index, row) in rows.iter().enumerate() {
for interval in row.iter() {
if interval.start < 0 {
let i = interval.render_half(true, graph_width);
let l = Lines::from_string(&String::from_utf16_lossy(&i), Alignment::First, &ColorMode::None);
result = result.blit(&l, 0, index, None);
}
else if interval.plot_end >= graph_width {
let i = interval.render_half(false, graph_width);
let l = Lines::from_string(&String::from_utf16_lossy(&i), Alignment::First, &ColorMode::None);
result = result.blit(&l, interval.plot_start, index, None);
}
else {
let i = interval.render_full();
let l = Lines::from_string(&String::from_utf16_lossy(&i), Alignment::First, &ColorMode::None);
result = result.blit(&l, interval.plot_start, index, None);
}
}
}
result
}
fn can_push(mask: &Vec<bool>, interval: &Interval) -> bool {
let start = interval.plot_start;
let end = interval.plot_end.min(mask.len() - 1);
mask[start..(end + 1)].iter().all(|c| !c)
}
fn push(mask: &mut Vec<bool>, interval: &Interval) {
let start = interval.plot_start;
let end = interval.plot_end.min(mask.len() - 1);
for i in start..(end + 1) {
mask[i] = true;
}
}