use {Color, Colorable, Point, Positionable, Sizeable, Theme, Widget};
use graph;
use super::Style;
use widget;
use widget::triangles::Triangle;
use utils::{bounding_box_for_points, vec2_add, vec2_sub};
#[derive(Copy, Clone, Debug, WidgetCommon_)]
pub struct Polygon<I> {
#[conrod(common_builder)]
pub common: widget::CommonBuilder,
pub points: I,
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,
}
#[derive(Clone)]
pub struct Triangles<I> {
first: Point,
prev: Point,
points: I,
}
impl<I> Polygon<I> {
pub fn styled(points: I, style: Style) -> Self {
Polygon {
points: points,
common: widget::CommonBuilder::default(),
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 init_state(&self, _: widget::id::Generator) -> Self::State {
State {
kind: Kind::Fill,
points: Vec::new(),
}
}
fn style(&self) -> Self::Style {
self.style.clone()
}
fn is_over(&self) -> widget::IsOverFn {
is_over_widget
}
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
}
}
pub fn triangles<I>(points: I) -> Option<Triangles<I::IntoIter>>
where I: IntoIterator<Item=Point>,
{
let mut points = points.into_iter();
let first = match points.next() {
Some(p) => p,
None => return None,
};
let prev = match points.next() {
Some(p) => p,
None => return None,
};
Some(Triangles {
first: first,
prev: prev,
points: points,
})
}
impl<I> Iterator for Triangles<I>
where I: Iterator<Item=Point>,
{
type Item = Triangle<Point>;
fn next(&mut self) -> Option<Self::Item> {
self.points.next().map(|point| {
let t = Triangle([self.first, self.prev, point]);
self.prev = point;
t
})
}
}
pub fn is_over<I>(points: I, point: Point) -> bool
where
I: IntoIterator<Item=Point>,
{
triangles(points).map(|ts| widget::triangles::is_over(ts, point)).unwrap_or(false)
}
pub fn is_over_widget(widget: &graph::Container, point: Point, _: &Theme) -> widget::IsOver {
widget
.state_and_style::<State, Style>()
.map(|widget| is_over(widget.state.points.iter().cloned(), point))
.unwrap_or_else(|| widget.rect.is_over(point))
.into()
}