diff --git a/README.md b/README.md index 2752819..0c1868f 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ A graphing calculator written in C++ with an ImGui user interface rendered with ## How to get started The codebase of this repo is quite big, but the file that is of interest to you as a contributor is [ -`src/code/Core/Application.cpp`](src/core/Core/Application.cpp). It contains the code for parsing expressions +`src/core/Core/Application.cpp`](src/core/Core/Application.cpp). It contains the code for parsing expressions (using [ExprTk](https://github.com/ArashPartow/exprtk)) and the rendering logic for calculating the graph's points. Most, if not all features can be implemented by only modifying this one file. diff --git a/src/core/Core/Application.cpp b/src/core/Core/Application.cpp index 23764d9..aab4c77 100644 --- a/src/core/Core/Application.cpp +++ b/src/core/Core/Application.cpp @@ -212,18 +212,88 @@ ExitStatus App::Application::run() { } } - // check for implicit form: f(x,y) = g(x,y) - if (!plotted) { - size_t equals_pos = findTopLevelEquals(func_str); + // check for inequality + if (!plotted && hasInequalityOperator(func_str)) { + double x = 0.0, y = 0.0; + exprtk::symbol_table symbol_table; + symbol_table.add_constants(); + addConstants(symbol_table); + symbol_table.add_variable("x", x); + symbol_table.add_variable("y", y); + + exprtk::expression expression; + expression.register_symbol_table(symbol_table); + + exprtk::parser parser; - if (equals_pos != std::string::npos) { - // split into LHS and RHS - std::string lhs = trim(func_str.substr(0, equals_pos)); - std::string rhs = trim(func_str.substr(equals_pos + 1)); + if (parser.compile(func_str, expression)) { + // grid parameters + const double x_min = -canvas_sz.x / (2 * zoom); + const double x_max = canvas_sz.x / (2 * zoom); + const double y_min = -canvas_sz.y / (2 * zoom); + const double y_max = canvas_sz.y / (2 * zoom); - // create expression: LHS - RHS - std::string implicit_expr = "(" + lhs + ") - (" + rhs + ")"; + // adaptive step size with performance limit + const double step = std::max(0.025, 1.5 / zoom); + const ImU32 inequality_color = IM_COL32(100, 150, 255, 180); + const float dot_size = std::max(1.5f, zoom / 60.0f); + + for (y = y_min; y <= y_max; y += step) { + for (x = x_min; x <= x_max; x += step) { + + // if expression is true, plot the point + if (expression.value() == 1.0) { + ImVec2 screen_pos(origin.x + static_cast(x * zoom), + origin.y - static_cast(y * zoom)); + draw_list->AddCircleFilled(screen_pos, dot_size, inequality_color); + } + } + } + + plotted = true; + } + } + + // check for implicit form: f(x,y) = g(x,y) + if (!plotted) { + size_t equals_pos = findTopLevelEquals(func_str); + bool has_double_equals = hasEqualsEqualsOperator(func_str); + + if (equals_pos != std::string::npos || has_double_equals) { + + std::string implicit_expr; + + if (has_double_equals) { + // Handle == operator + std::string temp_str = func_str; + int depth = 0; + size_t eq_pos = std::string::npos; + + for (size_t i = 0; i < temp_str.size() - 1; ++i) { + char c = temp_str[i]; + if (c == '(') ++depth; + else if (c == ')') --depth; + else if (depth == 0 && c == '=' && temp_str[i+1] == '=') { + eq_pos = i; + break; + } + } + + if (eq_pos != std::string::npos) { + std::string lhs = trim(temp_str.substr(0, eq_pos)); + std::string rhs = trim(temp_str.substr(eq_pos + 2)); // +2 to skip == + implicit_expr = "(" + lhs + ") - (" + rhs + ")"; + } + } else { + // Handle = operator + std::string lhs = trim(func_str.substr(0, equals_pos)); + std::string rhs = trim(func_str.substr(equals_pos + 1)); + implicit_expr = "(" + lhs + ") - (" + rhs + ")"; + } + + if (!implicit_expr.empty()) { + // setup exprtk with x and y variables double x = 0.0, y = 0.0; exprtk::symbol_table symbolTable; @@ -298,7 +368,8 @@ ExitStatus App::Application::run() { } } - plotted = true; + plotted = true; + } } } } diff --git a/src/core/Core/funcs.hpp b/src/core/Core/funcs.hpp index d945fc5..128f6b4 100644 --- a/src/core/Core/funcs.hpp +++ b/src/core/Core/funcs.hpp @@ -56,5 +56,47 @@ static size_t findTopLevelEquals(const std::string& str) { return std::string::npos; } +//function to check for == operator specifically +static bool hasEqualsEqualsOperator(const std::string& str) { + int depth = 0; + for (size_t i = 0; i < str.size(); ++i) { + char c = str[i]; + + if (c == '(') { + ++depth; + } else if (c == ')') { + --depth; + } else if (depth == 0) { + if (c == '=' && i + 1 < str.size() && str[i+1] == '=') { + return true; + } + } + } + return false; +} + +// function to check if expression contains inequality operators +static bool hasInequalityOperator(const std::string& str) { + int depth = 0; + for (size_t i = 0; i < str.size(); ++i) { + char c = str[i]; + + if (c == '(') { + ++depth; + } else if (c == ')') { + --depth; + } else if (depth == 0) { + // check for <, >, <=, >=, != + if (c == '<' || c == '>') { + return true; + } + if (c == '!' && i + 1 < str.size() && str[i+1] == '=') { + return true; + } + } + } + return false; +} + #endif // IMGRAPH_FUNCS_HPP