aboutsummaryrefslogtreecommitdiff
path: root/src/main.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs264
1 files changed, 238 insertions, 26 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]
}
}