diff --git a/src/graph.rs b/src/graph.rs index 3bb77fb..76d80a2 100644 --- a/src/graph.rs +++ b/src/graph.rs @@ -1,10 +1,12 @@ +//! This part do manage most of work realted to drawing graph + use gtk::prelude::*; use gtk::DrawingArea; use std::rc::Rc; use std::cell::RefCell; use std::collections::HashMap; - +/// A single line pub struct Line { pub points: Vec<(f64,f64)>, pub color: (f64, f64, f64) @@ -18,45 +20,30 @@ impl Line { } } } +/// Tools to draw Graph 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 draw_baarik_box: bool, - pub draw_box: bool, - pub auto_adjust_y: bool, - pub lines: HashMap, - pub pankti_sankya: f64 + pub scale_x_start: f64, // start of x on pankti + pub scale_x_size: f64, // size of pankti to show + pub scale_y_start: f64, // start of y on stambh + pub scale_y_size: f64, // size of stambh to show + pub draw_patch: bool, // enable to draw circle spot on line + pub draw_box: bool, // enable to show boxes linke graph paper + pub draw_baarik_box: bool, // enable to show baarik(similar meaning to smaller) linke graph paper + pub auto_adjust_y: bool, // enable to automatically adjust y axis + pub lines: HashMap, + pub pankti_sankya: f64 // use used while adding to point in lines to see last count of graphable input } impl Graph { - 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 new(area: DrawingArea, scale_x_start: f64, scale_x_size: f64, scale_y_start: f64, scale_y_size: f64, draw_patch: bool, - draw_baarik_box: bool, draw_box: bool, + draw_baarik_box: bool, auto_adjust_y: bool, lines: HashMap, pankti_sankya: f64) -> Rc> { @@ -68,8 +55,8 @@ impl Graph { scale_y_start, scale_y_size, draw_patch, - draw_baarik_box: draw_baarik_box, draw_box, + draw_baarik_box, auto_adjust_y, lines, pankti_sankya @@ -84,6 +71,44 @@ impl Graph { graph } + /// used to draw box and baarik box + fn draw_boxes(ctx: &cairo::Context, area_width: f64, area_height: f64, src_x: f64, cell_size: f64, color: f64) { + ctx.set_source_rgb(color, color, color); + ctx.set_line_width(1.0); + // lines parallel to stambh + for i in 1..math::round::ceil(area_width/cell_size, 0) as i32 { + let xi = i as f64 * cell_size + src_x; + ctx.move_to(xi, 0.0); + ctx.line_to(xi, area_height); + } + // lines parallel to pankti + for i in 1..math::round::ceil(area_height/cell_size, 0) as i32 { + let yi = area_height - i as f64 * cell_size; + ctx.move_to(src_x, yi); + ctx.line_to(area_width + src_x, yi); + } + ctx.stroke(); + } + + /// transform point to show on graph + fn transform_on_graph( + p_start: f64, + s_start: f64, + p: f64, + s: f64, + aa_dumm_pankti: f64, + aa_dumm_stambh: f64, + scale_x_size: f64, + scale_y_size: f64, + height: f64, + stambh_scale_width: f64) -> (f64, f64){ + ( + ((p - p_start) * aa_dumm_pankti)/scale_x_size + stambh_scale_width, + height - ((s - s_start) * aa_dumm_stambh) / scale_y_size + ) + } + + /// callback of drawing area to draw graph fn draw(area: >k::DrawingArea, ctx: &cairo::Context, graph: &Rc>) { @@ -92,95 +117,140 @@ impl Graph { 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; + let pankti_scale_height = 50.0; + let stambh_scale_width = 60.0; + let width = area.get_allocated_width() as f64 - stambh_scale_width; + let height = area.get_allocated_height() as f64 - pankti_scale_height; + + let manjusa_maap = 50.0; + + let rekha_sankhya_pankti = math::round::floor(width / manjusa_maap, 0); + let rekha_sankhya_stambh = math::round::floor(height / manjusa_maap, 0); + + let aa_dumm_pankti = rekha_sankhya_pankti * manjusa_maap; + let aa_dumm_stambh = rekha_sankhya_stambh * manjusa_maap; + + let anupat_pankti = (manjusa_maap * graph.scale_x_size) / aa_dumm_pankti; + let anupat_stambh = (manjusa_maap * graph.scale_y_size) / aa_dumm_stambh; + + // drawing boxes to show area as graph paper if graph.draw_box { if graph.draw_baarik_box { - Graph::draw_boxes(ctx, width, height, 40.0, 20.0, 5.0, 0.3); + Graph::draw_boxes(ctx, width, height, stambh_scale_width, 5.0, 0.3); } - - Graph::draw_boxes(ctx, width, height, 40.0, 20.0, 50.0, 0.1); + Graph::draw_boxes(ctx, width, height, stambh_scale_width, 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.scale_x_size)/(v_bars - 1) as f64; // ms - let v_scale = (graph.scale_y_size)/(h_bars - 1) as f64; // ms - + // Drawing point and line on graph area ctx.set_line_width(2.0); ctx.set_line_cap(cairo::LineCap::Round); let draw_patch = graph.draw_patch; for (_,line) in graph.lines.iter() { - for p in line.points.iter().enumerate() { - let xp = if p.0 < line.points.len() - 1 { - line.points[p.0 + 1] + for (i, (p,s)) in line.points.iter().enumerate() { + // check if point is last poin on line + let (p_dumm, s_dumm) = if i < line.points.len() - 1 { + line.points[i + 1] } else { - line.points[p.0] + line.points[i] }; + + let bindu_t = Graph::transform_on_graph( + graph.scale_x_start, + graph.scale_y_start, + *p, + *s, + aa_dumm_pankti, + aa_dumm_stambh, + graph.scale_x_size, + graph.scale_y_size, + height, + stambh_scale_width, + ); + + let bindu_dumm_t = Graph::transform_on_graph( + graph.scale_x_start, + graph.scale_y_start, + p_dumm, + s_dumm, + aa_dumm_pankti, + aa_dumm_stambh, + graph.scale_x_size, + graph.scale_y_size, + height, + stambh_scale_width, + ); + ctx.set_source_rgb(line.color.0, line.color.1, line.color.2); - ctx.move_to(((cell_size as f64)*(xp.0 - graph.scale_x_start))/h_scale + 40.0, height - ((cell_size as f64)*(xp.1 - graph.scale_y_start))/v_scale - 20.0); - ctx.line_to(((cell_size as f64)*(p.1.0 - graph.scale_x_start))/h_scale + 40.0, height - ((cell_size as f64)*(p.1.1 - graph.scale_y_start))/v_scale - 20.0); + ctx.move_to(bindu_dumm_t.0, bindu_dumm_t.1); + ctx.line_to(bindu_t.0, bindu_t.1); ctx.stroke(); + // draw circle over point if draw_patch { ctx.set_source_rgb(0.0, 0.0, 1.0); - ctx.arc(((cell_size as f64)*(p.1.0 - graph.scale_x_start))/h_scale + 40.0, height - ((cell_size as f64)*(p.1.1 - graph.scale_y_start))/v_scale - 20.0, 5.0, 0.0, std::f64::consts::PI * 2.0); + ctx.arc(bindu_dumm_t.0, bindu_dumm_t.1, + 5.0, 0.0, std::f64::consts::PI * 2.0); ctx.stroke(); } } } + // draw darker recragle over scales 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.rectangle(0.0, 0.0, stambh_scale_width, height + pankti_scale_height); + ctx.rectangle(stambh_scale_width, height, width, pankti_scale_height); 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.scale_x_start, 1).to_string(); + // write numbers on pankti scale + for i in 0..(rekha_sankhya_pankti as i32 + 1) { + let text = math::round::floor(i as f64 * anupat_pankti + graph.scale_x_start, 4).to_string(); let f = ctx.text_extents(&text); - ctx.move_to(40.0 + x as f64 * cell_size - f.width, height - 10.0); + ctx.move_to(i as f64 * manjusa_maap - f.width + stambh_scale_width + f.height / 0.866, height + f.width * 0.5 + f.height); + ctx.save(); + ctx.rotate(std::f64::consts::PI / -6.0); ctx.show_text(&text); + ctx.restore(); } - - for y in (0..h_bars).rev() { - let text = math::round::floor(y as f64 * v_scale + graph.scale_y_start, 1).to_string(); + // write numbers on stambh scale + for i in (0..aa_dumm_stambh as i32 + 1).rev() { + let text = math::round::floor(i as f64 * anupat_stambh + graph.scale_y_start, 4).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.move_to(stambh_scale_width - f.width + f.height / 1.732, height - i as f64 * manjusa_maap + f.width * 0.5); + ctx.save(); + ctx.rotate(std::f64::consts::PI / -6.0); ctx.show_text(&text); + ctx.restore(); } } + /// Adjust stambh and pankti as needed , trim lines and redraws pub fn redraw(&mut self) { let (mx_x, mi_x, mx_y, mi_y) = self.get_extremes(); // stambh if self.auto_adjust_y { let spread = (mx_y - mi_y).abs(); - self.scale_y_start = mi_y - spread * 0.1; self.scale_y_size = spread * 1.2; } // pankti let spread = (mx_x - mi_x).abs(); - if spread < self.scale_x_size { - self.scale_x_start = mi_x; + self.scale_x_start = mi_x; } else { self.scale_x_start = mx_x - self.scale_x_size; } - self.trim_lines(); - self.area.queue_draw(); + self.trim_lines(); // trim + self.area.queue_draw(); // redraw } + /// find minimun and maximum value from all lines pub fn get_extremes(&self) -> (f64, f64, f64, f64){ + // trick to avoid no lines if self.lines.len() == 0 { return (0.0,0.0,0.0,0.0); } @@ -199,6 +269,10 @@ impl Graph { continue; } + // keeping these in here is a good idea than picking line[0].point[0] + // because consider if 0th line have no points and 1st too than that fails + // and also if we keep 0 as default value to start then a graph with + // 100 to 200 will have 0 as minimum if let None = mx_x { mx_x = Some(line.points[0].0); } @@ -226,6 +300,8 @@ impl Graph { (mx_x.unwrap(), mi_x.unwrap(), mx_y.unwrap(), mi_y.unwrap()) } + // tims line form left side of line. Why left?? to avoid wasting time in ponits in range + // it skips i point out of screen to keep a non terminating line experience pub fn trim_lines(&mut self) { for (_, line) in self.lines.iter_mut() { let mut i = 0; diff --git a/src/lib.rs b/src/lib.rs index abbed87..78cb5d5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,8 +12,9 @@ use std::io::BufReader; use graph::Graph; -pub enum Status { - JAGRIT, SAYAN, AVRODTIH, PARIVARTIT, NIKAS +/// Status of Serial reading +enum Status { + JAGRIT, SAYAN, AVRODTIH, PARIVARTIT } pub struct Config { @@ -22,6 +23,7 @@ pub struct Config { port: String } +// impl Config { pub fn new() -> Config { Config { @@ -50,7 +52,7 @@ pub fn build_ui(app: >k::Application, config: Arc::>) { 0.0, 100.0, 0.0, 100.0, false, - false, + true, false, true, HashMap::new(), @@ -72,6 +74,10 @@ pub fn build_ui(app: >k::Application, config: Arc::>) { let about_menu = builder.get_object::("about_menu").expect("Resource file missing!"); let about = builder.get_object::("about").expect("Resource file missing!"); + about.connect_close(|a| { + a.hide(); + }); + about_menu.connect_activate(move |_|{ about.show_all(); }); @@ -155,6 +161,7 @@ pub fn build_ui(app: >k::Application, config: Arc::>) { let tmp_graph = Rc::clone(&graph); draw_box.connect_clicked(move |btn| { + draw_baarik_box.set_sensitive(btn.get_active()); let mut tmp_graph = tmp_graph.borrow_mut(); tmp_graph.draw_box = btn.get_active(); tmp_graph.redraw(); @@ -311,21 +318,6 @@ pub fn build_ui(app: >k::Application, config: Arc::>) { } glib::Continue(true) }); - - // Time ke hisab se pankti ko aage bhadhay - // let (sender, receiver) = glib::MainContext::channel(glib::PRIORITY_DEFAULT); - // glib::timeout_add(300, move || { - // sender.send(()).unwrap(); - // glib::Continue(true) - // }); - - // let tmp_graph = Rc::clone(&graph); - // receiver.attach(None, move |_| { - // // println!("{:?}", tmp_graph.borrow_mut().lines[0].points); - // tmp_graph.borrow_mut().scale_x_start += 1.0; - // tmp_graph.borrow_mut().redraw(); - // glib::Continue(true) - // }); } fn serial_thread_work( @@ -333,6 +325,7 @@ fn serial_thread_work( bufread: &mut Option>>, sender: &glib::Sender, buf: &mut String) { + let mut do_sleep = false; match config.lock() { Ok(mut config) => { match config.status { @@ -348,7 +341,6 @@ fn serial_thread_work( } } }, - Status::NIKAS => {}, Status::PARIVARTIT => { let p = match serialport::new(&config.port, config.bondrate).open() { Ok(p) => p, @@ -360,80 +352,92 @@ fn serial_thread_work( *bufread = Some(BufReader::new(p)); config.status = Status::JAGRIT; }, - Status::SAYAN => {} + Status::SAYAN => { + do_sleep = true; + } } - }, Err(_) => { sender.send(MessageSerialThread::Status("Faild prepare for communication!".to_owned())).unwrap(); return; } }; + + // Hack for smooth performance + if do_sleep { + std::thread::sleep(std::time::Duration::from_millis(100)); + } else { + std::thread::sleep(std::time::Duration::from_nanos(1)); + } } fn receiver_for_msg(text: String, graph: &Rc>, full_log: >k::CheckButton, log_area: >k::TextView) { - if text.starts_with("#") { - graph.borrow_mut().pankti_sankya += 1.0; - for (index, line) in text[1..].split(" ").enumerate() { - let part = line.split("="); - let part = part.into_iter().collect::>(); - if part.len() == 1 { - let num = match part[0].trim().parse::() { - Ok(val) => val, - Err(_) => { - continue; + for text in text.lines() { + if text.len() == 0 { + return; + } else if text.starts_with("#") { + graph.borrow_mut().pankti_sankya += 1.0; + for (index, line) in text[1..].split(" ").enumerate() { + let part = line.split("="); + let part = part.into_iter().collect::>(); + if part.len() == 1 { + let num = match part[0].trim().parse::() { + Ok(val) => val, + Err(_) => { + continue; + } + }; + let mut gp = graph.borrow_mut(); + + let sankhya = gp.pankti_sankya; + match gp.lines.get_mut(&index.to_string()) { + Some(val) => { + val.points.push((sankhya, num)); + } None => { + let v = vec![(sankhya, num)]; + + let mut rng = rand::thread_rng(); + gp.lines.insert(index.to_string(), graph::Line::new(rng.gen_range(0.0..1.0), 0.0, rng.gen_range(0.0..1.0), v)); + } } - }; - let mut gp = graph.borrow_mut(); - - let sankhya = gp.pankti_sankya; - match gp.lines.get_mut(&index.to_string()) { - Some(val) => { - val.points.push((sankhya, num)); - } None => { - let v = vec![(sankhya, num)]; - - let mut rng = rand::thread_rng(); - gp.lines.insert(index.to_string(), graph::Line::new(rng.gen_range(0.0..1.0), 0.0, rng.gen_range(0.0..1.0), v)); + gp.redraw(); + } else if part.len() == 2 { + let num = match part[1].trim().parse::() { + Ok(val) => val, + Err(_) => { + continue; + } + }; + let mut gp = graph.borrow_mut(); + + let sankhya = gp.pankti_sankya; + match gp.lines.get_mut(part[0]) { + Some(val) => { + val.points.push((sankhya, num)); + } None => { + let v = vec![(sankhya, num)]; + + let mut rng = rand::thread_rng(); + gp.lines.insert(part[0].to_owned(), graph::Line::new(rng.gen_range(0.0..1.0), 0.0, rng.gen_range(0.0..1.0), v)); + } } + gp.redraw(); } - gp.redraw(); - } else if part.len() == 2 { - let num = match part[1].trim().parse::() { - Ok(val) => val, - Err(_) => { - continue; - } - }; - let mut gp = graph.borrow_mut(); - - let sankhya = gp.pankti_sankya; - match gp.lines.get_mut(part[0]) { - Some(val) => { - val.points.push((sankhya, num)); - } None => { - let v = vec![(sankhya, num)]; - - let mut rng = rand::thread_rng(); - gp.lines.insert(part[0].to_owned(), graph::Line::new(rng.gen_range(0.0..1.0), 0.0, rng.gen_range(0.0..1.0), v)); - } - } - gp.redraw(); } - } - - if full_log.get_active(){ + + if full_log.get_active(){ + let buf = log_area.get_buffer() + .expect("Couldn't get log_area"); + buf.insert(&mut buf.get_end_iter(), &format!("{}\n",text)); + log_area.scroll_to_iter(&mut buf.get_end_iter(), 0.4, true, 0.0, 0.0); + log_area.queue_draw(); + } + } else { let buf = log_area.get_buffer() .expect("Couldn't get log_area"); - buf.insert(&mut buf.get_end_iter(), &text); + buf.insert(&mut buf.get_end_iter(), &format!("{}\n",text)); log_area.scroll_to_iter(&mut buf.get_end_iter(), 0.4, true, 0.0, 0.0); log_area.queue_draw(); } - } else { - let buf = log_area.get_buffer() - .expect("Couldn't get log_area"); - buf.insert(&mut buf.get_end_iter(), &text); - log_area.scroll_to_iter(&mut buf.get_end_iter(), 0.4, true, 0.0, 0.0); - log_area.queue_draw(); } } diff --git a/ui/main_window.glade b/ui/main_window.glade index 1b73215..f10784e 100644 --- a/ui/main_window.glade +++ b/ui/main_window.glade @@ -8,7 +8,6 @@ Tarangam (तरंगम्) A simple serial plotter. एक सरल सीरीय्ल पलौटर। - Piyush Mishra image-missing @@ -278,6 +277,7 @@ True True False + True True diff --git a/ui/main_window.glade~ b/ui/main_window.glade~ index 1b73215..f10784e 100644 --- a/ui/main_window.glade~ +++ b/ui/main_window.glade~ @@ -8,7 +8,6 @@ Tarangam (तरंगम्) A simple serial plotter. एक सरल सीरीय्ल पलौटर। - Piyush Mishra image-missing @@ -278,6 +277,7 @@ True True False + True True