1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
extern crate graphics; extern crate opengl_graphics; extern crate piston; extern crate glutin_window; pub mod input; use glutin_window::GlutinWindow as Window; use piston::input::{UpdateEvent, RenderEvent, MouseCursorEvent, PressEvent, ReleaseEvent}; pub type Color = graphics::types::Color; pub struct Graphics<'a> { gl: &'a mut opengl_graphics::GlGraphics } const INVISIBLE: Color = [0.0; 4]; impl<'a> Graphics<'a> { pub fn rectangle<W: Into<f64>, H: Into<f64>>(&mut self, state: DrawState, width: W, height: H) { let coords = [0.0, 0.0, width.into(), height.into()]; let rect = graphics::rectangle::Rectangle { color: state.fill.unwrap_or(INVISIBLE), shape: graphics::rectangle::Shape::Square, border: state.stroke.and_then(|color| { Some(graphics::rectangle::Border { color, radius: state.stroke_width }) }) }; rect.draw(coords, &Default::default(), state.transform, self.gl); } pub fn ellipse<W: Into<f64>, H: Into<f64>>(&mut self, state: DrawState, width: W, height: H) { let coords = [0.0, 0.0, width.into(), height.into()]; let shape = graphics::ellipse::Ellipse { color: state.fill.unwrap_or(INVISIBLE), border: state.stroke.and_then(|color| { Some(graphics::ellipse::Border { color, radius: state.stroke_width }) }), resolution: state.smoothness }; shape.draw(coords, &Default::default(), state.transform, self.gl); } } pub struct DrawState { transform: graphics::math::Matrix2d, fill: Option<Color>, stroke: Option<Color>, stroke_width: f64, smoothness: graphics::types::Resolution } impl DrawState { fn new(transform: graphics::math::Matrix2d) -> Self { DrawState { transform, fill: None, stroke: None, stroke_width: 1.0, smoothness: 20 } } pub fn fill(mut self, color: Color) -> Self { self.fill = Some(color); self } pub fn stroke(mut self, color: Color) -> Self { self.stroke = Some(color); self } pub fn translate<X: Into<f64>, Y: Into<f64>>(mut self, x: X, y: Y) -> Self { use graphics::Transformed; self.transform = self.transform.trans(x.into(), y.into()); self } pub fn rotate<A: Into<f64>>(mut self, angle: A) -> Self { use graphics::Transformed; self.transform = self.transform.rot_rad(angle.into()); self } } pub struct Context<'a> { args: piston::input::RenderArgs, ctx: graphics::context::Context, buttons: &'a std::collections::HashSet<input::Button>, pub dt: f64, pub mouse_x: f64, pub mouse_y: f64 } impl<'a> Context<'a> { pub fn width(&self) -> f64 { self.args.width.into() } pub fn height(&self) -> f64 { self.args.height.into() } pub fn state(&self) -> DrawState { DrawState::new(self.ctx.transform) } pub fn is_pressed(&self, button: input::Button) -> bool { self.buttons.contains(&button) } } pub struct WindowBuilder { settings: piston::window::WindowSettings, draw: Option<Box<FnMut(Graphics, Context) -> ()>> } const OPENGL_VERSION: opengl_graphics::OpenGL = opengl_graphics::OpenGL::V2_1; impl WindowBuilder { pub fn run(mut self) { let mut window: Window = self.settings.build().unwrap(); let mut gl = opengl_graphics::GlGraphics::new(OPENGL_VERSION); let mut events = piston::event_loop::Events::new(piston::event_loop::EventSettings::new()); let mut render_dt = 0.0; let mut mouse_x = 0.0; let mut mouse_y = 0.0; let mut buttons = std::collections::HashSet::new(); while let Some(e) = events.next(&mut window) { if let Some(r) = e.render_args() { if let Some(ref mut draw) = self.draw { gl.draw(r.viewport(), |c, glo| { let ctx = Context { args: r, ctx: c, dt: render_dt, mouse_x, mouse_y, buttons: &buttons }; render_dt = 0.0; let graphics = Graphics { gl: glo }; draw(graphics, ctx); }); } } if let Some(u) = e.update_args() { render_dt += u.dt; } if let Some(m) = e.mouse_cursor_args() { mouse_x = m[0]; mouse_y = m[1]; } if let Some(b) = e.press_args() { buttons.insert(b); } if let Some(b) = e.release_args() { buttons.remove(&b); } } } pub fn draw<F: 'static + FnMut(Graphics, Context) -> ()> (mut self, draw: F) -> Self { self.draw = Some(Box::new(draw)); self } } pub fn create_window(title: &str, width: u32, height: u32) -> WindowBuilder { WindowBuilder { settings: piston::window::WindowSettings::new( title, [width, height]) .opengl(OPENGL_VERSION) .srgb(false), draw: None } }