aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main.rs264
-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.rs8
-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