From d712c0dd90f87f8e84c8c7d4bbccefbf5792326f Mon Sep 17 00:00:00 2001 From: tovjemam Date: Fri, 31 Jan 2025 16:25:16 +0100 Subject: [PATCH] jej --- CMakeLists.txt | 12 +--- canvas_graph.cpp | 169 +++++++++++++++++++++++++++++------------------ canvas_graph.h | 14 ---- canvas_graph.hpp | 17 +++++ expression.hpp | 101 ++++++++++++++++++++++++++++ main.cpp | 78 +++++++++------------- shell.html | 67 ++++++++++++++----- 7 files changed, 308 insertions(+), 150 deletions(-) delete mode 100755 canvas_graph.h create mode 100755 canvas_graph.hpp create mode 100644 expression.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index da1552d..152240d 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,17 +11,7 @@ add_executable(GraphWeb ) if (CMAKE_SYSTEM_NAME STREQUAL Emscripten) set(CMAKE_EXECUTABLE_SUFFIX .html) - - # Specify the custom HTML shell - # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --shell-file ${CMAKE_SOURCE_DIR}/shell.html") - # set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --shell-file ${CMAKE_SOURCE_DIR}/shell.html") - - # target_compile_options(GraphWeb PRIVATE --shell-file ${CMAKE_SOURCE_DIR}/shell.html) - # target_link_options(GraphWeb PRIVATE ) - set_target_properties(GraphWeb PROPERTIES LINK_FLAGS "--bind -sEXPORTED_FUNCTIONS='[\"_main\", \"_draw_graph\"]' -sEXPORTED_RUNTIME_METHODS='[\"cwrap\", \"ccall\"]' --shell-file ${CMAKE_SOURCE_DIR}/shell.html") - # target_link_options(GraphWeb PRIVATE -) - - + set_target_properties(GraphWeb PROPERTIES LINK_FLAGS "--bind -sEXPORTED_FUNCTIONS='[\"_main\", \"_draw_graph\", \"_set_expr\", \"_set_num_exprs\", \"_print_errors\"]' -sEXPORTED_RUNTIME_METHODS='[\"cwrap\", \"ccall\"]' --shell-file ${CMAKE_SOURCE_DIR}/shell.html") endif() diff --git a/canvas_graph.cpp b/canvas_graph.cpp index 7557ffd..285cf6d 100755 --- a/canvas_graph.cpp +++ b/canvas_graph.cpp @@ -1,6 +1,4 @@ -extern "C" { -#include "canvas_graph.h" -} +#include "canvas_graph.hpp" #include @@ -31,11 +29,7 @@ void draw_grid(emscripten::val& ctx, const struct graph_range *range, double can // grid double big_range = std::max(range->xmax - range->xmin, range->ymax - range->ymin); double log_base = 2.0; - double grid_size = std::pow(log_base, std::floor(std::log(big_range * 0.05) / std::log(log_base))); - // double grid_size = std::pow(10.0, std::floor(std::log10(big_range * 0.1))); - - // if (grid_size < 0.1) - // grid_size = 0.1; + double grid_size = std::pow(log_base, std::floor(std::log(big_range * 0.02) / std::log(log_base))); static std::vector origin_lines; static std::vector bold_grid_lines; @@ -101,61 +95,10 @@ void draw_grid(emscripten::val& ctx, const struct graph_range *range, double can draw_lines(ctx, origin_lines, "#000000", 1.5); } -void canvas_generate_graph(const struct expr_node *node, const struct graph_range *range, const char *function) { - const val document = val::global("document"); - // val temp = document.call("getElementById", val("temp")); +void draw_expr_graph(emscripten::val& ctx, const Expression& expr, const struct graph_range *range, const char* style, double width, double canvas_width, double canvas_height) { - // std::string out; - - // for (int i = -10; i <= 10; ++i) { - // double x = i * 0.1; - // double y; - // eval_result res = node_eval(node, x, &y); - - // if (res == EVAL_OK) { - // out += std::to_string(y); - // out += "\n"; - // } - // } - - // temp.set("innerText", out); - - val canvas = document.call("getElementById", val("canvas")); - val ctx = canvas.call("getContext", val("2d")); - double canvas_width = canvas["width"].as(); - double canvas_height = canvas["height"].as(); - - // clear canvas - ctx.call("clearRect", val(0), val(0), val(canvas_width), val(canvas_height)); - - draw_grid(ctx, range, canvas_width, canvas_height); - - // ctx.set("strokeStyle", val("#888888")); - // ctx.set("lineWidth", val(1.0)); - // ctx.call("beginPath"); - - // double first_grid_x = (int)(range->xmin / grid_size) * grid_size; - // double grid_x; - // for (int i = 0; (grid_x = first_grid_x + i * grid_size) <= range->xmax; ++i) { - // double x_canvas = (grid_x - range->xmin) / (range->xmax - range->xmin) * canvas_width; - // ctx.call("moveTo", val(x_canvas), val(0)); - // ctx.call("lineTo", val(x_canvas), val(canvas_height)); - // } - - // double first_grid_y = (int)(range->ymin / grid_size) * grid_size; - // double grid_y; - // for (int i = 0; (grid_y = first_grid_y + i * grid_size) <= range->ymax; ++i) { - // double y_canvas = (range->ymax - grid_y) / (range->ymax - range->ymin) * canvas_height; - // ctx.call("moveTo", val(0), val(y_canvas)); - // ctx.call("lineTo", val(canvas_width), val(y_canvas)); - // } - - - // ctx.call("stroke"); - - // graph - ctx.set("strokeStyle", val("#000088")); - ctx.set("lineWidth", val(2.0)); + ctx.set("strokeStyle", val(style)); + ctx.set("lineWidth", val(width)); ctx.call("beginPath"); double segment_size = (range->xmax - range->xmin) / 1000.0; double first_x = (int)(range->xmin / segment_size) * segment_size; @@ -163,9 +106,10 @@ void canvas_generate_graph(const struct expr_node *node, const struct graph_rang bool use_moveto = true; for (int i = 0; (x = first_x + i * segment_size) <= range->xmax; ++i) { double y; - eval_result res = node_eval(node, x, &y); + // eval_result res = node_eval(node, x, &y); + bool res = expr.Evaluate(x, y); - if (res != EVAL_OK) { + if (!res) { use_moveto = true; continue; } @@ -183,4 +127,101 @@ void canvas_generate_graph(const struct expr_node *node, const struct graph_rang } ctx.call("stroke"); +} + +const char* const colors[] = { + "#000088", + "#880000", + "#008800", + "#880088", + "#008888", + "#888800", + "#000000", + "#888888", + "#444444", + "#ff0000", + "#00ff00", + "#0000ff", + "#ff00ff", + "#00ffff", + "#ffff00", + "#ffffff", +}; + +const size_t color_count = sizeof(colors) / sizeof(colors[0]); + +#define IDX_NOT_FOUND SIZE_MAX + +void canvas_generate_graph(const Expression* expr, size_t expr_count, const struct graph_range *range, double pointer_x, double pointer_y) { + const val document = val::global("document"); + + + val canvas = document.call("getElementById", val("canvas")); + val ctx = canvas.call("getContext", val("2d")); + double canvas_width = canvas["width"].as(); + double canvas_height = canvas["height"].as(); + + // clear canvas + ctx.call("clearRect", val(0), val(0), val(canvas_width), val(canvas_height)); + + ctx.set("font", val("12px Arial")); + + draw_grid(ctx, range, canvas_width, canvas_height); + + size_t closest_expr_idx = IDX_NOT_FOUND; + double closest_dist = std::numeric_limits::max(); + double closest_y_canvas = 0; + double closest_x_value = 0; + double closest_y_value = 0; + + for (size_t i = 0; i < expr_count; ++i) { + if (!expr[i].IsValid()) { + continue; + } + + double x = pointer_x * (range->xmax - range->xmin) / canvas_width + range->xmin; + double y; + bool res = expr[i].Evaluate(x, y); + + if (!res) { + continue; + } + + double y_canvas = (range->ymax - y) / (range->ymax - range->ymin) * canvas_height; + double dist = std::abs(y_canvas - pointer_y); + + if (dist > 50) { + continue; + } + + if (dist < closest_dist) { + closest_dist = dist; + closest_expr_idx = i; + closest_y_canvas = y_canvas; + closest_x_value = x; + closest_y_value = y; + } + } + + for (size_t i = 0; i < expr_count; ++i) { + if (!expr[i].IsValid()) { + continue; + } + + double width = 2.0; + if (i == closest_expr_idx) { + width = 2.5; + } + + draw_expr_graph(ctx, expr[i], range, colors[i % color_count], width, canvas_width, canvas_height); + } + + if (closest_expr_idx != IDX_NOT_FOUND) { + std::string text = "f(" + std::to_string(closest_x_value) + ") = " + std::to_string(closest_y_value); + ctx.set("fillStyle", val("#000000")); + ctx.call("beginPath"); + ctx.call("arc", val(pointer_x), val(closest_y_canvas), val(2.5), val(0), val(2 * M_PI)); + ctx.call("fill"); + ctx.call("fillText", val(text.c_str()), val(pointer_x), val(closest_y_canvas - 5)); + } } \ No newline at end of file diff --git a/canvas_graph.h b/canvas_graph.h deleted file mode 100755 index 10ddc75..0000000 --- a/canvas_graph.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef CANVAS_GRAPH_H -#define CANVAS_GRAPH_H - -#include -#include "tree.h" - -struct graph_range { - double xmin, xmax; - double ymin, ymax; -}; - -void canvas_generate_graph(const struct expr_node *node, const struct graph_range *range, const char *function); - -#endif /* CANVAS_GRAPH_H */ diff --git a/canvas_graph.hpp b/canvas_graph.hpp new file mode 100755 index 0000000..666b3ef --- /dev/null +++ b/canvas_graph.hpp @@ -0,0 +1,17 @@ +#ifndef CANVAS_GRAPH_H +#define CANVAS_GRAPH_H + +// #include +#include +// #include "tree.h" +#include "expression.hpp" + + +struct graph_range { + double xmin, xmax; + double ymin, ymax; +}; + +void canvas_generate_graph(const Expression* expr, size_t expr_count, const struct graph_range *range, double pointer_x, double pointer_y); + +#endif /* CANVAS_GRAPH_H */ diff --git a/expression.hpp b/expression.hpp new file mode 100644 index 0000000..ae40fad --- /dev/null +++ b/expression.hpp @@ -0,0 +1,101 @@ +#pragma once + +#include + +extern "C" { +#include "tree.h" +#include "parser.h" +} + +class Expression { + std::string m_expr_str; + struct parser m_parser; + struct expr_node* m_root; + +public: + Expression(const std::string& expr_str) { + m_root = nullptr; + SetExpression(expr_str); + } + + Expression() : Expression("") {} + + Expression(const Expression&) = delete; + Expression& operator=(const Expression&) = delete; + + Expression(Expression&& other) { + m_expr_str = std::move(other.m_expr_str); + m_parser = other.m_parser; + m_root = other.m_root; + + other.m_root = nullptr; + } + + Expression& operator=(Expression&& other) { + if (this != &other) { + if (m_root) { + node_free(m_root); + } + + m_expr_str = std::move(other.m_expr_str); + m_parser = other.m_parser; + m_root = other.m_root; + + other.m_root = nullptr; + } + + return *this; + } + + void SetExpression(const std::string& expr_str) { + if (expr_str == m_expr_str) { + return; + } + + m_expr_str = expr_str; + + if (m_root) { + node_free(m_root); + m_root = nullptr; + } + + if (m_expr_str.empty()) { + return; + } + + m_root = parser_parse(&m_parser, m_expr_str.c_str(), "x"); + } + + bool Evaluate(double x, double& y) const { + if (!m_root) { + return false; + } + + return node_eval(m_root, x, &y) == EVAL_OK; + } + + bool IsValid() const { + return m_root != nullptr; + } + + const char* GetErrorText() const { + if (parser_get_error(&m_parser) == ERR_NO_ERR) { + return nullptr; + } + + if (m_expr_str.empty()) { + return nullptr; + } + + return parser_get_error_text(&m_parser); + } + + ~Expression() { + if (m_root) { + node_free(m_root); + } + } + +}; + + diff --git a/main.cpp b/main.cpp index 43995c6..38fc2e7 100644 --- a/main.cpp +++ b/main.cpp @@ -2,57 +2,45 @@ #include #include -extern "C" { -#include "lex.h" -#include "parser.h" -#include "canvas_graph.h" -} - -static void draw_graph_impl(struct error_buffer *eb, const char *expr, const struct graph_range *range) { - struct parser parser; - struct expr_node *node; - - error_buffer_init(eb); - - /* zpracování výrazu */ - node = parser_parse(&parser, expr, "x"); - - if (!node) { - /* vypsání chyby */ - error_set(eb, parser_get_error(&parser)); - error_printf(eb, "%s", parser_get_error_text(&parser)); - return; - } - - /* vygenerování grafu */ - canvas_generate_graph(node, range, expr); - - /* uvolnění paměti */ - node_free(node); - - return; -} - -int main() { - -} +#include "canvas_graph.hpp" using namespace emscripten; -extern "C" void draw_graph(const char *expr, double xmin, double xmax, double ymin, double ymax) { +static std::vector s_exprs; + +int main() { + return 0; +} + +extern "C" void set_num_exprs(size_t num_exprs) { + s_exprs.resize(num_exprs); +} + +extern "C" void set_expr(size_t idx, const char *expr) { + if (idx >= s_exprs.size()) + return; + + s_exprs[idx].SetExpression(expr); +} + +extern "C" void print_errors() { const val document = val::global("document"); val p = document.call("getElementById", val("output")); - - struct error_buffer eb; - struct graph_range range = {xmin, xmax, ymin, ymax}; - draw_graph_impl(&eb, expr, &range); - if (error_get(&eb) != ERR_NO_ERR) { - p.set("innerText", error_get_text(&eb)); - } - else { - p.set("innerText", ""); + std::string errors; + + for (const auto& expr : s_exprs) { + const char* error_text = expr.GetErrorText(); + if (error_text) { + errors += error_text; + errors += "\n"; + } } - + p.set("innerText", errors.c_str()); +} + +extern "C" void draw_graph(double xmin, double xmax, double ymin, double ymax, double pointer_x, double pointer_y) { + struct graph_range range = {xmin, xmax, ymin, ymax}; + canvas_generate_graph(s_exprs.data(), s_exprs.size(), &range, pointer_x, pointer_y); } diff --git a/shell.html b/shell.html index 8a80771..a06e74f 100644 --- a/shell.html +++ b/shell.html @@ -25,6 +25,7 @@ #input { width: 200px; + height: 300px; font-size: 18px; padding: 5px; } @@ -54,6 +55,10 @@ {{{ SCRIPT }}}