diff --git a/CMakeLists.txt b/CMakeLists.txt index 280229f..2cc5287 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.25) -project(graphs VERSION 0.1.6) +project(graphs VERSION 0.1.7) option(ENABLE_ADDRSAN "Enable the address sanitizer" OFF) option(ENABLE_UBSAN "Enable the ub sanitizer" OFF) diff --git a/include/config.h b/include/config.h index 486fb31..14a8ab9 100644 --- a/include/config.h +++ b/include/config.h @@ -30,7 +30,7 @@ namespace conf inline constexpr float DEFAULT_INITIAL_TEMPERATURE = 100; inline constexpr float POINT_SIZE = 35; - inline constexpr auto DEFAULT_IMAGE = "parker_point"; + inline constexpr auto DEFAULT_IMAGE = "unknown"; inline constexpr auto POINT_OUTLINE_COLOR = blt::make_color(0, 0.6, 0.6); inline constexpr auto POINT_HIGHLIGHT_COLOR = blt::make_color(0, 1.0, 1.0); inline constexpr auto POINT_SELECT_COLOR = blt::make_color(0, 0.9, 0.7); diff --git a/include/graph.h b/include/graph.h index c553680..dc5a120 100644 --- a/include/graph.h +++ b/include/graph.h @@ -188,7 +188,7 @@ class engine_t friend struct loader_t; private: graph_t graph; - selector_t selector; + selector_t selector {graph}; void draw_gui(const blt::gfx::window_data& data); @@ -205,10 +205,12 @@ class engine_t auto& io = ImGui::GetIO(); if (!io.WantCaptureMouse) - selector.process_mouse(graph, data.width, data.height); + selector.process_mouse(data.width, data.height); + if (!io.WantCaptureKeyboard) + selector.process_keyboard(data.width, data.height); graph.render(); - selector.render(graph, data.width, data.height); + selector.render(data.width, data.height); } }; diff --git a/include/selection.h b/include/selection.h index c89cf01..a4451bb 100644 --- a/include/selection.h +++ b/include/selection.h @@ -24,23 +24,36 @@ class selector_t { public: + explicit selector_t(graph_t& graph): graph(graph) + {} + // called inside the info panel block, used for adding more information - void draw_gui(graph_t& graph, blt::i32 width, blt::i32 height); + void draw_gui(blt::i32 width, blt::i32 height); // called once per frame, for rendering animations - void render(graph_t& graph, blt::i32 width, blt::i32 height); + void render(blt::i32 width, blt::i32 height); // called once per frame assuming imgui doesn't want the mouse - void process_mouse(graph_t& graph, blt::i32 width, blt::i32 height); + void process_mouse(blt::i32 width, blt::i32 height); + + void process_keyboard(blt::i32 width, blt::i32 height); + private: void set_primary_selection(blt::i64 n); + void set_drag_selection(blt::i64 n); + void set_secondary_selection(blt::i64 n); + + void create_placement_node(const blt::vec2& pos); + void destroy_node(blt::i64 node); + private: blt::i64 drag_selection = -1; blt::i64 primary_selection = -1; blt::i64 secondary_selection = -1; - bool edge = false; + bool placement = false; + graph_t& graph; }; #endif //GRAPHS_SELECTION_H diff --git a/res/ben.jpg b/res/ben.jpg new file mode 100644 index 0000000..9bcc043 Binary files /dev/null and b/res/ben.jpg differ diff --git a/res/braxton.jpg b/res/braxton.jpg new file mode 100644 index 0000000..6d3185a Binary files /dev/null and b/res/braxton.jpg differ diff --git a/res/ivy.jpg b/res/ivy.jpg new file mode 100644 index 0000000..7c8d5c8 Binary files /dev/null and b/res/ivy.jpg differ diff --git a/res/jacob.jpg b/res/jacob.jpg new file mode 100644 index 0000000..6286394 Binary files /dev/null and b/res/jacob.jpg differ diff --git a/res/me.png b/res/me.png new file mode 100644 index 0000000..eadd6e0 Binary files /dev/null and b/res/me.png differ diff --git a/res/no_image.jpg b/res/no_image.jpg new file mode 100644 index 0000000..d5d7e5b Binary files /dev/null and b/res/no_image.jpg differ diff --git a/res/sayori.jpg b/res/sayori.jpg new file mode 100644 index 0000000..ef827a0 Binary files /dev/null and b/res/sayori.jpg differ diff --git a/src/graph.cpp b/src/graph.cpp index c7b63cb..f0f0a7e 100644 --- a/src/graph.cpp +++ b/src/graph.cpp @@ -269,7 +269,7 @@ void engine_t::draw_gui(const blt::gfx::window_data& data) } } } - selector.draw_gui(graph, data.width, data.height); + selector.draw_gui(data.width, data.height); im::End(); } } diff --git a/src/main.cpp b/src/main.cpp index ddfd560..2ac514c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -54,6 +54,13 @@ void init(const blt::gfx::window_data& data) resources.enqueue("res/parker.png", "parker"); resources.enqueue("res/parkerpoint.png", "parker_point"); resources.enqueue("res/parker cat ears.jpg", "parkercat"); + resources.enqueue("res/ivy.jpg", "ivy"); + resources.enqueue("res/sayori.jpg", "sayori"); + resources.enqueue("res/jacob.jpg", "jacob"); + resources.enqueue("res/braxton.jpg", "braxton"); + resources.enqueue("res/ben.jpg", "ben"); + resources.enqueue("res/no_image.jpg", "unknown"); + resources.enqueue("res/me.png", "me"); if (auto loader = loader_t::load_for(engine, data, "default.json", "save.json")) { @@ -76,7 +83,8 @@ void update(const blt::gfx::window_data& data) engine.render(data); - camera.update(); + if (!im::GetIO().WantCaptureKeyboard) + camera.update(); camera.update_view(global_matrices); global_matrices.update(); diff --git a/src/selector.cpp b/src/selector.cpp index b3d6af1..c02396e 100644 --- a/src/selector.cpp +++ b/src/selector.cpp @@ -18,71 +18,191 @@ #include #include #include +#include extern blt::gfx::batch_renderer_2d renderer_2d; extern blt::gfx::matrix_state_manager global_matrices; -void selector_t::render(graph_t& graph, blt::i32 width, blt::i32 height) +blt::expanding_buffer name_input; +blt::expanding_buffer texture_input; +blt::expanding_buffer description_input; + +struct callback_data_t +{ + std::string& str; + blt::expanding_buffer& buffer; +}; + +void from_string(std::string& str, blt::expanding_buffer& buf) +{ + if (buf.size() <= str.size()) + buf.resize(str.size() + 1); + std::memcpy(buf.data(), str.data(), str.size()); + buf.data()[str.size()] = '\0'; +} + +int text_input_callback(ImGuiInputTextCallbackData* data) +{ + auto [str, buffer] = *static_cast(data->UserData); + + if (data->EventFlag == ImGuiInputTextFlags_CallbackResize) + { + buffer.resize(data->BufSize); + data->Buf = buffer.data(); + } else if (data->EventFlag == ImGuiInputTextFlags_CallbackEdit || data->EventFlag == ImGuiInputTextFlags_CallbackCompletion) + str = std::string_view{data->Buf, static_cast(data->BufTextLen)}; + + return 0; +} + +void selector_t::render(blt::i32 width, blt::i32 height) { } -void selector_t::process_mouse(graph_t& graph, blt::i32 width, blt::i32 height) +void selector_t::process_mouse(blt::i32 width, blt::i32 height) { const auto mouse_pos = blt::make_vec2(blt::gfx::calculateRay2D(width, height, global_matrices.getScale2D(), global_matrices.getView2D(), global_matrices.getOrtho())); bool mouse_pressed = blt::gfx::isMousePressed(0) && blt::gfx::mousePressedLastFrame(); - for (const auto& [index, node] : blt::enumerate(graph.nodes)) + if (placement) { - const auto pos = node.getPosition(); - const auto dist = pos - mouse_pos; - const auto mag = dist.magnitude(); - - if (mag < node.getRenderObj().scale && mouse_pressed) + if (mouse_pressed) { - set_drag_selection(static_cast(index)); - if (primary_selection == drag_selection || secondary_selection == drag_selection) + placement = false; + set_drag_selection(-1); + } else if (blt::gfx::isKeyPressed(GLFW_KEY_ESCAPE)) + { + destroy_node(primary_selection); + } + } else + { + for (const auto& [index, node] : blt::enumerate(graph.nodes)) + { + const auto pos = node.getPosition(); + const auto dist = pos - mouse_pos; + const auto mag = dist.magnitude(); + + if (mag < node.getRenderObj().scale && mouse_pressed) { - edge = false; + set_drag_selection(static_cast(index)); + if (blt::gfx::isKeyPressed(GLFW_KEY_LEFT_SHIFT)) + set_secondary_selection(drag_selection); + else + { + set_primary_selection(drag_selection); + set_secondary_selection(-1); + } break; } - if (blt::gfx::isKeyPressed(GLFW_KEY_LEFT_SHIFT)) - set_secondary_selection(drag_selection); - else - set_primary_selection(drag_selection); - break; } + if (blt::gfx::mouseReleaseLastFrame()) + set_drag_selection(-1); } - if (blt::gfx::mouseReleaseLastFrame()) - set_drag_selection(-1); - if (drag_selection >= 0 && drag_selection < static_cast(graph.nodes.size()) && blt::gfx::isMousePressed(0)) + if (drag_selection >= 0 && drag_selection < static_cast(graph.nodes.size()) && (blt::gfx::isMousePressed(0) || placement)) { graph.nodes[drag_selection].getPositionRef() = mouse_pos; } } -void selector_t::draw_gui(graph_t& graph, blt::i32 width, blt::i32 height) +void selector_t::process_keyboard(blt::i32 width, blt::i32 height) +{ + const auto mouse_pos = blt::make_vec2(blt::gfx::calculateRay2D(width, height, global_matrices.getScale2D(), global_matrices.getView2D(), + global_matrices.getOrtho())); + if (blt::gfx::keyPressedLastFrame()) + { + if (blt::gfx::isKeyPressed(GLFW_KEY_C)) + { + if (placement) + destroy_node(primary_selection); + else + create_placement_node(mouse_pos); + } else if (blt::gfx::isKeyPressed(GLFW_KEY_X)) + { + if (primary_selection != -1) + destroy_node(primary_selection); + } + } +} + +void selector_t::draw_gui(blt::i32 width, blt::i32 height) { im::SetNextItemOpen(true, ImGuiCond_Once); if (im::CollapsingHeader("Selection Information")) { im::Text("Silly (%ld) | (%ld || %ld)", drag_selection, primary_selection, secondary_selection); + if (primary_selection >= 0) + { + if (secondary_selection >= 0) + { + } else + { + im::InputFloat("Size", &graph.nodes[primary_selection].point.scale); + callback_data_t name_data{graph.nodes[primary_selection].name, name_input}; + callback_data_t texture_data{graph.nodes[primary_selection].texture, texture_input}; + callback_data_t description_data{graph.nodes[primary_selection].description, description_input}; + im::InputText("Name", name_input.data(), name_input.size(), + ImGuiInputTextFlags_CallbackResize | ImGuiInputTextFlags_CallbackEdit | ImGuiInputTextFlags_CallbackCompletion, + text_input_callback, &name_data); + im::InputText("Texture", texture_input.data(), texture_input.size(), + ImGuiInputTextFlags_CallbackResize | ImGuiInputTextFlags_CallbackEdit | ImGuiInputTextFlags_CallbackCompletion, + text_input_callback, &texture_data); + im::InputTextMultiline("Description", description_input.data(), description_input.size(), {}, + ImGuiInputTextFlags_CallbackResize | ImGuiInputTextFlags_CallbackEdit | ImGuiInputTextFlags_CallbackCompletion, + text_input_callback, &description_data); + } + } } } void selector_t::set_secondary_selection(blt::i64 n) { secondary_selection = n; + } void selector_t::set_primary_selection(blt::i64 n) { primary_selection = n; + if (primary_selection >= 0) + { + from_string(graph.nodes[primary_selection].name, name_input); + from_string(graph.nodes[primary_selection].texture, texture_input); + from_string(graph.nodes[primary_selection].description, description_input); + } } void selector_t::set_drag_selection(blt::i64 n) { drag_selection = n; } + +void selector_t::create_placement_node(const blt::vec2& pos) +{ + auto node = static_cast(graph.nodes.size()); + graph.nodes.push_back(node_t{{pos, conf::POINT_SIZE}}); + set_drag_selection(node); + set_primary_selection(node); + placement = true; +} + +void selector_t::destroy_node(blt::i64 node) +{ + auto& node_v = graph.nodes[node]; + graph.names_to_node.erase(node_v.name); + erase_if(graph.edges, [node](const edge_t& v) { + return static_cast(v.getFirst()) == node || static_cast(v.getSecond()) == node; + }); + graph.connected_nodes.erase(node); + for (auto& v : graph.connected_nodes) + { + v.second.erase(node); + } + + graph.nodes.erase(graph.nodes.begin() + node); + set_drag_selection(-1); + set_primary_selection(-1); + placement = false; +}