diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/main.rs | 74 | ||||
| -rw-r--r-- | src/math/expression_function.rs | 26 | ||||
| -rw-r--r-- | src/ui/graph.rs | 74 | ||||
| -rw-r--r-- | src/ui/mod.rs | 8 |
4 files changed, 111 insertions, 71 deletions
diff --git a/src/main.rs b/src/main.rs index b21585f..a00d137 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,15 +2,14 @@ pub mod function_cache; pub mod math; pub mod ui; -use function_cache::FunctionCache; +use iced::widget::{ + button, canvas, container, pane_grid, responsive, text, text_input, Column, PaneGrid, row, +}; use iced::{executor, Renderer}; -use iced::widget::{canvas, container, pane_grid, responsive, text, text_input, PaneGrid}; use iced::{Application, Command, Element, Length, Settings, Theme}; -use math::expression_function::ExpressionFunction; -use std::sync::Mutex; use ui::graph::GraphCanvas; -use ui::Message; use ui::pane::{Pane, PaneKind}; +use ui::Message; pub fn main() -> iced::Result { Graph::run(Settings { @@ -21,7 +20,7 @@ pub fn main() -> iced::Result { struct Graph { panes: pane_grid::State<Pane>, - input_state: String, + func_vec: Vec<String>, graph_canvas: GraphCanvas, } @@ -38,11 +37,10 @@ impl Application for Graph { &pane, Pane::new(PaneKind::GraphPane), ); - let default_func = "f(x) = x^2".to_string(); ( Graph { - graph_canvas: GraphCanvas::new(&default_func), - input_state: default_func, + graph_canvas: GraphCanvas::new(), + func_vec: Vec::new(), panes, }, Command::none(), @@ -55,18 +53,23 @@ impl Application for Graph { fn update(&mut self, message: Message) -> Command<Message> { match message { - Message::InputChanged(s) => { - self.input_state = s; + Message::InputChanged(content, i) => { + self.func_vec[i] = content; + } + Message::AddFunction => { + self.func_vec.push(String::new()); + self.graph_canvas.push(""); } - Message::UpdateFunction => { - let func = ExpressionFunction::from_string( - self.input_state.clone(), - self.graph_canvas.ctx.operations(), - ); - self.graph_canvas.func = Mutex::new(FunctionCache::new(func)); + Message::RemoveFunction(i) => { + self.func_vec.remove(i); + self.graph_canvas.remove(i); + self.graph_canvas.clear() + } + Message::UpdateFunction(i) => { + self.graph_canvas.set(i, &self.func_vec[i]); self.graph_canvas.clear(); } - Message::UpdateScreen => { + Message::RefreshGraphCanvas => { self.graph_canvas.clear(); } Message::Resized(pane_grid::ResizeEvent { split, ratio }) => { @@ -79,19 +82,30 @@ impl Application for Graph { fn view(&self) -> Element<Message> { let pane_grid = PaneGrid::new(&self.panes, |_id, pane, _| { pane_grid::Content::new(responsive(move |_size| { - let child : Element<'_, Message, Renderer> = match pane.kind { - PaneKind::GraphPane => { - canvas(&self.graph_canvas) - .width(Length::Fill) - .height(Length::Fill).into() - } + let child: Element<'_, Message, Renderer> = match pane.kind { + PaneKind::GraphPane => canvas(&self.graph_canvas) + .width(Length::Fill) + .height(Length::Fill) + .into(), PaneKind::FunctionPane => { - text_input("x", &self.input_state) - .on_input(Message::InputChanged) - .on_submit(Message::UpdateFunction) - .padding(15) - .size(30).into() - }, + let mut flist = Column::new(); + for (i, f) in self.func_vec.iter().enumerate() { + flist = flist.push( + row![ + text_input("f(x) = x^2", f) + .on_input(move |s| Message::InputChanged(s, i)) + .on_submit(Message::UpdateFunction(i)), + button("R").on_press(Message::RemoveFunction(i)) + ].padding(5) + ); + } + flist = flist.push( + button("+") + .width(Length::Fill) + .on_press(Message::AddFunction), + ); + flist.into() + } _ => text("").into(), }; diff --git a/src/math/expression_function.rs b/src/math/expression_function.rs index 44f83d1..b9a480f 100644 --- a/src/math/expression_function.rs +++ b/src/math/expression_function.rs @@ -17,19 +17,23 @@ pub struct ExpressionFunction { } impl ExpressionFunction { - pub fn from_string(str: String, operations: &Vec<Operation>) -> Self { + pub fn from_string(str: String, operations: &Vec<Operation>) -> Result<Self, ()> { let str = str.replace(' ', ""); - let (lhs, expr) = str.split_once('=').unwrap(); - let (name, a) = lhs.split_once('(').unwrap(); - let args: Vec<String> = a[0..a.len() - 1] - .split(',') - .map(|s| s.to_string()) - .collect(); - Self { - expr: Expression::from_string(expr, operations), - name: name.to_string(), - args, + if let Some((lhs, expr)) = str.split_once('=') { + if let Some((name, a)) = lhs.split_once('(') { + let args: Vec<String> = a[0..a.len() - 1] + .split(',') + .map(|s| s.to_string()) + .collect(); + + return Ok(Self { + expr: Expression::from_string(expr, operations), + name: name.to_string(), + args, + }) + } } + Err(()) } pub fn name(&self) -> &str { diff --git a/src/ui/graph.rs b/src/ui/graph.rs index 63e1c43..83d90f8 100644 --- a/src/ui/graph.rs +++ b/src/ui/graph.rs @@ -16,17 +16,16 @@ use crate::{ #[derive(Default)] pub struct GraphCanvas { graph: Cache, - pub func: Mutex<FunctionCache>, - pub ctx: Context, + funcs: Mutex<Vec<Option<FunctionCache>>>, + ctx: Context, } impl GraphCanvas { - pub fn new(fstr: &String) -> Self { + pub fn new() -> Self { let ctx = Context::commonsense(); - let func = ExpressionFunction::from_string(fstr.clone(), ctx.operations()); Self { graph: Cache::default(), - func: Mutex::new(FunctionCache::new(func)), + funcs: Mutex::new(Vec::new()), ctx, } } @@ -34,6 +33,26 @@ impl GraphCanvas { pub fn clear(&self) { self.graph.clear(); } + + pub fn push(&mut self, f: &str) { + if let Ok(f) = ExpressionFunction::from_string(f.to_string(), self.ctx.operations()) { + self.funcs.lock().unwrap().push(Some(FunctionCache::new(f))); + } else { + self.funcs.lock().unwrap().push(None); + } + } + + pub fn set(&mut self, i: usize, f: &str) { + if let Ok(f) = ExpressionFunction::from_string(f.to_string(), self.ctx.operations()) { + self.funcs.lock().unwrap()[i] = Some(FunctionCache::new(f)); + } else { + self.funcs.lock().unwrap().push(None); + } + } + + pub fn remove(&mut self, i: usize) { + self.funcs.lock().unwrap().remove(i); + } } #[derive(Copy, Clone)] @@ -190,28 +209,29 @@ impl canvas::Program<Message, Renderer> for GraphCanvas { }); let step = (step / 10).max(1); - let mut y1 = self - .func + + for func in self + .funcs .lock() .unwrap() - .eval(Complex::new(rect.x as f64, 0.0), &self.ctx) - .real as f32; - for x in (rect.x as i64..(rect.x + rect.width) as i64).step_by(step) { - for d in 0..10 { - let d = d as f32 / 10.0; - let x1 = x as f32 + d * step as f32; - let x2 = x as f32 + (d + 0.1) * step as f32; - 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()) + .iter_mut() + .filter(|f| f.is_some()) + .map(|f| f.as_mut().unwrap()) + { + let mut y1 = func.eval(Complex::new(rect.x as f64, 0.0), &self.ctx).real as f32; + for x in (rect.x as i64..(rect.x + rect.width) as i64).step_by(step) { + for d in 0..10 { + let d = d as f32 / 10.0; + let x1 = x as f32 + d * step as f32; + let x2 = x as f32 + (d + 0.1) * step as f32; + let y2 = func.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; } - y1 = y2; } } }); @@ -232,7 +252,7 @@ impl canvas::Program<Message, Renderer> for GraphCanvas { 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)); + return (event::Status::Captured, Some(Message::RefreshGraphCanvas)); } if let canvas::Event::Mouse(mouse::Event::CursorMoved { position }) = event { @@ -242,7 +262,7 @@ impl canvas::Program<Message, Renderer> for GraphCanvas { state.center.y += position.y - lp.y; } state.last_position = Some(position); - return (event::Status::Captured, Some(Message::UpdateScreen)); + return (event::Status::Captured, Some(Message::RefreshGraphCanvas)); } } @@ -259,7 +279,7 @@ impl canvas::Program<Message, Renderer> for GraphCanvas { state.scale *= 1.1; } state.scale = state.scale.max(0.000001); - return (event::Status::Captured, Some(Message::UpdateScreen)); + return (event::Status::Captured, Some(Message::RefreshGraphCanvas)); } _ => {} } diff --git a/src/ui/mod.rs b/src/ui/mod.rs index b7c11fb..9ea4662 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -5,8 +5,10 @@ pub mod pane; #[derive(Debug, Clone)] pub enum Message { - InputChanged(String), - UpdateFunction, - UpdateScreen, + InputChanged(String, usize), + AddFunction, + RemoveFunction(usize), + UpdateFunction(usize), + RefreshGraphCanvas, Resized(ResizeEvent) } |