Improve canvas functionalities and fix clippy warnings

This commit is contained in:
Florian Dehau 2016-10-28 19:30:30 +02:00
parent 25bb360f13
commit f0979dfeee
7 changed files with 166 additions and 47 deletions

View File

@ -98,7 +98,7 @@ impl Terminal {
bg = cell.bg;
inst += 1;
}
string.push_str(&format!("{}", cell.symbol));
string.push_str(&cell.symbol);
inst += 1;
}
string.push_str(&format!("{}{}", Color::Reset.fg(), Color::Reset.bg()));

View File

@ -0,0 +1,65 @@
use super::Shape;
use style::Color;
pub struct Line {
pub x1: f64,
pub y1: f64,
pub x2: f64,
pub y2: f64,
pub color: Color,
}
pub struct LineIterator {
x: f64,
y: f64,
dx: f64,
dy: f64,
dir_x: f64,
dir_y: f64,
current: f64,
end: f64,
}
impl Iterator for LineIterator {
type Item = (f64, f64);
fn next(&mut self) -> Option<Self::Item> {
self.current += 1.0;
if self.current < self.end + 1.0 {
Some((self.x + (self.current * self.dx) / self.end * self.dir_x,
self.y + (self.current * self.dy) / self.end * self.dir_y))
} else {
None
}
}
}
impl<'a> IntoIterator for &'a Line {
type Item = (f64, f64);
type IntoIter = LineIterator;
fn into_iter(self) -> Self::IntoIter {
let dx = self.x1.max(self.x2) - self.x1.min(self.x2);
let dy = self.y1.max(self.y2) - self.y1.min(self.y2);
let dir_x = if self.x1 <= self.x2 { 1.0 } else { -1.0 };
let dir_y = if self.y1 <= self.y2 { 1.0 } else { -1.0 };
let end = dx.max(dy);
LineIterator {
x: self.x1,
y: self.y1,
dx: dx,
dy: dy,
dir_x: dir_x,
dir_y: dir_y,
current: 0.0,
end: end,
}
}
}
impl<'a> Shape<'a> for Line {
fn color(&self) -> Color {
self.color
}
fn points(&'a self) -> Box<Iterator<Item = (f64, f64)> + 'a> {
Box::new(self.into_iter())
}
}

View File

@ -1,3 +1,9 @@
mod points;
mod line;
pub use self::points::Points;
pub use self::line::Line;
use style::Color;
use buffer::Buffer;
use widgets::{Block, Widget};
@ -8,37 +14,16 @@ pub const DOTS: [[u16; 2]; 4] =
pub const BRAILLE_OFFSET: u16 = 0x2800;
pub const BRAILLE_BLANK: char = '';
pub struct Shape<'a> {
data: &'a [(f64, f64)],
color: Color,
}
impl<'a> Default for Shape<'a> {
fn default() -> Shape<'a> {
Shape {
data: &[],
color: Color::Reset,
}
}
}
impl<'a> Shape<'a> {
pub fn data(mut self, data: &'a [(f64, f64)]) -> Shape<'a> {
self.data = data;
self
}
pub fn color(mut self, color: Color) -> Shape<'a> {
self.color = color;
self
}
pub trait Shape<'a> {
fn color(&self) -> Color;
fn points(&'a self) -> Box<Iterator<Item = (f64, f64)> + 'a>;
}
pub struct Canvas<'a> {
block: Option<Block<'a>>,
x_bounds: [f64; 2],
y_bounds: [f64; 2],
shapes: &'a [Shape<'a>],
shapes: &'a [&'a Shape<'a>],
}
impl<'a> Default for Canvas<'a> {
@ -65,7 +50,7 @@ impl<'a> Canvas<'a> {
self.y_bounds = bounds;
self
}
pub fn shapes(&mut self, shapes: &'a [Shape<'a>]) -> &mut Canvas<'a> {
pub fn shapes(&mut self, shapes: &'a [&'a Shape<'a>]) -> &mut Canvas<'a> {
self.shapes = shapes;
self
}
@ -80,34 +65,42 @@ impl<'a> Widget for Canvas<'a> {
}
None => *area,
};
let width = canvas_area.width as usize;
let height = canvas_area.height as usize;
let mut grid: Vec<u16> = vec![BRAILLE_OFFSET; width * height + 1];
let mut x_bounds = self.x_bounds.clone();
let mut colors: Vec<Color> = vec![Color::Reset; width * height + 1];
let mut x_bounds = self.x_bounds;
x_bounds.sort_by(|a, b| a.partial_cmp(b).unwrap());
let mut y_bounds = self.y_bounds.clone();
let mut y_bounds = self.y_bounds;
y_bounds.sort_by(|a, b| a.partial_cmp(b).unwrap());
for shape in self.shapes {
for &(x, y) in shape.data.iter().filter(|&&(x, y)| {
for (x, y) in shape.points().filter(|&(x, y)| {
!(x < x_bounds[0] || x > x_bounds[1] || y < y_bounds[0] || y > y_bounds[1])
}) {
let dy = ((self.y_bounds[1] - y) * canvas_area.height as f64 * 4.0 /
(self.y_bounds[1] - self.y_bounds[0])) as usize;
let dx = ((self.x_bounds[1] - x) * canvas_area.width as f64 * 2.0 /
(self.x_bounds[1] - self.x_bounds[0])) as usize;
grid[dy / 4 * width + dx / 2] |= DOTS[dy % 4][dx % 2];
let index = dy / 4 * width + dx / 2;
grid[index] |= DOTS[dy % 4][dx % 2];
colors[index] = shape.color();
}
let string = String::from_utf16(&grid).unwrap();
for (i, ch) in string.chars().enumerate() {
if ch != BRAILLE_BLANK {
let (x, y) = (i % width, i / width);
buf.update_cell(x as u16 + canvas_area.left(), y as u16 + area.top(), |c| {
c.symbol.clear();
c.symbol.push(ch);
c.fg = shape.color;
c.bg = Color::Reset;
});
}
}
let string = String::from_utf16(&grid).unwrap();
for (i, (ch, color)) in string.chars().zip(colors.into_iter()).enumerate() {
if ch != BRAILLE_BLANK {
let (x, y) = (i % width, i / width);
buf.update_cell(x as u16 + canvas_area.left(), y as u16 + area.top(), |c| {
c.symbol.clear();
c.symbol.push(ch);
c.fg = color;
c.bg = Color::Reset;
});
}
}
}

View File

@ -0,0 +1,54 @@
use super::Shape;
use style::Color;
pub struct Points<'a> {
pub coords: &'a [(f64, f64)],
pub color: Color,
}
impl<'a> Shape<'a> for Points<'a> {
fn color(&self) -> Color {
self.color
}
fn points(&'a self) -> Box<Iterator<Item = (f64, f64)> + 'a> {
Box::new(self.into_iter())
}
}
impl<'a> Default for Points<'a> {
fn default() -> Points<'a> {
Points {
coords: &[],
color: Color::Reset,
}
}
}
impl<'a> IntoIterator for &'a Points<'a> {
type Item = (f64, f64);
type IntoIter = PointsIterator<'a>;
fn into_iter(self) -> Self::IntoIter {
PointsIterator {
coords: self.coords,
index: 0,
}
}
}
pub struct PointsIterator<'a> {
coords: &'a [(f64, f64)],
index: usize,
}
impl<'a> Iterator for PointsIterator<'a> {
type Item = (f64, f64);
fn next(&mut self) -> Option<Self::Item> {
let point = if self.index < self.coords.len() {
Some(self.coords[self.index])
} else {
None
};
self.index += 1;
point
}
}

View File

@ -2,7 +2,7 @@ use std::cmp::max;
use unicode_width::UnicodeWidthStr;
use widgets::{Widget, Block, Canvas, Shape};
use widgets::{Widget, Block, Canvas, Points};
use buffer::Buffer;
use layout::Rect;
use style::Color;
@ -325,7 +325,10 @@ impl<'a> Widget for Chart<'a> {
Canvas::default()
.x_bounds(self.x_axis.bounds)
.y_bounds(self.y_axis.bounds)
.shapes(&[Shape::default().data(dataset.data).color(dataset.color)])
.shapes(&[&Points {
coords: dataset.data,
color: dataset.color,
}])
.buffer(&graph_area, buf);
}
}

View File

@ -1,6 +1,7 @@
use widgets::{Widget, Block, Canvas, Shape};
use widgets::{Widget, Block, Canvas, Points};
use buffer::Buffer;
use layout::Rect;
use style::Color;
pub struct Map<'a> {
block: Option<Block<'a>>,
@ -32,7 +33,10 @@ impl<'a> Widget for Map<'a> {
Canvas::default()
.x_bounds([180.0, -180.0])
.y_bounds([-90.0, 90.0])
.shapes(&[Shape::default().data(&WORLD)])
.shapes(&[&Points {
coords: &WORLD,
color: Color::Reset,
}])
.buffer(&map_area, buf);
}
}

View File

@ -17,7 +17,7 @@ pub use self::sparkline::Sparkline;
pub use self::chart::{Chart, Axis, Dataset, Marker};
pub use self::barchart::BarChart;
pub use self::tabs::Tabs;
pub use self::canvas::{Canvas, Shape};
pub use self::canvas::{Canvas, Shape, Line, Points};
pub use self::map::Map;
use buffer::Buffer;