aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNathan Reiner <nathan@nathanreiner.xyz>2024-01-21 17:02:00 +0100
committerNathan Reiner <nathan@nathanreiner.xyz>2024-01-21 17:02:00 +0100
commitb9b32972afda261020dd207b4ea2b44b7b697b83 (patch)
treedba426aabaa569952e2aa0dda0f5e1e836bae55f
parent53a48a778af34bd25963881539e6f66f47dc5580 (diff)
add multiple functions to canvas
-rw-r--r--src/main.rs74
-rw-r--r--src/math/expression_function.rs26
-rw-r--r--src/ui/graph.rs74
-rw-r--r--src/ui/mod.rs8
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)
}