use {
Color,
Colorable,
Point,
Positionable,
Range,
Rect,
Scalar,
Sizeable,
Widget,
};
use utils::{vec2_add, vec2_sub};
use widget;
pub use super::line::Pattern;
pub use super::line::Style;
#[derive(Clone, Debug)]
pub struct PointPath<I> {
pub points: I,
pub common: widget::CommonBuilder,
pub style: Style,
pub maybe_shift_to_centre_from: Option<Point>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct State {
pub points: Vec<Point>,
}
fn bounding_box_for_points<I>(mut points: I) -> Rect
where I: Iterator<Item=Point>,
{
points.next().map(|first| {
let start_rect = Rect {
x: Range { start: first[0], end: first[0] },
y: Range { start: first[1], end: first[1] },
};
points.fold(start_rect, Rect::stretch_to_point)
}).unwrap_or_else(|| Rect::from_xy_dim([0.0, 0.0], [0.0, 0.0]))
}
impl<I> PointPath<I> {
pub fn styled(points: I, style: Style) -> Self {
PointPath {
points: points,
common: widget::CommonBuilder::new(),
style: style,
maybe_shift_to_centre_from: None,
}
}
pub fn new(points: I) -> Self {
PointPath::styled(points, Style::new())
}
pub fn abs(points: I) -> Self
where I: IntoIterator<Item=Point> + Clone,
{
PointPath::abs_styled(points, Style::new())
}
pub fn abs_styled(points: I, style: Style) -> Self
where I: IntoIterator<Item=Point> + Clone,
{
let points_clone = points.clone().into_iter();
let (xy, dim) = bounding_box_for_points(points_clone).xy_dim();
PointPath::styled(points, style).wh(dim).xy(xy)
}
pub fn centred(points: I) -> Self
where I: IntoIterator<Item=Point> + Clone,
{
PointPath::centred_styled(points, Style::new())
}
pub fn centred_styled(points: I, style: Style) -> Self
where I: IntoIterator<Item=Point> + Clone,
{
let points_clone = points.clone().into_iter();
let (xy, dim) = bounding_box_for_points(points_clone).xy_dim();
let mut point_path = PointPath::styled(points, style).wh(dim);
point_path.maybe_shift_to_centre_from = Some(xy);
point_path
}
pub fn thickness(mut self, thickness: Scalar) -> Self {
self.style.set_thickness(thickness);
self
}
pub fn solid(mut self) -> Self {
self.style.set_pattern(Pattern::Solid);
self
}
pub fn dashed(mut self) -> Self {
self.style.set_pattern(Pattern::Dashed);
self
}
pub fn dotted(mut self) -> Self {
self.style.set_pattern(Pattern::Dotted);
self
}
}
impl<I> Widget for PointPath<I>
where I: IntoIterator<Item=Point>,
{
type State = State;
type Style = Style;
type Event = ();
fn common(&self) -> &widget::CommonBuilder {
&self.common
}
fn common_mut(&mut self) -> &mut widget::CommonBuilder {
&mut self.common
}
fn init_state(&self, _: widget::id::Generator) -> Self::State {
State {
points: Vec::new(),
}
}
fn style(&self) -> Self::Style {
self.style.clone()
}
fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
use utils::{iter_diff, IterDiff};
let widget::UpdateArgs { rect, state, .. } = args;
let PointPath { points, maybe_shift_to_centre_from, .. } = self;
fn update_points<I>(state: &mut widget::State<State>, points: I)
where I: IntoIterator<Item=Point>,
{
match iter_diff(&state.points, points) {
Some(IterDiff::FirstMismatch(i, mismatch)) => state.update(|state| {
state.points.truncate(i);
state.points.extend(mismatch);
}),
Some(IterDiff::Longer(remaining)) =>
state.update(|state| state.points.extend(remaining)),
Some(IterDiff::Shorter(total)) =>
state.update(|state| state.points.truncate(total)),
None => (),
}
}
match maybe_shift_to_centre_from {
Some(original) => {
let xy = rect.xy();
let difference = vec2_sub(xy, original);
update_points(state, points.into_iter().map(|point| vec2_add(point, difference)))
},
None => update_points(state, points),
}
}
}
impl<I> Colorable for PointPath<I> {
fn color(mut self, color: Color) -> Self {
self.style.set_color(color);
self
}
}