use {Color, Colorable, Point, Positionable, Sizeable, Widget};
use super::Style;
use widget;
use utils::{bounding_box_for_points, vec2_add, vec2_sub};
#[derive(Copy, Clone, Debug)]
pub struct Polygon<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 {
kind: Kind,
pub points: Vec<Point>,
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum Kind {
Outline,
Fill,
}
impl<I> Polygon<I> {
pub fn styled(points: I, style: Style) -> Self {
Polygon {
points: points,
common: widget::CommonBuilder::new(),
style: style,
maybe_shift_to_centre_from: None,
}
}
pub fn fill(points: I) -> Self {
Polygon::styled(points, Style::fill())
}
pub fn fill_with(points: I, color: Color) -> Self {
Polygon::styled(points, Style::fill_with(color))
}
pub fn outline(points: I) -> Self {
Polygon::styled(points, Style::outline())
}
pub fn outline_styled(points: I, style: widget::line::Style) -> Self {
Polygon::styled(points, Style::outline_styled(style))
}
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();
Polygon::styled(points, style).wh(dim).xy(xy)
}
pub fn abs_fill(points: I) -> Self
where I: IntoIterator<Item=Point> + Clone,
{
Polygon::abs_styled(points, Style::fill())
}
pub fn abs_fill_with(points: I, color: Color) -> Self
where I: IntoIterator<Item=Point> + Clone,
{
Polygon::abs_styled(points, Style::fill_with(color))
}
pub fn abs_outline(points: I) -> Self
where I: IntoIterator<Item=Point> + Clone,
{
Polygon::abs_styled(points, Style::outline())
}
pub fn abs_outline_styled(points: I, style: widget::line::Style) -> Self
where I: IntoIterator<Item=Point> + Clone,
{
Polygon::abs_styled(points, Style::outline_styled(style))
}
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 polygon = Polygon::styled(points, style).wh(dim);
polygon.maybe_shift_to_centre_from = Some(xy);
polygon
}
pub fn centred_fill(points: I) -> Self
where I: IntoIterator<Item=Point> + Clone,
{
Polygon::centred_styled(points, Style::fill())
}
pub fn centred_fill_with(points: I, color: Color) -> Self
where I: IntoIterator<Item=Point> + Clone,
{
Polygon::centred_styled(points, Style::fill_with(color))
}
pub fn centred_outline(points: I) -> Self
where I: IntoIterator<Item=Point> + Clone,
{
Polygon::centred_styled(points, Style::outline())
}
pub fn centred_outline_styled(points: I, style: widget::line::Style) -> Self
where I: IntoIterator<Item=Point> + Clone,
{
Polygon::centred_styled(points, Style::outline_styled(style))
}
}
impl<I> Widget for Polygon<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 {
kind: Kind::Fill,
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, style, .. } = args;
let Polygon { 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),
}
let kind = match *style {
Style::Fill(_) => Kind::Fill,
Style::Outline(_) => Kind::Outline,
};
if state.kind != kind {
state.update(|state| state.kind = kind);
}
}
}
impl<I> Colorable for Polygon<I> {
fn color(mut self, color: Color) -> Self {
self.style.set_color(color);
self
}
}