use rin::events::*;
use rin::math::*;
use rin::graphics::{vertex2dcolor, Vertex2DColor, Vertex2DTex, CoordinateOrigin, Ttf};
use rin::color::consts::*;
use rin::color::*;
use rin::window::{self, Events};
use std::rc::Rc;
use std::ops::Range;
use rin::events::RangedParameter;
use control_shape::ControlShape;
use utils;
use traits::*;
pub struct Graph{
label: String,
geometry: Property<'static, Vec<Vertex2DColor>>,
text_geometry: Property<'static, Vec<Vertex2DTex>>,
font: Rc<Ttf>,
color: Rgb<f32>,
width: Property<'static, f32>,
height: Property<'static, f32>,
position: Property<'static, Pnt2>,
events: StreamRc<'static, window::Event>,
rendered: Sender<'static, ()>,
}
fn geometry<C: ToRgba>(data: &[f32], half_line_width: f32, graph_pos: &Pnt2, graph_width: f32, graph_height: f32, bounds: Range<f32>, line_color: &C) -> Vec<Vertex2DColor>{
let x = graph_pos.x;
let y = graph_pos.y;
let w = graph_width;
let h = graph_height;
let alpha = 0.7;
let color = line_color.to_rgba().c;
let line_color = &rgba!(color, 0.5);
let bg_bg_color = &rgba!(color, 0.5);
let bg_color = &rgba!(rgb!(1.0, 1.0, 1.0) - color, alpha);
let mut bg = vec![
vertex2dcolor(vec2(x,y), bg_bg_color),
vertex2dcolor(vec2(x+w,y), bg_bg_color),
vertex2dcolor(vec2(x+w,y+h), bg_bg_color),
vertex2dcolor(vec2(x+w,y+h), bg_bg_color),
vertex2dcolor(vec2(x,y+h), bg_bg_color),
vertex2dcolor(vec2(x,y), bg_bg_color),
vertex2dcolor(vec2(x,y), bg_color),
vertex2dcolor(vec2(x+w,y), bg_color),
vertex2dcolor(vec2(x+w,y+h), bg_color),
vertex2dcolor(vec2(x+w,y+h), bg_color),
vertex2dcolor(vec2(x,y+h), bg_color),
vertex2dcolor(vec2(x,y), bg_color),
];
let plot = data.windows(2)
.enumerate()
.flat_map(move |(idx, prev_next)| {
let x = idx as f32 / data.len() as f32 * graph_width + graph_pos.x;
let x_next = (idx as f32 + 1.)/ data.len() as f32 * graph_width + graph_pos.x;
let y = map_clamp(prev_next[0], bounds.start, bounds.end, graph_pos.y + h, graph_pos.y);
let y_next = map_clamp(prev_next[1], bounds.start, bounds.end, graph_pos.y + h, graph_pos.y);
let prev = vec2(x, y);
let next = vec2(x_next, y_next);
let dx = next.x - prev.x;
let dy = next.y - prev.y;
let normal = normalize(&vec2(-dy, dx));
let normal = normal * half_line_width;
vec![
vertex2dcolor(next + normal, line_color),
vertex2dcolor(prev + normal, line_color),
vertex2dcolor(prev - normal, line_color),
vertex2dcolor(prev - normal, line_color),
vertex2dcolor(next - normal, line_color),
vertex2dcolor(next + normal, line_color),
]
});
bg.extend(plot);
bg
}
impl Graph{
pub fn new<S: StreamT<'static, window::Event>>(name: &str, data: RangedParameter<'static, Vec<f32>, f32>, pos: Pnt2, width: f32, font: Rc<Ttf>, events: S) -> Graph{
GraphBuilder::new(name, data, events)
.position(pos)
.width(width)
.font(font)
.create()
}
pub fn new_auto_range<S: StreamT<'static, window::Event>>(name: &str, data: Property<'static, Vec<f32>>, pos: Pnt2, width: f32, font: Rc<Ttf>, events: S) -> Graph{
GraphAutoRangeBuilder::new(name, data, events)
.position(pos)
.width(width)
.font(font)
.create()
}
}
impl ControlGeometry for Graph{
fn flexible() -> bool{
true
}
fn geometry(&self, _container: Option<&Rect<f32>>) -> Option<Vec<Vertex2DColor>>{
self.rendered.send(());
Some(self.geometry.to_value())
}
fn text_geometry(&self, _container: Option<&Rect<f32>>) -> Option<Vec<Vertex2DTex>>{
Some(self.text_geometry.to_value())
}
fn texture_geometry(&self, _container: Option<&Rect<f32>>) -> Option<Vec<Vertex2DTex>>{
None
}
fn font(&self) -> Option<&Ttf>{
Some(&self.font)
}
fn color(&self) -> &Rgb<f32>{
&self.color
}
fn position(&self) -> &Property<'static, Pnt2>{
&self.position
}
fn width(&self) -> &Property<'static, f32>{
&self.width
}
fn height(&self) -> &Property<'static, f32>{
&self.height
}
}
impl ControlEvents for Graph{
fn non_attended(&self) -> StreamRc<'static, window::Event>{
self.events.clone()
}
}
impl Control for Graph{
fn name(&self) -> &str{
&self.label
}
}
pub struct GraphBuilder{
name: String,
data: RangedParameter<'static, Vec<f32>, f32>,
events: StreamRc<'static, window::Event>,
shape: ControlShape,
}
impl GraphBuilder{
pub fn new<S: StreamT<'static, window::Event>>(name: &str, parameter: RangedParameter<'static, Vec<f32>, f32>, events: S) -> GraphBuilder{
let pos = Property::new(origin());
let width = Property::new(200.);
let height = width.clone().map(|width| width * 9. / 16. );
let margin = 0.;
GraphBuilder{
name: name.to_string(),
data: parameter,
events: events.rc(),
shape: ControlShape::new(pos, width, height, margin, margin, WHITE),
}
}
}
impl ControlBuilder for GraphBuilder{
type Control = Graph;
fn shape(&mut self) -> &mut ControlShape{
&mut self.shape
}
fn create(self) -> Graph{
let line_width = 2.;
let half_line_width = line_width * 0.5;
let font = self.shape.font().unwrap_or_else(|| ::utils::default_font());
let width = self.shape.width();
let height = self.shape.width().map(|w| (w * 9. / 16.).min(100.));
let position = self.shape.position();
let data = self.data;
let color = self.shape.color();
let (rendered_sender, rendered_stream) = utils::rendered_stream(self.events.clone().unique());
let rendered = rendered_stream.to_property(false);
let filtered_update = self.events.clone().filter_by(rendered).update().rc();
let geometry = {
let position = position.clone();
let width = width.clone();
let height = height.clone();
filtered_update.clone().map(move |_|{
(data.to_value().unwrap_or(vec![]), data.range(), *position, *width, *height)
})
.dedup()
.map(move |(data, range, position, width, height)|{
geometry(&data,
half_line_width,
&position,
width,
height,
range,
&color)
}).to_property(vec![])
};
let descender = font.descender();
let line_gap = font.line_gap();
let spacing = line_gap - descender;
let line_height = font.line_height();
let v_margin = self.shape.v_margin();
let text_geometry = if self.shape.label_visible() {
let name = self.name;
let font = font.clone();
let position = position.clone();
let height = height.clone();
filtered_update.map(move |_| (*position, *height))
.map(move |(pos, height)|{
let pos_text = pos + vec2(0., height + v_margin + line_height);
font.mesh(&name, pos_text.x, pos_text.y, CoordinateOrigin::TopLeft).into()
}).to_property(vec![])
}else{
Property::default()
};
let height = if self.shape.label_visible() {
height.map(move |h| h + v_margin + line_height + spacing)
}else{
height
};
Graph {
label: "".to_string(),
geometry: geometry,
font: font,
text_geometry: text_geometry,
color: WHITE.to_rgb(),
width,
height,
position,
rendered: rendered_sender,
events: self.events,
}
}
}
impl ControlBuilt<RangedParameter<'static, Vec<f32>, f32>> for Graph{
type Builder = GraphBuilder;
}
impl ControlDefaultProperty for RangedParameter<'static, Vec<f32>, f32>{
type Builder = GraphBuilder;
}
impl BuilderFromProperty<RangedParameter<'static, Vec<f32>, f32>> for GraphBuilder{
fn from_parameter<S: StreamT<'static, window::Event>>(parameter: RangedParameter<'static, Vec<f32>, f32>, name: &str, events: S) -> GraphBuilder{
GraphBuilder::new(name, parameter, events)
}
}
pub struct GraphAutoRangeBuilder{
name: String,
data: Property<'static, Vec<f32>>,
events: StreamRc<'static, window::Event>,
shape: ControlShape,
}
impl GraphAutoRangeBuilder{
pub fn new<S: StreamT<'static, window::Event>>(name: &str, parameter: Property<'static, Vec<f32>>, events: S) -> GraphAutoRangeBuilder{
let pos = Property::new(origin());
let width = Property::new(200.);
let height = width.clone().map(|width| width * 9. / 16. );
let margin = 0.;
GraphAutoRangeBuilder {
name: name.to_string(),
data: parameter,
events: events.rc(),
shape: ControlShape::new(pos, width, height, margin, margin, WHITE),
}
}
}
impl ControlBuilder for GraphAutoRangeBuilder {
type Control = Graph;
fn shape(&mut self) -> &mut ControlShape {
&mut self.shape
}
fn create(self) -> Graph {
let line_width = 2.;
let half_line_width = line_width * 0.5;
let font = self.shape.font().unwrap_or_else(|| ::utils::default_font());
let width = self.shape.width();
let height = self.shape.width().map(|w| (w * 9. / 16.).min(100.));
let position = self.shape.position();
let data = self.data;
let color = self.shape.color();
let (rendered_sender, rendered_stream) = utils::rendered_stream(self.events.clone().unique());
let rendered = rendered_stream.to_property(false);
let filtered_update = self.events.clone().filter_by(rendered).update().rc();
let geometry = {
let position = position.clone();
let width = width.clone();
let height = height.clone();
filtered_update.clone().scan(None, move |range: &mut Option<Range<f32>>, _|{
if !data.is_empty() {
let data = data.to_value();
let min = *data.iter().min_by_key(|v| (*v * 10000.) as i32).unwrap();
let max = *data.iter().max_by_key(|v| (*v * 10000.) as i32).unwrap();
if range.is_none(){
*range = Some(min .. max);
}else{
let range = range.as_mut().unwrap();
range.start = range.start.min(min);
range.end = range.end.max(max);
}
Some((data, range.as_ref().unwrap().clone(), *position, *width, *height))
}else{
None
}
})
.dedup()
.map(move |(data, range, position, width, height)|{
geometry(&data,
half_line_width,
&position,
width,
height,
range,
&color)
}).to_property(vec![])
};
let descender = font.descender();
let line_gap = font.line_gap();
let spacing = line_gap - descender;
let line_height = font.line_height();
let v_margin = self.shape.v_margin();
let text_geometry = if self.shape.label_visible() {
let name = self.name;
let font = font.clone();
let position = position.clone();
let height = height.clone();
filtered_update.map(move |_| (*position, *height))
.map(move |(pos, height)|{
let pos_text = pos + vec2(0., height + v_margin + line_height);
font.mesh(&name, pos_text.x, pos_text.y, CoordinateOrigin::TopLeft).into()
}).to_property(vec![])
}else{
Property::default()
};
let height = if self.shape.label_visible() {
height.map(move |h| h + v_margin + line_height + spacing)
}else{
height
};
Graph {
label: "".to_string(),
geometry: geometry,
font: font,
text_geometry: text_geometry,
color: WHITE.to_rgb(),
width,
height,
position,
rendered: rendered_sender,
events: self.events,
}
}
}
impl ControlBuilt<Property<'static, Vec<f32>>> for Graph{
type Builder = GraphAutoRangeBuilder;
}
impl ControlDefaultProperty for Property<'static, Vec<f32>>{
type Builder = GraphAutoRangeBuilder;
}
impl BuilderFromProperty<Property<'static, Vec<f32>>> for GraphAutoRangeBuilder{
fn from_parameter<S: StreamT<'static, window::Event>>(parameter: Property<'static, Vec<f32>>, name: &str, events: S) -> GraphAutoRangeBuilder{
GraphAutoRangeBuilder::new(name, parameter, events)
}
}