use gtk::prelude::*; use gtk::DrawingArea; use std::rc::Rc; use std::cell::RefCell; pub struct Graph { pub area: DrawingArea, pub scale_x_start: f64, pub scale_x_size: f64, pub scale_y_start: f64, pub scale_y_size: f64, pub draw_patch: bool, pub lines: Vec } pub struct Line { points: Vec<(f64,f64)>, color: (f64, f64, f64) } impl Line { pub fn new(r: f64, g: f64, b: f64, points: Vec<(f64,f64)>) -> Self { Line { points, color: (r,g,b) } } } impl Graph { pub fn new(area: DrawingArea, scale_x_start: f64, scale_x_size: f64, scale_y_start: f64, scale_y_size: f64, draw_patch: bool, lines: Vec) -> Rc> { let graph = Rc::new(RefCell::new(Graph { area, scale_x_start, scale_x_size, scale_y_start, scale_y_size, draw_patch, lines })); let graph_tmp = Rc::clone(&graph); graph.borrow().area.connect_draw(move |area,ctx| { Graph::draw(area, ctx, &graph_tmp); Inhibit(false) }); graph } fn draw(area: >k::DrawingArea, ctx: &cairo::Context, graph: &Rc>) { ctx.set_source_rgb(0.1, 0.5, 0.5); ctx.paint(); let width = area.get_allocated_width() as f64; let height = area.get_allocated_height() as f64; graph.borrow_mut().adjust_scale_automatic_y(); graph.borrow_mut().adjust_scale_automatic_x(); Graph::draw_boxes(ctx, width, height, 40.0, 20.0, 5.0, 0.3); Graph::draw_boxes(ctx, width, height, 40.0, 20.0, 50.0, 0.1); let cell_size = 50.0; let v_bars = math::round::ceil(width/cell_size, 0) as i32; let h_bars= math::round::ceil(height/cell_size, 0) as i32; let h_scale = (graph.borrow().scale_x_size)/(v_bars - 1) as f64; // ms let v_scale = (graph.borrow().scale_y_size)/(h_bars - 1) as f64; // ms ctx.set_line_width(2.0); ctx.set_line_cap(cairo::LineCap::Round); let draw_patch = graph.borrow().draw_patch; for line in graph.borrow().lines.iter() { for p in line.points.iter().enumerate() { let xp = if p.0 < line.points.len() - 1 { line.points[p.0 + 1] } else { line.points[p.0] }; ctx.set_source_rgb(line.color.0, line.color.1, line.color.2); ctx.move_to(((cell_size as f64)*(xp.0 - graph.borrow().scale_x_start))/h_scale + 40.0, height - ((cell_size as f64)*(xp.1 - graph.borrow().scale_y_start))/v_scale - 20.0); ctx.line_to(((cell_size as f64)*(p.1.0 - graph.borrow().scale_x_start))/h_scale + 40.0, height - ((cell_size as f64)*(p.1.1 - graph.borrow().scale_y_start))/v_scale - 20.0); ctx.stroke(); if draw_patch { ctx.set_source_rgb(0.0, 0.0, 1.0); ctx.arc(((cell_size as f64)*(p.1.0 - graph.borrow().scale_x_start))/h_scale + 40.0, height - ((cell_size as f64)*(p.1.1 - graph.borrow().scale_y_start))/v_scale - 20.0, 5.0, 0.0, std::f64::consts::PI * 2.0); ctx.stroke(); } } } ctx.set_source_rgb(0.1, 0.4, 0.4); ctx.rectangle(0.0, 0.0, 40.0, height); ctx.rectangle(0.0, height - 20.0, width, 20.0); ctx.fill(); ctx.set_source_rgb(1.0, 1.0, 1.0); for x in 0..v_bars { let text = math::round::floor(x as f64 * h_scale + graph.borrow().scale_x_start, 1).to_string(); let f = ctx.text_extents(&text); ctx.move_to(40.0 + x as f64 * cell_size - f.width, height - 10.0); ctx.show_text(&text); } for y in (0..h_bars).rev() { let text = math::round::floor(y as f64 * v_scale + graph.borrow().scale_y_start, 1).to_string(); let f = ctx.text_extents(&text); ctx.move_to(40.0 - f.width, height - y as f64 * cell_size - f.height - 15.0); ctx.show_text(&text); } } fn draw_boxes(ctx: &cairo::Context, area_width: f64, area_height: f64, src_x: f64, src_y: f64, cell_size: f64, color: f64) { ctx.set_source_rgb(color, color, color); ctx.set_line_width(1.0); for x in 1..math::round::ceil(area_width/cell_size, 0) as i32 { let xi = x as f64 * cell_size + src_x; ctx.move_to(xi, 0.0); ctx.line_to(xi, area_height - src_y); } for y in 1..math::round::ceil(area_height/cell_size, 0) as i32 { let yi = area_height - y as f64 * cell_size - src_y; ctx.move_to(src_x, yi); ctx.line_to(area_width, yi); } ctx.stroke(); } pub fn adjust_scale_automatic_y(&mut self) { let mut mx:Option = None; let mut mi:Option = None; for line in self.lines.iter() { if let None = mx { mx = Some(line.points[0].0); } if let None = mi { mi = Some(line.points[0].0); } for (_,y) in line.points.iter().skip(1) { mx = Some(f64::max(mx.unwrap(), *y)); mi = Some(f64::min(mi.unwrap(), *y)); } } let spread = (mx.unwrap() - mi.unwrap()).abs(); self.scale_y_start = mi.unwrap() - spread * 0.1; self.scale_y_size = spread * 1.2; } pub fn adjust_scale_automatic_x(&mut self) { } }