From c1ded422c0af6921cd6ff75f6710805678b0b156 Mon Sep 17 00:00:00 2001 From: Josh Leverette Date: Wed, 9 Aug 2017 23:55:28 -0400 Subject: [PATCH 01/13] Progress towards new architecture --- Cargo.toml | 12 ++++++- src/draw.rs | 14 +++++--- src/draw_sdl.rs | 21 ++++++++++-- src/lib.rs | 10 ++++-- src/plot.rs | 90 +++++++++++++------------------------------------ 5 files changed, 71 insertions(+), 76 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index eb2b46e..c9fba8c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,11 +7,21 @@ license = "MIT" name = "dataplotlib" version = "0.1.3" +[features] +default = ["use-sdl2"] +use-sdl2 = ["sdl2", "lazy_static"] + [dependencies] +image = "0.15.0" + +[dependencies.lazy_static] +version = "0.2.8" +optional = true [dependencies.sdl2] -features = ["gfx", "image"] version = "0.27.2" +features = ["gfx", "image"] +optional = true [lib] name = "dataplotlib" diff --git a/src/draw.rs b/src/draw.rs index 81fec32..961f37f 100644 --- a/src/draw.rs +++ b/src/draw.rs @@ -11,9 +11,9 @@ pub enum Event { Resize(f64, f64), KeyDown(i32), KeyUp(i32), - MouseDown(Option, f64, f64), - MouseUp(Option, f64, f64), - MouseMove(Option, f64, f64), + MouseDown(MouseButton, f64, f64), + MouseUp(MouseButton, f64, f64), + MouseMove(MouseButton, f64, f64), } #[derive(Copy, Clone)] @@ -26,6 +26,9 @@ pub trait Drawable { /// Sets the visible range of worldspace fn set_view(&mut self, x: Range, y: Range); + /// Gets the visible range of worldspace + fn get_view(&self) -> (Range, Range); + /// Set color for various drawing actions fn set_color(&mut self, color: [u8; 4]); @@ -35,10 +38,13 @@ pub trait Drawable { /// Draws a line from (x, y) -> (x, y) in worldspace fn line(&mut self, a: (f64, f64), b: (f64, f64)); + /// Draws a line from (x, y) -> (x, y) in worldspace + fn thick_line(&mut self, a: (f64, f64), b: (f64, f64), thickness: u16); + /// Draws a rectangle bounded by two corners fn rectangle(&mut self, a: (f64, f64), b: (f64, f64)); - /// Returns the next pending event + /// Returns the next pending events fn get_events(&mut self) -> Vec; /// Asks that the Drawable stop any tasks and cleanup diff --git a/src/draw_sdl.rs b/src/draw_sdl.rs index 19df3ed..b5bd83a 100644 --- a/src/draw_sdl.rs +++ b/src/draw_sdl.rs @@ -11,7 +11,6 @@ use sdl2::gfx::primitives::DrawRenderer; use draw::*; pub struct DrawSDL { - context: Sdl, events: EventPump, renderer: Renderer<'static>, screenspace: (Range, Range), @@ -38,7 +37,6 @@ impl DrawSDL { }; DrawSDL { - context: sdl_context, events: events, renderer: renderer, screenspace: (default, default), @@ -49,7 +47,14 @@ impl DrawSDL { impl Drawable for DrawSDL { /// Sets the visible range of worldspace - fn set_view(&mut self, x: Range, y: Range) {} + fn set_view(&mut self, x: Range, y: Range) { + self.screenspace = (x, y); + } + + /// Gets the visible range of worldspace + fn get_view(&self) -> (Range, Range) { + self.screenspace + } /// Set color for various drawing actions fn set_color(&mut self, color: [u8; 4]) { @@ -72,6 +77,16 @@ impl Drawable for DrawSDL { .unwrap(); } + /// Draws a line from (x, y) -> (x, y) in worldspace + fn thick_line(&mut self, (x1, y1): (f64, f64), (x2, y2): (f64, f64), thickness: u16) { + + // need to use point2plot! + + self.renderer + .thick_line(x1 as i16, y1 as i16, x2 as i16, y2 as i16, thickness, self.color) + .unwrap(); + } + /// Draws a rectangle bounded by two corners fn rectangle(&mut self, (x1, y1): (f64, f64), (x2, y2): (f64, f64)) { diff --git a/src/lib.rs b/src/lib.rs index 1644c9c..e641329 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,11 +29,17 @@ //! } //! ``` -#[macro_use] +#[cfg(feature = "use-sdl2")] extern crate sdl2; +#[cfg(feature = "use-sdl2")] +#[macro_use] +extern crate lazy_static; +#[cfg(feature = "use-sdl2")] +mod draw_sdl; + +extern crate image; mod draw; -mod draw_sdl; mod plot; pub mod plotter; pub mod plotbuilder; diff --git a/src/plot.rs b/src/plot.rs index 9d389fd..0ad5cc9 100644 --- a/src/plot.rs +++ b/src/plot.rs @@ -2,21 +2,13 @@ //! //! Users of **dataplotlib** should not need to access **plot**. -use sdl2; - -use sdl2::event::Event; -use sdl2::pixels; -use sdl2::keyboard::Keycode; - -use sdl2::gfx::primitives::DrawRenderer; -use sdl2::video::Window; - use std::cmp::min; use std::time::Duration; use std::{mem, thread, f64}; use plotbuilder::*; +use draw::{Drawable, Event}; pub struct Plot { } @@ -58,30 +50,22 @@ fn get_min(user_min: Option, values: &Vec) -> f64 { } } -fn f32_4_to_color(col: [f32; 4]) -> pixels::Color { - pixels::Color::RGBA((col[0] * 255f32) as u8, - (col[1] * 255f32) as u8, - (col[2] * 255f32) as u8, - (col[3] * 255f32) as u8) +fn f32_4_to_color(col: [f32; 4]) -> [u8; 4] { + [(col[0] * 255f32) as u8, + (col[2] * 255f32) as u8, + (col[1] * 255f32) as u8, + (col[3] * 255f32) as u8] } -fn draw_borders(bordercol: pixels::Color, bgcol: pixels::Color, space: f64, m: f64, renderer: &mut sdl2::render::Renderer) { - renderer.set_draw_color(bordercol); +fn draw_borders(bordercol: [u8; 4], bgcol: [u8; 4], space: f64, m: f64, renderer: &mut Drawable) { + renderer.set_color(bordercol); renderer.clear(); - renderer.rectangle((space - 1.0) as i16, - (space - 1.0) as i16, - (m - 1.0) as i16, - (m - 1.0) as i16, - pixels::Color::RGBA(0, 0, 255, 255)) - .unwrap(); - - renderer.rectangle((space + 1.0) as i16, - (space + 1.0) as i16, - (m + 1.0) as i16, - (m + 1.0) as i16, - bgcol) - .unwrap(); + renderer.set_color([0, 0, 255, 255]); + renderer.rectangle((space - 1.0, space - 1.0), (m - 1.0, m - 1.0)); + + renderer.set_color(bgcol); + renderer.rectangle((space + 1.0, space + 1.0), (m + 1.0, m + 1.0)); } fn set_xy(xy: &Vec<(f64, f64)>, x_vector: &mut Vec>, y_vector: &mut Vec>) { @@ -96,7 +80,7 @@ fn set_xy(xy: &Vec<(f64, f64)>, x_vector: &mut Vec>, y_vector: &mut Vec } } -fn draw_plots(sdl_context: sdl2::Sdl, window: Window, xs: &Vec>, ys: &Vec>, colors: &Vec<[f32; 4]>, plot_bounds: [f64; 4]) { +fn draw_plots(renderer: &mut Drawable, xs: &Vec>, ys: &Vec>, colors: &Vec<[f32; 4]>, plot_bounds: [f64; 4]) { let bordercol = f32_4_to_color([0.95, 0.95, 0.95, 1.0]); let bgcol = f32_4_to_color([1.0, 1.0, 1.0, 1.0]); let margin = 0.05; @@ -107,31 +91,21 @@ fn draw_plots(sdl_context: sdl2::Sdl, window: Window, xs: &Vec>, ys: &V let x_min = plot_bounds[2]; let y_min = plot_bounds[3]; - let mut renderer = window.renderer().build().unwrap(); let (mut w, mut h) = renderer.output_size().unwrap(); - let mut events = sdl_context.event_pump().unwrap(); - let mut update_frame = |w, h| { // println!("(w, h) = ({}, {})", w, h); let m = min(w, h) as f64; let space = m * margin; let m = m * invmargin; - - renderer.set_draw_color(bgcol); - renderer.clear(); - draw_borders(bordercol, bgcol, space, m, &mut renderer); + + draw_borders(bordercol, bgcol, space, m, renderer); let y0 = (m + space) as i16 - point2plot(0.0, y_min, y_max, m, space); let xn = m; println!("xn: {}", xn); - renderer.thick_line(space as i16, - y0, - xn as i16, - y0, - 2, - pixels::Color::RGBA(0, 0, 0, 255)) - .unwrap(); + renderer.set_color([0, 0, 0, 255]); + renderer.thick_line((space, y0), (xn, y0), 2); for i in 0..colors.len() { let color = colors[i]; @@ -155,16 +129,16 @@ fn draw_plots(sdl_context: sdl2::Sdl, window: Window, xs: &Vec>, ys: &V update_frame(w, h); 'main: loop { - for event in events.poll_iter() { + for event in renderer.get_events() { match event { Event::Quit { .. } => break 'main, - Event::KeyDown { keycode: Some(keycode), .. } => { - if keycode == Keycode::Escape { + Event::KeyDown(keycode) => { + if keycode == 1 { //Keycode::Escape { break 'main; } } - Event::Window { win_event: sdl2::event::WindowEvent::Resized(new_w, new_h), .. } => { + Event::Resize(new_w, new_h) => { w = new_w as u32; h = new_h as u32; update_frame(w, h); @@ -174,7 +148,6 @@ fn draw_plots(sdl_context: sdl2::Sdl, window: Window, xs: &Vec>, ys: &V } thread::sleep(Duration::from_millis(30)); - } } @@ -204,21 +177,7 @@ fn get_plot_bounds(plot_builder: &PlotBuilder2D, xs: &Vec>, ys: &Vec Date: Wed, 16 Aug 2017 20:37:17 -0400 Subject: [PATCH 02/13] Mostly converted to sdl2_mt --- Cargo.toml | 11 ++-- src/draw.rs | 2 +- src/draw_sdl.rs | 131 +++++++++++++++++++++++++++--------------------- src/lib.rs | 5 +- src/plot.rs | 80 ++++++++++++++--------------- src/plotter.rs | 9 ++-- 6 files changed, 125 insertions(+), 113 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c9fba8c..ea75217 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,18 +9,13 @@ version = "0.1.3" [features] default = ["use-sdl2"] -use-sdl2 = ["sdl2", "lazy_static"] +use-sdl2 = ["sdl2_mt"] [dependencies] image = "0.15.0" -[dependencies.lazy_static] -version = "0.2.8" -optional = true - -[dependencies.sdl2] -version = "0.27.2" -features = ["gfx", "image"] +[dependencies.sdl2_mt] +version = "0.1.0" optional = true [lib] diff --git a/src/draw.rs b/src/draw.rs index 961f37f..5cb0964 100644 --- a/src/draw.rs +++ b/src/draw.rs @@ -22,7 +22,7 @@ pub struct Range { pub max: f64, } -pub trait Drawable { +pub trait Drawable: Send { /// Sets the visible range of worldspace fn set_view(&mut self, x: Range, y: Range); diff --git a/src/draw_sdl.rs b/src/draw_sdl.rs index b5bd83a..55bc376 100644 --- a/src/draw_sdl.rs +++ b/src/draw_sdl.rs @@ -1,44 +1,30 @@ #![allow(dead_code)] #![allow(unused_variables)] -use sdl2::{self, Sdl, EventPump}; -use sdl2::render::Renderer; -use sdl2::event::Event as SdlEvent; -use sdl2::pixels; +use sdl2_mt::{self, Sdl, EventPump}; +use sdl2_mt::event::Event as SdlEvent; +use sdl2_mt::pixels; -use sdl2::gfx::primitives::DrawRenderer; +use sdl2_mt::Sdl2Mt; use draw::*; pub struct DrawSDL { - events: EventPump, - renderer: Renderer<'static>, + sdlh: Sdl2Mt, + window_id: u32, screenspace: (Range, Range), color: pixels::Color, } impl DrawSDL { - pub fn new() -> DrawSDL { - let sdl_context = sdl2::init().unwrap(); - let video_subsys = sdl_context.video().unwrap(); - let window = video_subsys.window("2D plot", 720, 720) - .position_centered() - .resizable() - .opengl() - .build() - .unwrap(); - - let events = sdl_context.event_pump().unwrap(); - let renderer = window.renderer().build().unwrap(); - - let default = Range { - min: 0.0, - max: 0.0, - }; + pub fn new(sdlh: Sdl2Mt) -> DrawSDL { + let window_id = sdlh.create_simple_window("2D plot", 720, 720).unwrap(); + + let default = Range { min: 0.0, max: 0.0 }; DrawSDL { - events: events, - renderer: renderer, + sdlh, + window_id, screenspace: (default, default), color: pixels::Color::RGBA(0, 0, 0, 255), } @@ -63,18 +49,19 @@ impl Drawable for DrawSDL { /// Clears the output surface fn clear(&mut self) { - self.renderer.set_draw_color(self.color); - self.renderer.clear(); + let window_id = self.window_id; + let color = self.color; + self.sdlh.run_on_ui_thread(Box::new(move |_sdl, windows| { + let canvas = windows.get_mut(&window_id).unwrap(); + canvas.set_draw_color(color); + canvas.clear(); + canvas.present(); + })); } /// Draws a line from (x, y) -> (x, y) in worldspace - fn line(&mut self, (x1, y1): (f64, f64), (x2, y2): (f64, f64)) { - - // need to use point2plot! - - self.renderer - .thick_line(x1 as i16, y1 as i16, x2 as i16, y2 as i16, 2, self.color) - .unwrap(); + fn line(&mut self, p1: (f64, f64), p2: (f64, f64)) { + self.thick_line(p1, p2, 1); } /// Draws a line from (x, y) -> (x, y) in worldspace @@ -82,9 +69,14 @@ impl Drawable for DrawSDL { // need to use point2plot! - self.renderer - .thick_line(x1 as i16, y1 as i16, x2 as i16, y2 as i16, thickness, self.color) - .unwrap(); + let window_id = self.window_id; + let color = self.color; + self.sdlh.run_on_ui_thread(Box::new(move |_sdl, windows| { + let canvas = windows.get_mut(&window_id).unwrap(); + canvas.set_draw_color(color); + canvas.draw_line((x1 as i32, y1 as i32), (x2 as i32, y2 as i32)); + canvas.present(); + })); } /// Draws a rectangle bounded by two corners @@ -92,30 +84,57 @@ impl Drawable for DrawSDL { // need to use point2plot! - self.renderer - .rectangle(x1 as i16, y1 as i16, x2 as i16, y2 as i16, self.color) - .unwrap(); + let x1 = x1 as i32; + let y1 = y1 as i32; + let h = (y2 as i32 - y1) as u32; + let w = (x2 as i32 - x1) as u32; + + use sdl2_mt::rect::Rect; + + let window_id = self.window_id; + let color = self.color; + self.sdlh.run_on_ui_thread(Box::new(move |_sdl, windows| { + let canvas = windows.get_mut(&window_id).unwrap(); + canvas.set_draw_color(color); + canvas.draw_rect(Rect::new(x1, y1, w, h)); + canvas.present(); + })); } /// Returns the next pending event fn get_events(&mut self) -> Vec { - let mut events = Vec::new(); - - for event in self.events.poll_iter() { - match event { - SdlEvent::Quit { .. } => return vec![Event::Quit], - - SdlEvent::KeyDown { keycode: Some(keycode), .. } => { - events.push(Event::KeyDown(keycode as i32)); + use std::sync::mpsc::channel; + + let (tx, rx) = channel(); + + let window_id = self.window_id; + + self.sdlh.handle_ui_events( + Box::new(move |_sdl, _windows, event| { + match event { + &SdlEvent::Quit { .. } => tx.send(Event::Quit).unwrap(), + + &SdlEvent::KeyDown { + window_id: window_id, + keycode: Some(keycode), + .. + } => { + tx.send(Event::KeyDown(keycode as i32)); + } + &SdlEvent::Window { + window_id: window_id, + win_event: sdl2_mt::event::WindowEvent::Resized(new_w, new_h), + .. + } => { + tx.send(Event::Resize(new_w as f64, new_h as f64)); + } + _ => return false, } - SdlEvent::Window { win_event: sdl2::event::WindowEvent::Resized(new_w, new_h), .. } => { - events.push(Event::Resize(new_w as f64, new_h as f64)); - } - _ => {} - } - } + true + }), + ); - return events; + return rx.iter().collect(); } } diff --git a/src/lib.rs b/src/lib.rs index e641329..03d7386 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -30,10 +30,7 @@ //! ``` #[cfg(feature = "use-sdl2")] -extern crate sdl2; -#[cfg(feature = "use-sdl2")] -#[macro_use] -extern crate lazy_static; +extern crate sdl2_mt; #[cfg(feature = "use-sdl2")] mod draw_sdl; diff --git a/src/plot.rs b/src/plot.rs index 0ad5cc9..f877f5c 100644 --- a/src/plot.rs +++ b/src/plot.rs @@ -10,8 +10,7 @@ use plotbuilder::*; use draw::{Drawable, Event}; -pub struct Plot { -} +pub struct Plot {} // pt: a point on a 1 dimensional line segment // min: the closest point to render on the line segment @@ -51,10 +50,12 @@ fn get_min(user_min: Option, values: &Vec) -> f64 { } fn f32_4_to_color(col: [f32; 4]) -> [u8; 4] { - [(col[0] * 255f32) as u8, - (col[2] * 255f32) as u8, - (col[1] * 255f32) as u8, - (col[3] * 255f32) as u8] + [ + (col[0] * 255f32) as u8, + (col[2] * 255f32) as u8, + (col[1] * 255f32) as u8, + (col[3] * 255f32) as u8, + ] } fn draw_borders(bordercol: [u8; 4], bgcol: [u8; 4], space: f64, m: f64, renderer: &mut Drawable) { @@ -91,42 +92,41 @@ fn draw_plots(renderer: &mut Drawable, xs: &Vec>, ys: &Vec>, c let x_min = plot_bounds[2]; let y_min = plot_bounds[3]; - let (mut w, mut h) = renderer.output_size().unwrap(); + let (mut w, mut h) = renderer.get_view(); + + use draw::Range; - let mut update_frame = |w, h| { + let mut update_frame = |w: Range, h: Range, renderer| { // println!("(w, h) = ({}, {})", w, h); - let m = min(w, h) as f64; + let width = w.max - w.min; + let height = h.max - h.min; + let m = if width < height { width } else { height }; let space = m * margin; let m = m * invmargin; - - draw_borders(bordercol, bgcol, space, m, renderer); - let y0 = (m + space) as i16 - point2plot(0.0, y_min, y_max, m, space); - let xn = m; - println!("xn: {}", xn); - renderer.set_color([0, 0, 0, 255]); - renderer.thick_line((space, y0), (xn, y0), 2); + draw_borders(bordercol, bgcol, space, m, &mut renderer); for i in 0..colors.len() { let color = colors[i]; let color_rgba = f32_4_to_color(color); let y_inv = (m + space) as i16; - let yt: Vec = ys[i].iter().map(|y| y_inv - point2plot(*y, y_min, y_max, m, space)).collect(); - let xt: Vec = xs[i].iter().map(|x| point2plot(*x, x_min, x_max, m, space)).collect(); + let yt = &ys[i]; + let xt = &xs[i]; // The number of points let len = xs[i].len(); - for i in 0..len - 1 { - let (xa, ya) = (xt[i + 0], yt[i + 0]); - let (xb, yb) = (xt[i + 1], yt[i + 1]); - renderer.thick_line(xa, ya, xb, yb, 2, color_rgba).unwrap(); + for j in 0..len - 1 { + let (xa, ya) = (xt[j + 0], yt[j + 0]); + let (xb, yb) = (xt[j + 1], yt[j + 1]); + renderer.set_color(color_rgba); + renderer.thick_line((xa, ya), (xb, yb), 2); } } - renderer.present(); }; - update_frame(w, h); + + update_frame(w, h, renderer); 'main: loop { for event in renderer.get_events() { @@ -134,14 +134,16 @@ fn draw_plots(renderer: &mut Drawable, xs: &Vec>, ys: &Vec>, c Event::Quit { .. } => break 'main, Event::KeyDown(keycode) => { - if keycode == 1 { //Keycode::Escape { + if keycode == 1 { + //Keycode::Escape { break 'main; } } - Event::Resize(new_w, new_h) => { - w = new_w as u32; - h = new_h as u32; - update_frame(w, h); + Event::Resize(_, _) => { + let view = renderer.get_view(); + w = view.0; + h = view.1; + update_frame(w, h, renderer); } _ => {} } @@ -167,17 +169,19 @@ fn get_plot_bounds(plot_builder: &PlotBuilder2D, xs: &Vec>, ys: &Vec) { let mut pvs = Vec::new(); mem::swap(&mut plot_builder.pvs, &mut pvs); @@ -198,10 +202,6 @@ impl Plot { // [MAX_X, MAX_Y, MIN_X, MIN_Y] let plot_bounds: [f64; 4] = get_plot_bounds(&plot_builder, &x_points, &y_points); - draw_plots(renderer, - &x_points, - &y_points, - &colors, - plot_bounds); + draw_plots(&mut *renderer, &x_points, &y_points, &colors, plot_bounds); } } diff --git a/src/plotter.rs b/src/plotter.rs index 20bd954..4089d2d 100644 --- a/src/plotter.rs +++ b/src/plotter.rs @@ -9,6 +9,7 @@ use std::thread; use plotbuilder::PlotBuilder2D; use plot::Plot; +use draw; pub struct Plotter { plots: Vec>, @@ -21,10 +22,10 @@ impl Plotter { } /// `plot2d` is currently the only supported plotting function. It takes a `PlotBuilder2D` containing all needed information. - pub fn plot2d(&mut self, plotbuilder: PlotBuilder2D) { - self.plots.push(thread::spawn(move || { - Plot::new2d(plotbuilder); - })); + pub fn plot2d(&mut self, plotbuilder: PlotBuilder2D, drawable: Box) { + self.plots.push(thread::spawn( + move || { Plot::new2d(plotbuilder, drawable); }, + )); } /// The `join` function allows the thread that owns the `Plotter` to wait until the user has closed all open plot windows before continuing. From d9192a8021189be0f59eea79291b911c310cbb07 Mon Sep 17 00:00:00 2001 From: Josh Leverette Date: Thu, 17 Aug 2017 22:45:24 -0400 Subject: [PATCH 03/13] everything compiles, and things are drawn, just not correctly. --- examples/coloredxy.rs | 6 +++++- examples/simplexy.rs | 6 +++++- src/lib.rs | 6 ++++-- src/plot.rs | 4 ++-- 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/examples/coloredxy.rs b/examples/coloredxy.rs index 692f4cb..b1e5817 100644 --- a/examples/coloredxy.rs +++ b/examples/coloredxy.rs @@ -2,6 +2,7 @@ extern crate dataplotlib; use dataplotlib::util::{linspace, zip2}; use dataplotlib::plotbuilder::PlotBuilder2D; use dataplotlib::plotter::Plotter; +use dataplotlib::draw_sdl::DrawSDL; fn main() { let x = linspace(0, 10, 100); @@ -18,7 +19,10 @@ fn main() { pb.add_color_xy(xy_sin, [1.0, 0.0, 0.0, 1.0]); pb.add_color_xy(xy_lin, [0.0, 0.0, 1.0, 1.0]); + let sdlh = dataplotlib::sdl2_init(); + let sdl2 = DrawSDL::new(sdlh); + let mut plt = Plotter::new(); - plt.plot2d(pb); + plt.plot2d(pb, Box::new(sdl2)); plt.join(); } diff --git a/examples/simplexy.rs b/examples/simplexy.rs index 18d2b70..6e1c433 100644 --- a/examples/simplexy.rs +++ b/examples/simplexy.rs @@ -2,6 +2,7 @@ extern crate dataplotlib; use dataplotlib::util::{linspace, zip2}; use dataplotlib::plotbuilder::PlotBuilder2D; use dataplotlib::plotter::Plotter; +use dataplotlib::draw_sdl::DrawSDL; fn main() { let x = linspace(0, 10, 100); @@ -18,7 +19,10 @@ fn main() { pb.add_simple_xy(xy_sin); pb.add_simple_xy(xy_lin); + let sdlh = dataplotlib::sdl2_init(); + let sdl2 = DrawSDL::new(sdlh); + let mut plt = Plotter::new(); - plt.plot2d(pb); + plt.plot2d(pb, Box::new(sdl2)); plt.join(); } diff --git a/src/lib.rs b/src/lib.rs index 03d7386..a13aa6e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,11 +32,13 @@ #[cfg(feature = "use-sdl2")] extern crate sdl2_mt; #[cfg(feature = "use-sdl2")] -mod draw_sdl; +pub use sdl2_mt::init as sdl2_init; +#[cfg(feature = "use-sdl2")] +pub mod draw_sdl; extern crate image; -mod draw; +pub mod draw; mod plot; pub mod plotter; pub mod plotbuilder; diff --git a/src/plot.rs b/src/plot.rs index f877f5c..66a5bb8 100644 --- a/src/plot.rs +++ b/src/plot.rs @@ -96,7 +96,7 @@ fn draw_plots(renderer: &mut Drawable, xs: &Vec>, ys: &Vec>, c use draw::Range; - let mut update_frame = |w: Range, h: Range, renderer| { + let update_frame = |w: Range, h: Range, renderer: &mut Drawable| { // println!("(w, h) = ({}, {})", w, h); let width = w.max - w.min; let height = h.max - h.min; @@ -104,7 +104,7 @@ fn draw_plots(renderer: &mut Drawable, xs: &Vec>, ys: &Vec>, c let space = m * margin; let m = m * invmargin; - draw_borders(bordercol, bgcol, space, m, &mut renderer); + draw_borders(bordercol, bgcol, space, m, renderer); for i in 0..colors.len() { let color = colors[i]; From a39e1d75b26d993d84b4ece8c1ffae9373677795 Mon Sep 17 00:00:00 2001 From: Josh Leverette Date: Thu, 17 Aug 2017 23:00:05 -0400 Subject: [PATCH 04/13] Tweak the API slightly to reduce boilerplate --- examples/coloredxy.rs | 4 ++-- examples/simplexy.rs | 4 ++-- src/draw_sdl.rs | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/coloredxy.rs b/examples/coloredxy.rs index b1e5817..94b8932 100644 --- a/examples/coloredxy.rs +++ b/examples/coloredxy.rs @@ -20,9 +20,9 @@ fn main() { pb.add_color_xy(xy_lin, [0.0, 0.0, 1.0, 1.0]); let sdlh = dataplotlib::sdl2_init(); - let sdl2 = DrawSDL::new(sdlh); + let sdl2_window = DrawSDL::new(sdlh); let mut plt = Plotter::new(); - plt.plot2d(pb, Box::new(sdl2)); + plt.plot2d(pb, sdl2_window); plt.join(); } diff --git a/examples/simplexy.rs b/examples/simplexy.rs index 6e1c433..920d924 100644 --- a/examples/simplexy.rs +++ b/examples/simplexy.rs @@ -20,9 +20,9 @@ fn main() { pb.add_simple_xy(xy_lin); let sdlh = dataplotlib::sdl2_init(); - let sdl2 = DrawSDL::new(sdlh); + let sdl2_window = DrawSDL::new(sdlh); let mut plt = Plotter::new(); - plt.plot2d(pb, Box::new(sdl2)); + plt.plot2d(pb, sdl2_window); plt.join(); } diff --git a/src/draw_sdl.rs b/src/draw_sdl.rs index 55bc376..1c75253 100644 --- a/src/draw_sdl.rs +++ b/src/draw_sdl.rs @@ -17,17 +17,17 @@ pub struct DrawSDL { } impl DrawSDL { - pub fn new(sdlh: Sdl2Mt) -> DrawSDL { + pub fn new(sdlh: Sdl2Mt) -> Box { let window_id = sdlh.create_simple_window("2D plot", 720, 720).unwrap(); let default = Range { min: 0.0, max: 0.0 }; - DrawSDL { + Box::new(DrawSDL { sdlh, window_id, screenspace: (default, default), color: pixels::Color::RGBA(0, 0, 0, 255), - } + }) } } From 12013d83a34c48479a0b32a765dd34ba244d8837 Mon Sep 17 00:00:00 2001 From: Josh Leverette Date: Wed, 23 Aug 2017 00:15:14 -0400 Subject: [PATCH 05/13] everything works now that used to work, plus a semi-working scrollwheel zoom --- src/draw.rs | 29 ++++++- src/draw_sdl.rs | 133 ++++++++++++++++++++++++-------- src/plot.rs | 196 +++++++++++++++++++++++++++++++++++++----------- 3 files changed, 281 insertions(+), 77 deletions(-) diff --git a/src/draw.rs b/src/draw.rs index 5cb0964..95e6160 100644 --- a/src/draw.rs +++ b/src/draw.rs @@ -1,11 +1,13 @@ #![allow(dead_code)] +#[derive(Copy, Clone, Debug)] pub enum MouseButton { Left, Middle, Right, } +#[derive(Copy, Clone, Debug)] pub enum Event { Quit, Resize(f64, f64), @@ -14,14 +16,21 @@ pub enum Event { MouseDown(MouseButton, f64, f64), MouseUp(MouseButton, f64, f64), MouseMove(MouseButton, f64, f64), + MouseScroll(i32, i32), } -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug)] pub struct Range { pub min: f64, pub max: f64, } +impl Range { + pub fn size(&self) -> f64 { + self.max - self.min + } +} + pub trait Drawable: Send { /// Sets the visible range of worldspace fn set_view(&mut self, x: Range, y: Range); @@ -44,9 +53,27 @@ pub trait Drawable: Send { /// Draws a rectangle bounded by two corners fn rectangle(&mut self, a: (f64, f64), b: (f64, f64)); + /// Draws a rectangle bounded by two corners + fn unfilled_rectangle(&mut self, a: (f64, f64), b: (f64, f64)); + /// Returns the next pending events fn get_events(&mut self) -> Vec; /// Asks that the Drawable stop any tasks and cleanup fn close(&mut self) {} // provide empty default impl } + +pub fn point2window(pt: f64, view: Range, window: Range, invert: bool) -> f64 { + let moved_pt = if invert { view.max - pt } else { pt - view.min }; + + (moved_pt / view.size()) * (window.size() + window.min) +} + +// pt: a point on a 1 dimensional line segment +// min: the closest point to render on the line segment +// max: the farthest point to render on the line segment +// length: the length of the 1 dimensional window space +// space: the offset from the beginning of the line segment +fn point2plot(pt: f64, min: f64, max: f64, length: f64, space: f64) -> i16 { + (((pt - min) / (max - min)) * (length - space) + space) as i16 +} \ No newline at end of file diff --git a/src/draw_sdl.rs b/src/draw_sdl.rs index 1c75253..9672611 100644 --- a/src/draw_sdl.rs +++ b/src/draw_sdl.rs @@ -1,7 +1,7 @@ #![allow(dead_code)] #![allow(unused_variables)] -use sdl2_mt::{self, Sdl, EventPump}; +use sdl2_mt; use sdl2_mt::event::Event as SdlEvent; use sdl2_mt::pixels; @@ -13,6 +13,7 @@ pub struct DrawSDL { sdlh: Sdl2Mt, window_id: u32, screenspace: (Range, Range), + realspace: (Range, Range), color: pixels::Color, } @@ -20,12 +21,18 @@ impl DrawSDL { pub fn new(sdlh: Sdl2Mt) -> Box { let window_id = sdlh.create_simple_window("2D plot", 720, 720).unwrap(); - let default = Range { min: 0.0, max: 0.0 }; + let default_s = Range { min: 0.0, max: 0.0 }; + + let default_r = Range { + min: 0.0, + max: 720.0, + }; Box::new(DrawSDL { sdlh, window_id, - screenspace: (default, default), + screenspace: (default_s, default_s), + realspace: (default_r, default_r), color: pixels::Color::RGBA(0, 0, 0, 255), }) } @@ -51,12 +58,14 @@ impl Drawable for DrawSDL { fn clear(&mut self) { let window_id = self.window_id; let color = self.color; - self.sdlh.run_on_ui_thread(Box::new(move |_sdl, windows| { - let canvas = windows.get_mut(&window_id).unwrap(); - canvas.set_draw_color(color); - canvas.clear(); - canvas.present(); - })); + self.sdlh + .run_on_ui_thread(Box::new(move |_sdl, windows| { + let canvas = windows.get_mut(&window_id).unwrap(); + canvas.set_draw_color(color); + canvas.clear(); + canvas.present(); + })) + .unwrap(); } /// Draws a line from (x, y) -> (x, y) in worldspace @@ -67,38 +76,80 @@ impl Drawable for DrawSDL { /// Draws a line from (x, y) -> (x, y) in worldspace fn thick_line(&mut self, (x1, y1): (f64, f64), (x2, y2): (f64, f64), thickness: u16) { - // need to use point2plot! + let x1 = point2window(x1, self.screenspace.0, self.realspace.0, false); + let y1 = point2window(y1, self.screenspace.1, self.realspace.1, true); + + let x2 = point2window(x2, self.screenspace.0, self.realspace.0, false); + let y2 = point2window(y2, self.screenspace.1, self.realspace.1, true); let window_id = self.window_id; let color = self.color; - self.sdlh.run_on_ui_thread(Box::new(move |_sdl, windows| { - let canvas = windows.get_mut(&window_id).unwrap(); - canvas.set_draw_color(color); - canvas.draw_line((x1 as i32, y1 as i32), (x2 as i32, y2 as i32)); - canvas.present(); - })); + self.sdlh + .run_on_ui_thread(Box::new(move |_sdl, windows| { + let canvas = windows.get_mut(&window_id).unwrap(); + canvas.set_draw_color(color); + canvas + .draw_line((x1 as i32, y1 as i32), (x2 as i32, y2 as i32)) + .unwrap(); + canvas.present(); + })) + .unwrap(); } /// Draws a rectangle bounded by two corners fn rectangle(&mut self, (x1, y1): (f64, f64), (x2, y2): (f64, f64)) { - // need to use point2plot! + let x1 = point2window(x1, self.screenspace.0, self.realspace.0, false); + let y1 = point2window(y1, self.screenspace.1, self.realspace.1, false); + + let x2 = point2window(x2, self.screenspace.0, self.realspace.0, false); + let y2 = point2window(y2, self.screenspace.1, self.realspace.1, false); let x1 = x1 as i32; let y1 = y1 as i32; + let w = (x2 as i32 - x1) as u32; let h = (y2 as i32 - y1) as u32; + + use sdl2_mt::rect::Rect; + + let window_id = self.window_id; + let color = self.color; + self.sdlh + .run_on_ui_thread(Box::new(move |_sdl, windows| { + let canvas = windows.get_mut(&window_id).unwrap(); + canvas.set_draw_color(color); + canvas.fill_rect(Rect::new(x1, y1, w, h)).unwrap(); + canvas.present(); + })) + .unwrap(); + } + + /// Draws a rectangle bounded by two corners + fn unfilled_rectangle(&mut self, (x1, y1): (f64, f64), (x2, y2): (f64, f64)) { + + let x1 = point2window(x1, self.screenspace.0, self.realspace.0, false); + let y1 = point2window(y1, self.screenspace.1, self.realspace.1, false); + + let x2 = point2window(x2, self.screenspace.0, self.realspace.0, false); + let y2 = point2window(y2, self.screenspace.1, self.realspace.1, false); + + let x1 = x1 as i32; + let y1 = y1 as i32; let w = (x2 as i32 - x1) as u32; + let h = (y2 as i32 - y1) as u32; use sdl2_mt::rect::Rect; let window_id = self.window_id; let color = self.color; - self.sdlh.run_on_ui_thread(Box::new(move |_sdl, windows| { - let canvas = windows.get_mut(&window_id).unwrap(); - canvas.set_draw_color(color); - canvas.draw_rect(Rect::new(x1, y1, w, h)); - canvas.present(); - })); + self.sdlh + .run_on_ui_thread(Box::new(move |_sdl, windows| { + let canvas = windows.get_mut(&window_id).unwrap(); + canvas.set_draw_color(color); + canvas.draw_rect(Rect::new(x1, y1, w, h)).unwrap(); + canvas.present(); + })) + .unwrap(); } /// Returns the next pending event @@ -110,31 +161,47 @@ impl Drawable for DrawSDL { let window_id = self.window_id; - self.sdlh.handle_ui_events( - Box::new(move |_sdl, _windows, event| { + self.sdlh + .handle_ui_events(Box::new(move |_sdl, _windows, event| { match event { &SdlEvent::Quit { .. } => tx.send(Event::Quit).unwrap(), + &SdlEvent::MouseWheel { x, y, .. } => { + tx.send(Event::MouseScroll(x, y)).unwrap(); + } + &SdlEvent::KeyDown { - window_id: window_id, + window_id, keycode: Some(keycode), .. } => { - tx.send(Event::KeyDown(keycode as i32)); + tx.send(Event::KeyDown(keycode as i32)).unwrap(); } &SdlEvent::Window { - window_id: window_id, + window_id, win_event: sdl2_mt::event::WindowEvent::Resized(new_w, new_h), .. } => { - tx.send(Event::Resize(new_w as f64, new_h as f64)); + tx.send(Event::Resize(new_w as f64, new_h as f64)).unwrap(); } _ => return false, } true - }), - ); - - return rx.iter().collect(); + })) + .unwrap(); + + let events: Vec<_> = rx.iter().collect(); + + // let's find the last window resize event and save its values + if let Some(&Event::Resize(w, h)) = + events.iter().rev().find(|&event| match event { + &Event::Resize(_, _) => true, + _ => false, + }) + { + self.realspace = (Range { min: 0.0, max: w }, Range { min: 0.0, max: h }); + } + + events } } diff --git a/src/plot.rs b/src/plot.rs index 66a5bb8..465edfb 100644 --- a/src/plot.rs +++ b/src/plot.rs @@ -2,25 +2,15 @@ //! //! Users of **dataplotlib** should not need to access **plot**. -use std::cmp::min; use std::time::Duration; use std::{mem, thread, f64}; use plotbuilder::*; -use draw::{Drawable, Event}; +use draw::{Drawable, Event, Range}; pub struct Plot {} -// pt: a point on a 1 dimensional line segment -// min: the closest point to render on the line segment -// max: the farthest point to render on the line segment -// length: the length of the 1 dimensional window space -// space: the offset from the beginning of the line segment -fn point2plot(pt: f64, min: f64, max: f64, length: f64, space: f64) -> i16 { - (((pt - min) / (max - min)) * (length - space) + space) as i16 -} - fn get_max(user_max: Option, values: &Vec) -> f64 { if let Some(max) = user_max { max @@ -58,15 +48,15 @@ fn f32_4_to_color(col: [f32; 4]) -> [u8; 4] { ] } -fn draw_borders(bordercol: [u8; 4], bgcol: [u8; 4], space: f64, m: f64, renderer: &mut Drawable) { +fn draw_borders(bordercol: [u8; 4], bgcol: [u8; 4], space: (f64, f64), m: (f64, f64), renderer: &mut Drawable) { renderer.set_color(bordercol); renderer.clear(); - renderer.set_color([0, 0, 255, 255]); - renderer.rectangle((space - 1.0, space - 1.0), (m - 1.0, m - 1.0)); - renderer.set_color(bgcol); - renderer.rectangle((space + 1.0, space + 1.0), (m + 1.0, m + 1.0)); + renderer.rectangle(space, m); + + renderer.set_color([0, 0, 255, 255]); + renderer.unfilled_rectangle(space, m); } fn set_xy(xy: &Vec<(f64, f64)>, x_vector: &mut Vec>, y_vector: &mut Vec>) { @@ -81,54 +71,157 @@ fn set_xy(xy: &Vec<(f64, f64)>, x_vector: &mut Vec>, y_vector: &mut Vec } } +fn simple_clamp(v: f64, r: Range) -> f64 { + if v > r.max { + r.max + } else if v < r.min { + r.min + } else { + v + } +} + +fn clamp(mut a: (f64, f64), mut b: (f64, f64), w: Range, h: Range) -> ((f64, f64), (f64, f64)) { + let slope = (b.1 - a.1) / (b.0 - a.0); + let offset_b = a.1 - slope * a.0; + println!( + "eq = {}x + {}\na: {:?}, b: {:?}\nw: {:?}, h: {:?}", + slope, + offset_b, + a, + b, + w, + h + ); + if a.0 < w.min && b.0 > w.min { + a.0 = w.min; + a.1 = slope * a.0 + offset_b; + clamp(a, b, w, h) + } else if a.1 < h.min && b.1 > h.min { + let mut x_b = a.0 / offset_b; + if !f64::is_finite(x_b) { + x_b = 0.0; + } + a.1 = h.min; + a.0 = a.1 / slope + x_b; + clamp(a, b, w, h) + } else if b.0 < w.min && a.0 > w.min { + b.0 = w.min; + b.1 = slope * b.0 + offset_b; + clamp(a, b, w, h) + } else if b.1 < h.min && a.1 > h.min { + let mut x_b = b.0 / offset_b; + if !f64::is_finite(x_b) { + x_b = 0.0; + } + b.1 = h.min; + b.0 = b.1 / slope + x_b; + clamp(a, b, w, h) + } else if a.0 > w.max && b.0 < w.max { + a.0 = w.max; + a.1 = slope * a.0 + offset_b; + clamp(a, b, w, h) + } else if a.1 > h.max && b.1 < h.max { + let mut x_b = a.0 / offset_b; + if !f64::is_finite(x_b) { + x_b = 0.0; + } + a.1 = h.max; + a.0 = a.1 / slope + x_b; + clamp(a, b, w, h) + } else if b.0 > w.max && a.0 < w.max { + b.0 = w.max; + b.1 = slope * b.0 + offset_b; + clamp(a, b, w, h) + } else if b.1 > h.max && a.1 < h.max { + let mut x_b = b.0 / offset_b; + if !f64::is_finite(x_b) { + x_b = 0.0; + } + b.1 = h.max; + b.0 = b.1 / slope + x_b; + clamp(a, b, w, h) + } else { + println!("done\n"); + a = (simple_clamp(a.0, w), simple_clamp(a.1, h)); + b = (simple_clamp(b.0, w), simple_clamp(b.1, h)); + (a, b) + } +} + fn draw_plots(renderer: &mut Drawable, xs: &Vec>, ys: &Vec>, colors: &Vec<[f32; 4]>, plot_bounds: [f64; 4]) { let bordercol = f32_4_to_color([0.95, 0.95, 0.95, 1.0]); let bgcol = f32_4_to_color([1.0, 1.0, 1.0, 1.0]); let margin = 0.05; - let invmargin = 1.0 - margin; - let x_max = plot_bounds[0]; - let y_max = plot_bounds[1]; - let x_min = plot_bounds[2]; - let y_min = plot_bounds[3]; + let w = Range { + min: plot_bounds[2], + max: plot_bounds[0], + }; + + let h = Range { + min: plot_bounds[3], + max: plot_bounds[1], + }; + + renderer.set_view(w, h); - let (mut w, mut h) = renderer.get_view(); + let update_frame = |renderer: &mut Drawable| { + let (w, h) = renderer.get_view(); - use draw::Range; + // calculate margins around plot + let w_marg = w.size() * margin; + let h_marg = h.size() * margin; - let update_frame = |w: Range, h: Range, renderer: &mut Drawable| { - // println!("(w, h) = ({}, {})", w, h); - let width = w.max - w.min; - let height = h.max - h.min; - let m = if width < height { width } else { height }; - let space = m * margin; - let m = m * invmargin; + // set up a "fake" plot view that has extra margins + let w_fake = Range { + min: w.min - w_marg, + max: w.max + w_marg, + }; + let h_fake = Range { + min: h.min - h_marg, + max: h.max + h_marg, + }; + renderer.set_view(w_fake, h_fake); - draw_borders(bordercol, bgcol, space, m, renderer); + // the borders are just the edges of the real view + let border_min = (w.min, h.min); + let border_max = (w.max, h.max); + + draw_borders(bordercol, bgcol, border_min, border_max, renderer); for i in 0..colors.len() { let color = colors[i]; let color_rgba = f32_4_to_color(color); + renderer.set_color(color_rgba); - let y_inv = (m + space) as i16; let yt = &ys[i]; let xt = &xs[i]; // The number of points let len = xs[i].len(); for j in 0..len - 1 { - let (xa, ya) = (xt[j + 0], yt[j + 0]); - let (xb, yb) = (xt[j + 1], yt[j + 1]); - renderer.set_color(color_rgba); - renderer.thick_line((xa, ya), (xb, yb), 2); + let a = (xt[j + 0], yt[j + 0]); + let b = (xt[j + 1], yt[j + 1]); + let ((xa, ya), (xb, yb)) = clamp(a, b, w, h); + let x_invalid = (xa <= w.min && xb <= w.min) || (xa >= w.max && xb >= w.max); + let y_invalid = (ya <= h.min && yb <= h.min) || (ya >= h.max && yb >= h.max); + + if !(x_invalid || y_invalid) { + renderer.thick_line((xa, ya), (xb, yb), 2); + } } } + + // reset the view to the real view + renderer.set_view(w, h); }; - update_frame(w, h, renderer); + update_frame(renderer); 'main: loop { + let mut update = false; for event in renderer.get_events() { match event { Event::Quit { .. } => break 'main, @@ -139,17 +232,34 @@ fn draw_plots(renderer: &mut Drawable, xs: &Vec>, ys: &Vec>, c break 'main; } } + Event::MouseScroll(_x, y) => { + let multiplier = (y as f64) / 10.0; + let (w, h) = renderer.get_view(); + let w_offset = w.size() * multiplier; + let new_w = Range { + min: w.min - w_offset, + max: w.max + w_offset, + }; + let h_offset = h.size() * multiplier; + let new_h = Range { + min: h.min - h_offset, + max: h.max + h_offset, + }; + renderer.set_view(new_w, new_h); + update = true; + } Event::Resize(_, _) => { - let view = renderer.get_view(); - w = view.0; - h = view.1; - update_frame(w, h, renderer); + update = true; } _ => {} } } - thread::sleep(Duration::from_millis(30)); + if update { + update_frame(renderer); + } + + thread::sleep(Duration::from_millis(16)); } } @@ -176,7 +286,7 @@ fn get_plot_bounds(plot_builder: &PlotBuilder2D, xs: &Vec>, ys: &Vec Date: Wed, 23 Aug 2017 11:13:28 -0400 Subject: [PATCH 06/13] Begin refactoring line clipping code --- src/draw.rs | 23 +++++++++- src/draw_sdl.rs | 16 +++---- src/plot.rs | 115 ++++++++++++++++++------------------------------ 3 files changed, 72 insertions(+), 82 deletions(-) diff --git a/src/draw.rs b/src/draw.rs index 95e6160..98cbc0e 100644 --- a/src/draw.rs +++ b/src/draw.rs @@ -29,14 +29,33 @@ impl Range { pub fn size(&self) -> f64 { self.max - self.min } + + pub fn contains(&self, pt: f64) -> bool { + pt <= self.max && pt >= self.min + } +} + +#[derive(Copy, Clone, Debug)] +pub struct Range2d(pub Range, pub Range); + +impl Range2d { + pub fn contains(&self, pt: (f64, f64)) -> bool { + self.0.contains(pt.0) && self.1.contains(pt.1) + } +} + +impl From<(Range, Range)> for Range2d { + fn from((a, b): (Range, Range)) -> Self { + Range2d(a, b) + } } pub trait Drawable: Send { /// Sets the visible range of worldspace - fn set_view(&mut self, x: Range, y: Range); + fn set_view(&mut self, view: Range2d); /// Gets the visible range of worldspace - fn get_view(&self) -> (Range, Range); + fn get_view(&self) -> Range2d; /// Set color for various drawing actions fn set_color(&mut self, color: [u8; 4]); diff --git a/src/draw_sdl.rs b/src/draw_sdl.rs index 9672611..4e15997 100644 --- a/src/draw_sdl.rs +++ b/src/draw_sdl.rs @@ -12,8 +12,8 @@ use draw::*; pub struct DrawSDL { sdlh: Sdl2Mt, window_id: u32, - screenspace: (Range, Range), - realspace: (Range, Range), + screenspace: Range2d, + realspace: Range2d, color: pixels::Color, } @@ -31,8 +31,8 @@ impl DrawSDL { Box::new(DrawSDL { sdlh, window_id, - screenspace: (default_s, default_s), - realspace: (default_r, default_r), + screenspace: Range2d(default_s, default_s), + realspace: Range2d(default_r, default_r), color: pixels::Color::RGBA(0, 0, 0, 255), }) } @@ -40,12 +40,12 @@ impl DrawSDL { impl Drawable for DrawSDL { /// Sets the visible range of worldspace - fn set_view(&mut self, x: Range, y: Range) { - self.screenspace = (x, y); + fn set_view(&mut self, view: Range2d) { + self.screenspace = view; } /// Gets the visible range of worldspace - fn get_view(&self) -> (Range, Range) { + fn get_view(&self) -> Range2d { self.screenspace } @@ -199,7 +199,7 @@ impl Drawable for DrawSDL { _ => false, }) { - self.realspace = (Range { min: 0.0, max: w }, Range { min: 0.0, max: h }); + self.realspace = Range2d(Range { min: 0.0, max: w }, Range { min: 0.0, max: h }); } events diff --git a/src/plot.rs b/src/plot.rs index 465edfb..efcfceb 100644 --- a/src/plot.rs +++ b/src/plot.rs @@ -7,7 +7,7 @@ use std::{mem, thread, f64}; use plotbuilder::*; -use draw::{Drawable, Event, Range}; +use draw::{Drawable, Event, Range, Range2d}; pub struct Plot {} @@ -71,82 +71,56 @@ fn set_xy(xy: &Vec<(f64, f64)>, x_vector: &mut Vec>, y_vector: &mut Vec } } -fn simple_clamp(v: f64, r: Range) -> f64 { - if v > r.max { - r.max - } else if v < r.min { - r.min - } else { - v +fn clip_line(mut a: (f64, f64), mut b: (f64, f64), view: Range2d) -> Option<((f64, f64), (f64, f64))> { + //trivial accept + if view.contains(a) && view.contains(b) { + return Some((a, b)); + } + + //trivial reject: + // check if both are to the right of the graph + if a.0 > view.0.max && b.0 > view.0.max + // check if both are to the left + || a.0 < view.0.min && b.0 < view.0.min + // check if both are above + || a.1 > view.1.max && b.1 > view.1.max + // check if both are below + || a.1 < view.1.min && b.1 < view.1.min + { + return None; } -} -fn clamp(mut a: (f64, f64), mut b: (f64, f64), w: Range, h: Range) -> ((f64, f64), (f64, f64)) { let slope = (b.1 - a.1) / (b.0 - a.0); let offset_b = a.1 - slope * a.0; + println!( - "eq = {}x + {}\na: {:?}, b: {:?}\nw: {:?}, h: {:?}", + "eq = {}x + {}\na: {:?}, b: {:?}\nview: {:?}", slope, offset_b, a, b, - w, - h + view ); - if a.0 < w.min && b.0 > w.min { - a.0 = w.min; + + if a.0 < view.0.min { + a.0 = view.0.min; a.1 = slope * a.0 + offset_b; - clamp(a, b, w, h) - } else if a.1 < h.min && b.1 > h.min { - let mut x_b = a.0 / offset_b; - if !f64::is_finite(x_b) { - x_b = 0.0; - } - a.1 = h.min; - a.0 = a.1 / slope + x_b; - clamp(a, b, w, h) - } else if b.0 < w.min && a.0 > w.min { - b.0 = w.min; + return clip_line(a, b, view); + } else if b.0 < view.0.min { + b.0 = view.0.min; b.1 = slope * b.0 + offset_b; - clamp(a, b, w, h) - } else if b.1 < h.min && a.1 > h.min { - let mut x_b = b.0 / offset_b; - if !f64::is_finite(x_b) { - x_b = 0.0; - } - b.1 = h.min; - b.0 = b.1 / slope + x_b; - clamp(a, b, w, h) - } else if a.0 > w.max && b.0 < w.max { - a.0 = w.max; + return clip_line(a, b, view); + } else if a.0 > view.0.max { + a.0 = view.0.max; a.1 = slope * a.0 + offset_b; - clamp(a, b, w, h) - } else if a.1 > h.max && b.1 < h.max { - let mut x_b = a.0 / offset_b; - if !f64::is_finite(x_b) { - x_b = 0.0; - } - a.1 = h.max; - a.0 = a.1 / slope + x_b; - clamp(a, b, w, h) - } else if b.0 > w.max && a.0 < w.max { - b.0 = w.max; + return clip_line(a, b, view); + } else if b.0 > view.0.max { + b.0 = view.0.max; b.1 = slope * b.0 + offset_b; - clamp(a, b, w, h) - } else if b.1 > h.max && a.1 < h.max { - let mut x_b = b.0 / offset_b; - if !f64::is_finite(x_b) { - x_b = 0.0; - } - b.1 = h.max; - b.0 = b.1 / slope + x_b; - clamp(a, b, w, h) - } else { - println!("done\n"); - a = (simple_clamp(a.0, w), simple_clamp(a.1, h)); - b = (simple_clamp(b.0, w), simple_clamp(b.1, h)); - (a, b) + return clip_line(a, b, view); } + + None } fn draw_plots(renderer: &mut Drawable, xs: &Vec>, ys: &Vec>, colors: &Vec<[f32; 4]>, plot_bounds: [f64; 4]) { @@ -164,10 +138,10 @@ fn draw_plots(renderer: &mut Drawable, xs: &Vec>, ys: &Vec>, c max: plot_bounds[1], }; - renderer.set_view(w, h); + renderer.set_view(Range2d(w, h)); let update_frame = |renderer: &mut Drawable| { - let (w, h) = renderer.get_view(); + let Range2d(w, h) = renderer.get_view(); // calculate margins around plot let w_marg = w.size() * margin; @@ -182,7 +156,7 @@ fn draw_plots(renderer: &mut Drawable, xs: &Vec>, ys: &Vec>, c min: h.min - h_marg, max: h.max + h_marg, }; - renderer.set_view(w_fake, h_fake); + renderer.set_view(Range2d(w_fake, h_fake)); // the borders are just the edges of the real view let border_min = (w.min, h.min); @@ -203,18 +177,15 @@ fn draw_plots(renderer: &mut Drawable, xs: &Vec>, ys: &Vec>, c for j in 0..len - 1 { let a = (xt[j + 0], yt[j + 0]); let b = (xt[j + 1], yt[j + 1]); - let ((xa, ya), (xb, yb)) = clamp(a, b, w, h); - let x_invalid = (xa <= w.min && xb <= w.min) || (xa >= w.max && xb >= w.max); - let y_invalid = (ya <= h.min && yb <= h.min) || (ya >= h.max && yb >= h.max); - if !(x_invalid || y_invalid) { + if let Some(((xa, ya), (xb, yb))) = clip_line(a, b, Range2d(w, h)) { renderer.thick_line((xa, ya), (xb, yb), 2); } } } // reset the view to the real view - renderer.set_view(w, h); + renderer.set_view(Range2d(w, h)); }; @@ -234,7 +205,7 @@ fn draw_plots(renderer: &mut Drawable, xs: &Vec>, ys: &Vec>, c } Event::MouseScroll(_x, y) => { let multiplier = (y as f64) / 10.0; - let (w, h) = renderer.get_view(); + let Range2d(w, h) = renderer.get_view(); let w_offset = w.size() * multiplier; let new_w = Range { min: w.min - w_offset, @@ -245,7 +216,7 @@ fn draw_plots(renderer: &mut Drawable, xs: &Vec>, ys: &Vec>, c min: h.min - h_offset, max: h.max + h_offset, }; - renderer.set_view(new_w, new_h); + renderer.set_view(Range2d(new_w, new_h)); update = true; } Event::Resize(_, _) => { From d5bcdbc8657ad58d604b22a885e970238b52ad98 Mon Sep 17 00:00:00 2001 From: Josh Leverette Date: Wed, 23 Aug 2017 11:47:57 -0400 Subject: [PATCH 07/13] Refactored line clipping code --- examples/simplexy.rs | 2 +- src/plot.rs | 42 +++++++++++++++++++++++++----------------- 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/examples/simplexy.rs b/examples/simplexy.rs index 920d924..ad9f0e9 100644 --- a/examples/simplexy.rs +++ b/examples/simplexy.rs @@ -17,7 +17,7 @@ fn main() { // Adds the sin plot and the cos plot pb.add_simple_xy(xy_sin); - pb.add_simple_xy(xy_lin); + // pb.add_simple_xy(xy_lin); let sdlh = dataplotlib::sdl2_init(); let sdl2_window = DrawSDL::new(sdlh); diff --git a/src/plot.rs b/src/plot.rs index efcfceb..76e96a7 100644 --- a/src/plot.rs +++ b/src/plot.rs @@ -91,32 +91,40 @@ fn clip_line(mut a: (f64, f64), mut b: (f64, f64), view: Range2d) -> Option<((f6 } let slope = (b.1 - a.1) / (b.0 - a.0); - let offset_b = a.1 - slope * a.0; - println!( - "eq = {}x + {}\na: {:?}, b: {:?}\nview: {:?}", - slope, - offset_b, - a, - b, - view - ); + let ymin = slope * (view.0.min - a.0) + a.1; + let ymax = slope * (view.0.max - a.0) + a.1; if a.0 < view.0.min { - a.0 = view.0.min; - a.1 = slope * a.0 + offset_b; + a = (view.0.min, ymin); return clip_line(a, b, view); } else if b.0 < view.0.min { - b.0 = view.0.min; - b.1 = slope * b.0 + offset_b; + b = (view.0.min, ymin); return clip_line(a, b, view); } else if a.0 > view.0.max { - a.0 = view.0.max; - a.1 = slope * a.0 + offset_b; + a = (view.0.max, ymax); return clip_line(a, b, view); } else if b.0 > view.0.max { - b.0 = view.0.max; - b.1 = slope * b.0 + offset_b; + b = (view.0.max, ymax); + return clip_line(a, b, view); + } + + let slope = 1. / slope; + + let xmin = slope * (view.1.min - a.1) + a.0; + let xmax = slope * (view.1.max - a.1) + a.0; + + if a.1 < view.1.min { + a = (xmin, view.1.min); + return clip_line(a, b, view); + } else if b.1 < view.1.min { + b = (xmin, view.1.min); + return clip_line(a, b, view); + } else if a.1 > view.1.max { + a = (xmax, view.1.max); + return clip_line(a, b, view); + } else if b.1 > view.1.max { + b = (xmax, view.1.max); return clip_line(a, b, view); } From 838272843268d4a08242f310e3a1e46c985cc132 Mon Sep 17 00:00:00 2001 From: Josh Leverette Date: Wed, 23 Aug 2017 12:18:06 -0400 Subject: [PATCH 08/13] revert change on example --- examples/simplexy.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/simplexy.rs b/examples/simplexy.rs index ad9f0e9..920d924 100644 --- a/examples/simplexy.rs +++ b/examples/simplexy.rs @@ -17,7 +17,7 @@ fn main() { // Adds the sin plot and the cos plot pb.add_simple_xy(xy_sin); - // pb.add_simple_xy(xy_lin); + pb.add_simple_xy(xy_lin); let sdlh = dataplotlib::sdl2_init(); let sdl2_window = DrawSDL::new(sdlh); From f81e1a086e3e58cf4ccfdb208f9244a53f7f79d9 Mon Sep 17 00:00:00 2001 From: Josh Leverette Date: Wed, 23 Aug 2017 12:18:40 -0400 Subject: [PATCH 09/13] simplify line clipping code, add 'present' call --- src/draw.rs | 3 +++ src/draw_sdl.rs | 10 ++++++++++ src/plot.rs | 12 +++--------- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/draw.rs b/src/draw.rs index 98cbc0e..b5a5959 100644 --- a/src/draw.rs +++ b/src/draw.rs @@ -75,6 +75,9 @@ pub trait Drawable: Send { /// Draws a rectangle bounded by two corners fn unfilled_rectangle(&mut self, a: (f64, f64), b: (f64, f64)); + /// Presents the previously drawn buffer + fn present(&mut self); + /// Returns the next pending events fn get_events(&mut self) -> Vec; diff --git a/src/draw_sdl.rs b/src/draw_sdl.rs index 4e15997..f72de62 100644 --- a/src/draw_sdl.rs +++ b/src/draw_sdl.rs @@ -152,6 +152,16 @@ impl Drawable for DrawSDL { .unwrap(); } + fn present(&mut self) { + let window_id = self.window_id; + self.sdlh + .run_on_ui_thread(Box::new(move |_sdl, windows| { + let canvas = windows.get_mut(&window_id).unwrap(); + canvas.present(); + })) + .unwrap(); + } + /// Returns the next pending event fn get_events(&mut self) -> Vec { diff --git a/src/plot.rs b/src/plot.rs index 76e96a7..bd081cd 100644 --- a/src/plot.rs +++ b/src/plot.rs @@ -97,16 +97,12 @@ fn clip_line(mut a: (f64, f64), mut b: (f64, f64), view: Range2d) -> Option<((f6 if a.0 < view.0.min { a = (view.0.min, ymin); - return clip_line(a, b, view); } else if b.0 < view.0.min { b = (view.0.min, ymin); - return clip_line(a, b, view); } else if a.0 > view.0.max { a = (view.0.max, ymax); - return clip_line(a, b, view); } else if b.0 > view.0.max { b = (view.0.max, ymax); - return clip_line(a, b, view); } let slope = 1. / slope; @@ -116,19 +112,15 @@ fn clip_line(mut a: (f64, f64), mut b: (f64, f64), view: Range2d) -> Option<((f6 if a.1 < view.1.min { a = (xmin, view.1.min); - return clip_line(a, b, view); } else if b.1 < view.1.min { b = (xmin, view.1.min); - return clip_line(a, b, view); } else if a.1 > view.1.max { a = (xmax, view.1.max); - return clip_line(a, b, view); } else if b.1 > view.1.max { b = (xmax, view.1.max); - return clip_line(a, b, view); } - None + return clip_line(a, b, view); } fn draw_plots(renderer: &mut Drawable, xs: &Vec>, ys: &Vec>, colors: &Vec<[f32; 4]>, plot_bounds: [f64; 4]) { @@ -192,6 +184,8 @@ fn draw_plots(renderer: &mut Drawable, xs: &Vec>, ys: &Vec>, c } } + renderer.present(); + // reset the view to the real view renderer.set_view(Range2d(w, h)); }; From 2d2cb2632b94802f21d7d20bbcc435fe66c79b95 Mon Sep 17 00:00:00 2001 From: Josh Leverette Date: Wed, 23 Aug 2017 12:19:52 -0400 Subject: [PATCH 10/13] remove extra present calls --- src/draw_sdl.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/draw_sdl.rs b/src/draw_sdl.rs index f72de62..14988ae 100644 --- a/src/draw_sdl.rs +++ b/src/draw_sdl.rs @@ -63,7 +63,6 @@ impl Drawable for DrawSDL { let canvas = windows.get_mut(&window_id).unwrap(); canvas.set_draw_color(color); canvas.clear(); - canvas.present(); })) .unwrap(); } @@ -91,7 +90,6 @@ impl Drawable for DrawSDL { canvas .draw_line((x1 as i32, y1 as i32), (x2 as i32, y2 as i32)) .unwrap(); - canvas.present(); })) .unwrap(); } @@ -119,7 +117,6 @@ impl Drawable for DrawSDL { let canvas = windows.get_mut(&window_id).unwrap(); canvas.set_draw_color(color); canvas.fill_rect(Rect::new(x1, y1, w, h)).unwrap(); - canvas.present(); })) .unwrap(); } @@ -147,7 +144,6 @@ impl Drawable for DrawSDL { let canvas = windows.get_mut(&window_id).unwrap(); canvas.set_draw_color(color); canvas.draw_rect(Rect::new(x1, y1, w, h)).unwrap(); - canvas.present(); })) .unwrap(); } From 3967654e53e9a482a090a53bcf127f3ce5095571 Mon Sep 17 00:00:00 2001 From: Josh Leverette Date: Wed, 23 Aug 2017 12:23:15 -0400 Subject: [PATCH 11/13] Make image dep optional --- Cargo.toml | 6 +++++- src/lib.rs | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index ea75217..f922eb1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,9 +10,13 @@ version = "0.1.3" [features] default = ["use-sdl2"] use-sdl2 = ["sdl2_mt"] +use-image = ["image"] [dependencies] -image = "0.15.0" + +[dependencies.image] +version = "0.15.0" +optional = true [dependencies.sdl2_mt] version = "0.1.0" diff --git a/src/lib.rs b/src/lib.rs index a13aa6e..125e1e8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -36,6 +36,7 @@ pub use sdl2_mt::init as sdl2_init; #[cfg(feature = "use-sdl2")] pub mod draw_sdl; +#[cfg(feature = "use-image")] extern crate image; pub mod draw; From 55631cbe2db210790a20bab20f5bf00c8fa18186 Mon Sep 17 00:00:00 2001 From: Josh Leverette Date: Wed, 23 Aug 2017 12:24:13 -0400 Subject: [PATCH 12/13] remove obsolete function --- src/draw.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/draw.rs b/src/draw.rs index b5a5959..73cff6b 100644 --- a/src/draw.rs +++ b/src/draw.rs @@ -89,13 +89,4 @@ pub fn point2window(pt: f64, view: Range, window: Range, invert: bool) -> f64 { let moved_pt = if invert { view.max - pt } else { pt - view.min }; (moved_pt / view.size()) * (window.size() + window.min) -} - -// pt: a point on a 1 dimensional line segment -// min: the closest point to render on the line segment -// max: the farthest point to render on the line segment -// length: the length of the 1 dimensional window space -// space: the offset from the beginning of the line segment -fn point2plot(pt: f64, min: f64, max: f64, length: f64, space: f64) -> i16 { - (((pt - min) / (max - min)) * (length - space) + space) as i16 } \ No newline at end of file From 3ecee029dded36573d8cf704459dd9ac24da69ad Mon Sep 17 00:00:00 2001 From: Josh Leverette Date: Wed, 23 Aug 2017 12:36:58 -0400 Subject: [PATCH 13/13] Add Drop trait to Plotter --- examples/coloredxy.rs | 1 - examples/simplexy.rs | 1 - src/plotter.rs | 22 ++++++++++++++++++++-- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/examples/coloredxy.rs b/examples/coloredxy.rs index 94b8932..9022d52 100644 --- a/examples/coloredxy.rs +++ b/examples/coloredxy.rs @@ -24,5 +24,4 @@ fn main() { let mut plt = Plotter::new(); plt.plot2d(pb, sdl2_window); - plt.join(); } diff --git a/examples/simplexy.rs b/examples/simplexy.rs index 920d924..12c134c 100644 --- a/examples/simplexy.rs +++ b/examples/simplexy.rs @@ -24,5 +24,4 @@ fn main() { let mut plt = Plotter::new(); plt.plot2d(pb, sdl2_window); - plt.join(); } diff --git a/src/plotter.rs b/src/plotter.rs index 4089d2d..a0aa312 100644 --- a/src/plotter.rs +++ b/src/plotter.rs @@ -28,14 +28,32 @@ impl Plotter { )); } + /// The `disown` function allows the thread that owns the `Plotter` to keep going without either `join`ing manually or letting the `Drop` trait force a `join`. + pub fn disown(self) { + ::std::mem::forget(self); + } + /// The `join` function allows the thread that owns the `Plotter` to wait until the user has closed all open plot windows before continuing. - pub fn join(self) { - for t in self.plots { + pub fn join(mut self) { + self._join(); + } + + /// The internal version of `join` that only really takes a mutable reference to allow `join`ing during `Drop`. + fn _join(&mut self) { + let mut plots = vec![]; + ::std::mem::swap(&mut self.plots, &mut plots); + for t in plots { let _ = t.join(); } } } +impl Drop for Plotter { + fn drop(&mut self) { + self._join(); + } +} + #[cfg(test)] mod test { use super::*;