diff options
| author | Nathan Reiner <nathan@nathanreiner.xyz> | 2024-01-20 22:37:48 +0100 |
|---|---|---|
| committer | Nathan Reiner <nathan@nathanreiner.xyz> | 2024-01-20 22:37:48 +0100 |
| commit | 24ebe795e0ebf9dfc3cf2fafa609d8dc573a80a6 (patch) | |
| tree | 3e63187af686279e91d8e2686374b9e63d640d44 /src/main.rs | |
| parent | c5ab96ffc8e0ffa97b1bd7e72894ea39433b053f (diff) | |
make zooming and scrolling with translate and scale of canvas
Diffstat (limited to 'src/main.rs')
| -rw-r--r-- | src/main.rs | 266 |
1 files changed, 13 insertions, 253 deletions
diff --git a/src/main.rs b/src/main.rs index 7521440..d2effe6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,24 +1,17 @@ pub mod function_cache; pub mod math; +pub mod ui; use function_cache::FunctionCache; -use iced::event; use iced::executor; -use iced::mouse; -use iced::mouse::Interaction; -use iced::widget::canvas::{stroke, Cache, Geometry, LineCap, Path, Stroke}; use iced::widget::column; use iced::widget::text_input; use iced::widget::{canvas, container}; -use iced::{ - Application, Color, Command, Element, Length, Point, Rectangle, Renderer, Settings, Theme, - Vector, -}; -use math::context::Context; +use iced::{Application, Command, Element, Length, Settings, Theme}; use math::expression_function::ExpressionFunction; use std::sync::Mutex; - -use crate::math::complex::Complex; +use ui::graph::GraphCanvas; +use ui::Message; pub fn main() -> iced::Result { Graph::run(Settings { @@ -29,17 +22,8 @@ pub fn main() -> iced::Result { #[derive(Default)] struct Graph { - graph: Cache, - func: Mutex<FunctionCache>, - ctx: Context, input_state: String, -} - -#[derive(Debug, Clone)] -enum Message { - InputChanged(String), - UpdateFunction, - UpdateScreen, + graph_canvas: GraphCanvas, } impl Application for Graph { @@ -49,13 +33,11 @@ impl Application for Graph { type Flags = (); fn new(_flags: ()) -> (Self, Command<Message>) { - let ctx = Context::commonsense(); - let func = ExpressionFunction::from_string("f(x) = x^2".to_string(), ctx.operations()); + let default_func = "f(x) = x^2".to_string(); ( Graph { - input_state: "f(x) = x^2".to_string(), - func: Mutex::new(FunctionCache::new(func)), - ctx, + graph_canvas: GraphCanvas::new(&default_func), + input_state: default_func, ..Default::default() }, Command::none(), @@ -74,13 +56,13 @@ impl Application for Graph { Message::UpdateFunction => { let func = ExpressionFunction::from_string( self.input_state.clone(), - self.ctx.operations(), + self.graph_canvas.ctx.operations(), ); - self.func = Mutex::new(FunctionCache::new(func)); - self.graph.clear(); + self.graph_canvas.func = Mutex::new(FunctionCache::new(func)); + self.graph_canvas.clear(); } Message::UpdateScreen => { - self.graph.clear(); + self.graph_canvas.clear(); } } Command::none() @@ -92,7 +74,7 @@ impl Application for Graph { .on_submit(Message::UpdateFunction) .padding(15) .size(30); - let canvas = canvas(self as &Self) + let canvas = canvas(&self.graph_canvas) .width(Length::Fill) .height(Length::Fill); @@ -104,225 +86,3 @@ impl Application for Graph { column![input, container].into() } } - -#[derive(Copy, Clone)] -struct MouseState { - interaction: Interaction, - center: Point, - last_position: Option<Point>, - scale: f32, -} - -impl MouseState { - fn map_coords(&self, x: f32, y: f32) -> Point { - Point::new( - (self.center.x + x) * self.scale, - (self.center.y - y) * self.scale, - ) - } -} - -impl Default for MouseState { - fn default() -> Self { - Self { - interaction: Interaction::default(), - center: Point::default(), - last_position: None, - scale: 50.0, - } - } -} - -impl canvas::Program<Message, Renderer> for Graph { - type State = MouseState; - - fn draw( - &self, - state: &Self::State, - renderer: &Renderer, - _theme: &Theme, - bounds: Rectangle, - _cursor: mouse::Cursor, - ) -> Vec<Geometry> { - let graph = self.graph.draw(renderer, bounds.size(), |frame| { - let unitline = Stroke { - width: 2.0, - style: stroke::Style::Solid(Color::new(0.0, 0.0, 0.0, 1.0)), - line_cap: LineCap::Round, - ..Stroke::default() - }; - let unitline_whole = Stroke { - width: 1.0, - style: stroke::Style::Solid(Color::new(0.5, 0.5, 0.5, 1.0)), - line_cap: LineCap::Round, - ..Stroke::default() - }; - let zeroline = Stroke { - width: 3.0, - style: stroke::Style::Solid(Color::new(0.0, 0.0, 0.8, 1.0)), - line_cap: LineCap::Round, - ..Stroke::default() - }; - - let graphline = Stroke { - width: 3.0, - style: stroke::Style::Solid(Color::new(0.8, 0.0, 0.0, 1.0)), - line_cap: LineCap::Round, - ..Stroke::default() - }; - - frame.translate(Vector { - x: frame.center().x, - y: frame.center().y, - }); - - frame.with_save(|frame| { - let start_y = ((state.center.y - frame.height() / 2.0) / state.scale) as i64 - * state.scale as i64; - let start_x = ((state.center.x - frame.width() / 2.0) / state.scale) as i64 - * state.scale as i64; - let end_y = ((state.center.y + frame.height() / 2.0) / state.scale) as i64 - * state.scale as i64; - let end_x = ((state.center.x + frame.width() / 2.0) / state.scale) as i64 - * state.scale as i64; - - for y in (start_y..end_y + state.scale as i64).step_by(1) { - let line = Path::line( - state.map_coords(start_x as f32, y as f32), - state.map_coords(end_x as f32, y as f32), - ); - frame.stroke(&line, unitline_whole.clone()); - } - - for x in (start_x..end_x + state.scale as i64).step_by(1) { - let line = Path::line( - state.map_coords(x as f32, start_y as f32), - state.map_coords(x as f32, end_y as f32), - ); - frame.stroke(&line, unitline_whole.clone()); - } - - let start_y_100 = ((state.center.y - frame.height() / 2.0) / state.scale * 10.0) - as i64 - * 10 - * state.scale as i64; - let start_x_100 = ((state.center.x - frame.width() / 2.0) / state.scale * 10.0) - as i64 - * 10 - * state.scale as i64; - - for y in (start_y_100..end_y).step_by(10) { - let line = Path::line( - state.map_coords(start_x as f32, y as f32), - state.map_coords(end_x as f32, y as f32), - ); - frame.stroke(&line, unitline.clone()); - } - - for x in (start_x_100..end_x).step_by(10) { - let line = Path::line( - state.map_coords(x as f32, start_y as f32), - state.map_coords(x as f32, end_y as f32), - ); - frame.stroke(&line, unitline.clone()); - } - - let line = Path::line( - state.map_coords(0.0, start_y as f32), - state.map_coords(0.0, end_y as f32), - ); - frame.stroke(&line, zeroline.clone()); - - let line = Path::line( - state.map_coords(start_x as f32, 0.0), - state.map_coords(end_x as f32, 0.0), - ); - frame.stroke(&line, zeroline.clone()); - let mut y1 = self - .func - .lock() - .unwrap() - .eval(Complex::new(start_x as f64, 0.0), &self.ctx) - .real as f32; - for x in start_x..end_x { - for d in 0..10 { - let d = d as f32 / 10.0; - let x1 = x as f32 + d; - let x2 = x as f32 + d + 0.1; - let y2 = self - .func - .lock() - .unwrap() - .eval(Complex::new(x2 as f64, 0.0), &self.ctx) - .real as f32; - if y1.is_finite() && !y1.is_nan() && y2.is_finite() && !y2.is_nan() { - let line = - Path::line(state.map_coords(x1, y1), state.map_coords(x2, y2)); - frame.stroke(&line, graphline.clone()) - } - y1 = y2; - } - } - }); - }); - - vec![graph] - } - - fn update( - &self, - state: &mut Self::State, - event: canvas::Event, - _bounds: Rectangle, - cursor: iced::advanced::mouse::Cursor, - ) -> (canvas::event::Status, Option<Message>) { - if let canvas::Event::Mouse(mouse::Event::ButtonPressed(_)) = event { - state.interaction = Interaction::Grabbing; - state.last_position = cursor.position(); - return (event::Status::Captured, Some(Message::UpdateScreen)); - } - - if let canvas::Event::Mouse(mouse::Event::CursorMoved { position }) = event { - if state.interaction == Interaction::Grabbing { - if let Some(lp) = state.last_position { - state.center.x += (position.x - lp.x) / state.scale; - state.center.y += (position.y - lp.y) / state.scale; - } - state.last_position = Some(position); - return (event::Status::Captured, Some(Message::UpdateScreen)); - } - } - - if let canvas::Event::Mouse(mouse::Event::ButtonReleased(_)) = event { - state.interaction = Interaction::Pointer; - } - - if let canvas::Event::Mouse(mouse::Event::WheelScrolled { delta }) = event { - match delta { - mouse::ScrollDelta::Lines { y, .. } => { - if y > 0.0 { - state.scale /= 1.1; - } else { - state.scale *= 1.1; - } - state.scale = state.scale.max(0.000001); - return (event::Status::Captured, Some(Message::UpdateScreen)); - } - _ => {} - } - } - (event::Status::Ignored, None) - } - - fn mouse_interaction( - &self, - state: &Self::State, - _bounds: Rectangle, - _cursor: mouse::Cursor, - ) -> mouse::Interaction { - match state.interaction { - Interaction::Grabbing => mouse::Interaction::Grabbing, - _ => mouse::Interaction::Grab, - } - } -} |