diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/main.rs | 264 | ||||
| -rw-r--r-- | src/math/commonsense.rs (renamed from src/commonsense.rs) | 20 | ||||
| -rw-r--r-- | src/math/complex.rs (renamed from src/complex.rs) | 0 | ||||
| -rw-r--r-- | src/math/context.rs (renamed from src/context.rs) | 16 | ||||
| -rw-r--r-- | src/math/expression.rs (renamed from src/expression.rs) | 13 | ||||
| -rw-r--r-- | src/math/expression_function.rs (renamed from src/expression_function.rs) | 9 | ||||
| -rw-r--r-- | src/math/function.rs (renamed from src/function.rs) | 2 | ||||
| -rw-r--r-- | src/math/mod.rs | 8 | ||||
| -rw-r--r-- | src/math/operation.rs (renamed from src/operation.rs) | 2 | ||||
| -rw-r--r-- | src/math/string.rs (renamed from src/string.rs) | 0 |
10 files changed, 286 insertions, 48 deletions
diff --git a/src/main.rs b/src/main.rs index 5f42b10..8876f16 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,34 +1,246 @@ -pub mod complex; -pub mod context; -pub mod expression; -pub mod function; -pub mod operation; -pub mod string; -pub mod commonsense; -pub mod expression_function; +pub mod math; -use std::collections::HashMap; +use iced::executor; +use iced::mouse; +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 math::expression_function::ExpressionFunction; +use math::function::Function; -use complex::Complex; -use context::Context; -use expression::Expression; -use function::Function; -use operation::Operation; -use expression_function::ExpressionFunction; +use crate::math::complex::Complex; +use crate::math::function::FunctionArgument; -fn main() { - let func = ExpressionFunction::from_string("f(x) = x^2".to_string()); +pub fn main() -> iced::Result { + Graph::run(Settings { + antialiasing: true, + ..Settings::default() + }) +} + +#[derive(Default)] +struct Graph { + graph: Cache, + center_x: f32, + center_y: f32, + scale: f32, + func: ExpressionFunction, + ctx: Context, + input_state: String, +} + +#[derive(Debug, Clone)] +enum Message { + InputChanged(String), + FunctionRun, +} + +impl Graph { + fn map_coords(&self, x: f32, y: f32) -> Point { + Point::new( + (self.center_x + x) * self.scale, + (self.center_y - y) * self.scale, + ) + } +} + +impl Application for Graph { + type Executor = executor::Default; + type Message = Message; + type Theme = Theme; + type Flags = (); + + fn new(_flags: ()) -> (Self, Command<Message>) { + ( + Graph { + input_state: "f(x) = x^2".to_string(), + func: ExpressionFunction::from_string("f(x) = x^2".to_string()), + ctx: Context::commonsense(), + scale: 50.0, + ..Default::default() + }, + Command::none(), + ) + } + + fn title(&self) -> String { + String::from("MathEval") + } + + fn update(&mut self, message: Message) -> Command<Message> { + match message { + Message::InputChanged(s) => { + self.input_state = s; + } + Message::FunctionRun => { + self.func = ExpressionFunction::from_string(self.input_state.clone()); + self.graph.clear(); + } + } + Command::none() + } + + fn view(&self) -> Element<Message> { + let input = text_input("x", &self.input_state) + .on_input(Message::InputChanged) + .on_submit(Message::FunctionRun) + .padding(15) + .size(30); + let canvas = canvas(self as &Self) + .width(Length::Fill) + .height(Length::Fill); + + let container = container(canvas) + .width(Length::Fill) + .height(Length::Fill) + .padding(20); + + column![input, container].into() + } +} + +impl<Message> canvas::Program<Message, Renderer> for Graph { + type State = (); + + 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::Square, + ..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::Square, + ..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::Square, + ..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::Square, + ..Stroke::default() + }; + + frame.translate(Vector { + x: frame.center().x, + y: frame.center().y, + }); + + frame.with_save(|frame| { + let start_y = ((self.center_y - frame.height() / 2.0) / self.scale) as i64 + * self.scale as i64; + let start_x = + ((self.center_x - frame.width() / 2.0) / self.scale) as i64 * self.scale as i64; + let end_y = ((self.center_y + frame.height() / 2.0) / self.scale) as i64 + * self.scale as i64; + let end_x = + ((self.center_x + frame.width() / 2.0) / self.scale) as i64 * self.scale as i64; + + for y in (start_y..end_y + self.scale as i64).step_by(1) { + let line = Path::line( + self.map_coords(start_x as f32, y as f32), + self.map_coords(end_x as f32, y as f32), + ); + frame.stroke(&line, unitline_whole.clone()); + } + + for x in (start_x..end_x + self.scale as i64).step_by(1) { + let line = Path::line( + self.map_coords(x as f32, start_y as f32), + self.map_coords(x as f32, end_y as f32), + ); + frame.stroke(&line, unitline_whole.clone()); + } + + let start_y_100 = ((self.center_y - frame.height() / 2.0) / self.scale * 10.0) + as i64 + * 10 + * self.scale as i64; + let start_x_100 = ((self.center_x - frame.width() / 2.0) / self.scale * 10.0) + as i64 + * 10 + * self.scale as i64; + + for y in (start_y_100..end_y).step_by(10) { + let line = Path::line( + self.map_coords(start_x as f32, y as f32), + self.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( + self.map_coords(x as f32, start_y as f32), + self.map_coords(x as f32, end_y as f32), + ); + frame.stroke(&line, unitline.clone()); + } + + let line = Path::line( + self.map_coords(0.0, start_y as f32), + self.map_coords(0.0, end_y as f32), + ); + frame.stroke(&line, zeroline.clone()); - let expr = "f(4 + 3i)"; + let line = Path::line( + self.map_coords(start_x as f32, 0.0), + self.map_coords(end_x as f32, 0.0), + ); + frame.stroke(&line, zeroline.clone()); - let ctx: Context = Context::new() - .with_operations(commonsense_operations!{}) - .with_functions(commonsense_functions!{func.name() => func}) - .with_variables(commonsense_variables!{}); + 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 y1 = self + .func + .eval( + FunctionArgument::new(vec![Complex::new(x1 as f64, 0.0)]), + &self.ctx, + ) + .unwrap() + .real as f32; + let x2 = x as f32 + d + 0.1; + let y2 = self + .func + .eval( + FunctionArgument::new(vec![Complex::new(x2 as f64, 0.0)]), + &self.ctx, + ) + .unwrap() + .real as f32; + if y1.is_finite() && !y1.is_nan() && y2.is_finite() && !y2.is_nan() { + let line = Path::line(self.map_coords(x1, y1), self.map_coords(x2, y2)); + frame.stroke(&line, graphline.clone()) + } + } + } + }); + }); - let value = Expression::from_string(expr); - match value.evaluate(&ctx) { - Ok(res) => println!("{} = {}", expr, res), - Err(err) => println!("Error: {}", err), + vec![graph] } } diff --git a/src/commonsense.rs b/src/math/commonsense.rs index c42da00..e75ccc5 100644 --- a/src/commonsense.rs +++ b/src/math/commonsense.rs @@ -1,4 +1,4 @@ -use crate::complex::Complex; +use crate::math::complex::Complex; pub fn add(a: Complex, b: Complex) -> Complex { a + b @@ -43,10 +43,10 @@ macro_rules! commonsense_functions { use std::sync::Arc; let mut h : HashMap<String, Arc<dyn Function>> = HashMap::new(); h.extend(functions!{ - "sqrt" => &$crate::commonsense::sqrt, - "sin" => &$crate::commonsense::sin, - "cos" => &$crate::commonsense::cos, - "tan" => &$crate::commonsense::tan + "sqrt" => &$crate::math::commonsense::sqrt, + "sin" => &$crate::math::commonsense::sin, + "cos" => &$crate::math::commonsense::cos, + "tan" => &$crate::math::commonsense::tan }); $( h.insert($x.to_string(), Arc::new($y)); @@ -60,11 +60,11 @@ macro_rules! commonsense_functions { macro_rules! commonsense_operations { {$($x:expr => $y:expr), *} => { vec![ - Operation::new('+', $crate::commonsense::add), - Operation::new('-', $crate::commonsense::sub), - Operation::new('*', $crate::commonsense::mul), - Operation::new('/', $crate::commonsense::div), - Operation::new('^', $crate::commonsense::pow) + Operation::new('+', $crate::math::commonsense::add), + Operation::new('-', $crate::math::commonsense::sub), + Operation::new('*', $crate::math::commonsense::mul), + Operation::new('/', $crate::math::commonsense::div), + Operation::new('^', $crate::math::commonsense::pow) $( Operation::new($x, Box::new($y)), )*] diff --git a/src/complex.rs b/src/math/complex.rs index 5b95df4..5b95df4 100644 --- a/src/complex.rs +++ b/src/math/complex.rs diff --git a/src/context.rs b/src/math/context.rs index 409600f..cb0bca0 100644 --- a/src/context.rs +++ b/src/math/context.rs @@ -1,8 +1,9 @@ -use crate::complex::Complex; -use crate::function::Function; -use crate::operation::Operation; -use std::sync::Arc; +use crate::math::complex::Complex; +use crate::math::function::Function; +use crate::math::operation::Operation; +use crate::{commonsense_functions, commonsense_operations, commonsense_variables, functions, variables}; use std::collections::HashMap; +use std::sync::Arc; #[derive(Default, Clone)] pub struct Context { @@ -16,6 +17,13 @@ impl Context { Self::default() } + pub fn commonsense() -> Self { + Self::default() + .with_operations(commonsense_operations!{}) + .with_functions(commonsense_functions!{}) + .with_variables(commonsense_variables!{}) + } + pub fn with_operations(mut self, ops: Vec<Operation>) -> Self { self.ops = ops; self diff --git a/src/expression.rs b/src/math/expression.rs index c7574a9..9d6d4f3 100644 --- a/src/expression.rs +++ b/src/math/expression.rs @@ -1,8 +1,9 @@ -use crate::complex::Complex; -use crate::context::Context; -use crate::function::FunctionArgument; -use crate::string::{ContainsAndSkipBrackets, SplitMatchingBracket}; +use crate::math::complex::Complex; +use crate::math::context::Context; +use crate::math::function::FunctionArgument; +use crate::math::string::{ContainsAndSkipBrackets, SplitMatchingBracket}; +#[derive(Default)] pub struct Expression { repr: String, } @@ -14,6 +15,10 @@ impl Expression { } } + pub fn content(&self) -> &str { + &self.repr + } + pub fn evaluate(&self, context: &Context) -> Result<Complex, String> { let (repr, oprepr) = { if self.repr.starts_with('(') { diff --git a/src/expression_function.rs b/src/math/expression_function.rs index 8cafd76..20f4816 100644 --- a/src/expression_function.rs +++ b/src/math/expression_function.rs @@ -1,13 +1,14 @@ -use std::{iter::zip}; +use std::iter::zip; use std::sync::Arc; -use crate::{ +use crate::math::{ complex::Complex, context::Context, expression::Expression, function::{Function, FunctionArgument, EmptyFunction} }; +#[derive(Default)] pub struct ExpressionFunction { expr: Expression, name: String, @@ -33,6 +34,10 @@ impl ExpressionFunction { pub fn name(&self) -> &str { &self.name } + + pub fn content(&self) -> &str { + &self.expr.content() + } } impl Function for ExpressionFunction { diff --git a/src/function.rs b/src/math/function.rs index 4daebf4..bd782f3 100644 --- a/src/function.rs +++ b/src/math/function.rs @@ -1,4 +1,4 @@ -use crate::{complex::Complex, context::Context}; +use crate::math::{complex::Complex, context::Context}; pub struct FunctionArgument { args : Vec<Complex>, diff --git a/src/math/mod.rs b/src/math/mod.rs new file mode 100644 index 0000000..7b9c17c --- /dev/null +++ b/src/math/mod.rs @@ -0,0 +1,8 @@ +pub mod complex; +pub mod context; +pub mod expression; +pub mod function; +pub mod operation; +pub mod string; +pub mod commonsense; +pub mod expression_function; diff --git a/src/operation.rs b/src/math/operation.rs index 7eca774..bcf6510 100644 --- a/src/operation.rs +++ b/src/math/operation.rs @@ -1,4 +1,4 @@ -use crate::complex::Complex; +use crate::math::complex::Complex; pub type Operator = fn(Complex, Complex) -> Complex; diff --git a/src/string.rs b/src/math/string.rs index c8d719b..c8d719b 100644 --- a/src/string.rs +++ b/src/math/string.rs |