aboutsummaryrefslogtreecommitdiff
path: root/src/main.rs
diff options
context:
space:
mode:
authorNathan Reiner <nathan@nathanreiner.xyz>2024-01-20 22:37:48 +0100
committerNathan Reiner <nathan@nathanreiner.xyz>2024-01-20 22:37:48 +0100
commit24ebe795e0ebf9dfc3cf2fafa609d8dc573a80a6 (patch)
tree3e63187af686279e91d8e2686374b9e63d640d44 /src/main.rs
parentc5ab96ffc8e0ffa97b1bd7e72894ea39433b053f (diff)
make zooming and scrolling with translate and scale of canvas
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs266
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,
- }
- }
-}