diff options
Diffstat (limited to 'src/main.rs')
| -rw-r--r-- | src/main.rs | 264 |
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] } } |