use graph;
use utils::{vec2_add, vec2_sub};
use widget;
use widget::triangles::Triangle;
use {Color, Colorable, Point, Positionable, Scalar, Sizeable, Theme, Widget};
pub use super::line::Pattern;
pub use super::line::Style;
#[derive(Clone, Debug, WidgetCommon_)]
pub struct PointPath<I> {
pub points: I,
#[conrod(common_builder)]
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>,
}
#[derive(Clone)]
pub struct Triangles<I> {
next: Option<Triangle<Point>>,
prev: Point,
points: I,
half_thickness: Scalar,
cap: widget::line::Cap,
}
impl<I> PointPath<I> {
pub fn styled(points: I, style: Style) -> Self {
PointPath {
points: points,
common: widget::CommonBuilder::default(),
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) = super::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) = super::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 init_state(&self, _: widget::id::Generator) -> Self::State {
State { 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, .. } = 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
}
}
pub fn triangles<I>(
points: I,
cap: widget::line::Cap,
thickness: Scalar,
) -> Option<Triangles<I::IntoIter>>
where
I: IntoIterator<Item = Point>,
{
let mut points = points.into_iter();
let first = match points.next() {
Some(point) => point,
None => return None,
};
Some(Triangles {
next: None,
prev: first,
points: points,
half_thickness: thickness / 2.0,
cap: cap,
})
}
impl<I> Iterator for Triangles<I>
where
I: Iterator<Item = Point>,
{
type Item = Triangle<Point>;
fn next(&mut self) -> Option<Self::Item> {
if let Some(triangle) = self.next.take() {
return Some(triangle);
}
self.points.next().map(|point| {
let (a, b) = (self.prev, point);
self.prev = point;
let tris = widget::line::triangles(a, b, self.half_thickness);
self.next = Some(tris[1]);
tris[0]
})
}
}
pub fn is_over<I>(points: I, cap: widget::line::Cap, thickness: Scalar, p: Point) -> bool
where
I: IntoIterator<Item = Point>,
{
triangles(points, cap, thickness)
.map(|ts| widget::triangles::is_over(ts, p))
.unwrap_or(false)
}
pub fn is_over_widget(widget: &graph::Container, point: Point, theme: &Theme) -> widget::IsOver {
widget
.state_and_style::<State, Style>()
.map(|widget| {
let cap = widget.style.get_cap(theme);
let thickness = widget.style.get_thickness(theme);
is_over(widget.state.points.iter().cloned(), cap, thickness, point)
})
.unwrap_or_else(|| widget.rect.is_over(point))
.into()
}