conrod_core/widget/
plot_path.rs

1//! A widget for plotting a series of lines using the given function *x -> y*.
2
3use graph;
4use num;
5use utils;
6use widget;
7use {Color, Colorable, Point, Positionable, Scalar, Sizeable, Theme, Widget};
8
9/// A widget that plots a series of lines using the given function *x -> y*.
10///
11/// The function is sampled once per pixel and the result is mapped to the widget's height.
12///
13/// The resulting "path" is drawn using conrod's `PointPath` primitive widget.
14#[derive(WidgetCommon_)]
15pub struct PlotPath<X, Y, F> {
16    #[conrod(common_builder)]
17    common: widget::CommonBuilder,
18    style: Style,
19    min_x: X,
20    max_x: X,
21    min_y: Y,
22    max_y: Y,
23    f: F,
24}
25
26/// Unique styling parameters for the `PlotPath` widget.
27#[derive(Copy, Clone, Debug, Default, PartialEq, WidgetStyle_)]
28pub struct Style {
29    /// The thickness of the plotted line.
30    #[conrod(default = "1.0")]
31    pub thickness: Option<Scalar>,
32    /// The color of the line.
33    #[conrod(default = "theme.shape_color")]
34    pub color: Option<Color>,
35}
36
37widget_ids! {
38    struct Ids {
39        point_path,
40    }
41}
42
43/// Unique state stored between updates for the `PlotPath` widget.
44pub struct State {
45    ids: Ids,
46}
47
48impl<X, Y, F> PlotPath<X, Y, F> {
49    /// Begin building a new `PlotPath` widget instance.
50    pub fn new(min_x: X, max_x: X, min_y: Y, max_y: Y, f: F) -> Self {
51        PlotPath {
52            common: widget::CommonBuilder::default(),
53            style: Style::default(),
54            min_x: min_x,
55            max_x: max_x,
56            min_y: min_y,
57            max_y: max_y,
58            f: f,
59        }
60    }
61
62    /// The thickness of the point path used to draw the plot.
63    pub fn thickness(mut self, thickness: Scalar) -> Self {
64        self.style.thickness = Some(thickness);
65        self
66    }
67}
68
69impl<X, Y, F> Widget for PlotPath<X, Y, F>
70where
71    X: num::NumCast + Clone,
72    Y: num::NumCast + Clone,
73    F: FnMut(X) -> Y,
74{
75    type State = State;
76    type Style = Style;
77    type Event = ();
78
79    fn init_state(&self, id_gen: widget::id::Generator) -> Self::State {
80        State {
81            ids: Ids::new(id_gen),
82        }
83    }
84
85    fn style(&self) -> Self::Style {
86        self.style.clone()
87    }
88
89    fn is_over(&self) -> widget::IsOverFn {
90        fn is_over_widget(widget: &graph::Container, _: Point, _: &Theme) -> widget::IsOver {
91            let unique = widget.state_and_style::<State, Style>().unwrap();
92            unique.state.ids.point_path.into()
93        }
94        is_over_widget
95    }
96
97    /// Update the state of the PlotPath.
98    fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
99        let widget::UpdateArgs {
100            id,
101            state,
102            style,
103            rect,
104            ui,
105            ..
106        } = args;
107        let PlotPath {
108            min_x,
109            max_x,
110            min_y,
111            max_y,
112            mut f,
113            ..
114        } = self;
115
116        let y_to_scalar =
117            |y| utils::map_range(y, min_y.clone(), max_y.clone(), rect.bottom(), rect.top());
118        let scalar_to_x =
119            |s| utils::map_range(s, rect.left(), rect.right(), min_x.clone(), max_x.clone());
120
121        let point_iter = (0..rect.w() as usize).map(|x_scalar| {
122            let x_scalar = x_scalar as Scalar + rect.x.start;
123            let x = scalar_to_x(x_scalar);
124            let y = f(x);
125            let y_scalar = y_to_scalar(y);
126            [x_scalar, y_scalar]
127        });
128
129        let thickness = style.thickness(ui.theme());
130        let color = style.color(ui.theme());
131        widget::PointPath::new(point_iter)
132            .wh(rect.dim())
133            .xy(rect.xy())
134            .color(color)
135            .thickness(thickness)
136            .parent(id)
137            .graphics_for(id)
138            .set(state.ids.point_path, ui);
139    }
140}
141
142impl<X, Y, F> Colorable for PlotPath<X, Y, F> {
143    builder_method!(color { style.color = Some(Color) });
144}