From 2438b8b89d3da681c1fb11546e23666707d60fee Mon Sep 17 00:00:00 2001 From: lachrymaLF Date: Mon, 24 Jun 2024 08:32:01 -0400 Subject: [PATCH] im scared now --- Keishiki/CMakeLists.txt | 2 +- Keishiki/Plugboard.cpp | 52 +------- Keishiki/UI.cpp | 226 +++++++++++++++++++-------------- Keishiki/VisualTrack.cpp | 17 ++- Keishiki/ext/imgui | 2 +- Keishiki/ext/plf_colony | 1 + Keishiki/include/Common.h | 2 +- Keishiki/include/Graphics.h | 11 +- Keishiki/include/Plugboard.h | 34 ++++- Keishiki/include/VisualTrack.h | 2 +- README.md | 1 + TODO.md | 4 +- 12 files changed, 190 insertions(+), 164 deletions(-) create mode 160000 Keishiki/ext/plf_colony diff --git a/Keishiki/CMakeLists.txt b/Keishiki/CMakeLists.txt index a0c5f0c..61caa35 100644 --- a/Keishiki/CMakeLists.txt +++ b/Keishiki/CMakeLists.txt @@ -16,7 +16,7 @@ add_executable (Keishiki ${IMGUI_SRC} ${IMPLOT_SRC} set_property(TARGET Keishiki PROPERTY CXX_STANDARD 23) -include_directories ("include" "include/ext" "ext/imgui" "ext/implot") +include_directories ("include" "include/ext" "ext/imgui" "ext/implot" "ext/plf_colony") add_subdirectory("ext/freetype") add_subdirectory("ext/bgfx") diff --git a/Keishiki/Plugboard.cpp b/Keishiki/Plugboard.cpp index 4c80bbf..b6ed3ac 100644 --- a/Keishiki/Plugboard.cpp +++ b/Keishiki/Plugboard.cpp @@ -4,7 +4,7 @@ namespace K::Plugboard { bool DetectCycle(const NodeInstanceP n, Vector nodes) { // true if cycle found return !DFS(n, [&nodes](ConnectInfo info){ - if (std::find(nodes.begin(), nodes.end(), info.p) == nodes.end()) { + if (std::ranges::find(nodes, info.p) == nodes.end()) { nodes.push_back(info.p); return true; } @@ -37,57 +37,11 @@ namespace K::Plugboard { } void Disconnect(NodeInstanceP from_instance, u8 input_index) { - std::visit([&](auto&& from) { - std::visit([&](auto &&arg) { - using T = std::decay_t; - if constexpr (std::is_same_v) { - std::visit([&](auto&& a) { - auto& v = a->out[arg.index].outgoing; - for (auto it = v.begin(); it != v.end(); it++) { - if (it->p == from_instance) { - v.erase(it); - break; - } - } - }, arg.p); - from->in[input_index].value = ExpandVariant(from->in[input_index].type); - } - else if constexpr (std::is_same_v::type>) - ; // already disconnected - }, from->in[input_index].value); + std::visit([&input_index](auto&& from) { + Disconnect(*from, input_index); }, from_instance); } -/* NodeInstance MakeInstance(K_P_Nodes i) { - static NodeInstance table[] = { - Add{}, - Negate{}, - Subtract{}, - Multiply{}, - Divide{}, - Sign{}, - Sin{}, - Cos{}, - Tan{}, - Arcsin{}, - Arccos{}, - Arctan{}, - Atan2{}, - Minimum{}, - Maximum{}, - Power{}, - SquareRoot{}, - NaturalLogarithm{}, - AbsoluteValue{}, - Interpolation{}, - Chain{}, - CompositionIn{}, - CompositionOut{} - }; - static_assert(sizeof(table)/sizeof(table[0]) == K_P_Count); - return table[i]; - }*/ - T_Map::type ExpandVariant(u32 i) { static T_Map::type table[] = { f32{}, i32{}, RGBA{}, XY{}, XYZ{}, String{} }; return table[i]; diff --git a/Keishiki/UI.cpp b/Keishiki/UI.cpp index 720fddc..5954ac7 100644 --- a/Keishiki/UI.cpp +++ b/Keishiki/UI.cpp @@ -58,7 +58,7 @@ namespace ImGui { namespace K::UI { - void AddTransformLayer(CompState& s, const std::string& name) { + void AddTransformLayer(CompState& s, const String& name) { auto l = Layer{ VisualTrack{}, name, @@ -188,8 +188,7 @@ namespace K::UI { template void PlugboardNodeDrawSockets(CompState& s, N& n, ImGuiStyle& style, - bool& dragging_on_socket, ImVec2& source, Dict& link_pos, - bool dummy = true) { + bool& dragging_on_socket, ImVec2& source, bool dummy = true) { i32 row = 1; for (u32 out = 0; out < n.out.size(); out++, row++) { ImGui::PushID(row); @@ -224,7 +223,7 @@ namespace K::UI { // Update link info if needed if (!n.out[out].outgoing.empty()) {\ - link_pos[{&n, out}].source = ImGui::GetCursorScreenPos() + ImVec2{ImGui::GetFrameHeight() / 2, + s.plugboard.links_pos[{&n, out}].source = ImGui::GetCursorScreenPos() + ImVec2{ImGui::GetFrameHeight() / 2, -ImGui::GetFrameHeight() / 2 - style.ItemInnerSpacing.y}; } @@ -255,13 +254,13 @@ namespace K::UI { ImGui::TextUnformatted(n.in[in].name.c_str()); // Update link info - std::visit([&socket_link_pos, &dragging_on_socket, &source, &link_pos](auto&& arg) { + std::visit([&socket_link_pos, &dragging_on_socket, &source, &s](auto&& arg) { using T = std::decay_t; if constexpr (std::is_same_v) { if (ImGui::IsItemActive()) - link_pos[arg].sinks.push_back(ImGui::GetMousePos()); + s.plugboard.links_pos[arg].sinks.push_back(ImGui::GetMousePos()); else - link_pos[arg].sinks.push_back(socket_link_pos); + s.plugboard.links_pos[arg].sinks.push_back(socket_link_pos); } else if (ImGui::IsItemActive()) { dragging_on_socket = true; @@ -279,15 +278,16 @@ namespace K::UI { } template - bool PlugboardDrawNode(CompState& s, N& n, ImVec2& view_pos, ImGuiIO& io, ImGuiStyle& style, - bool& dragging_on_socket, ImVec2& source, Dict& link_pos) { - bool stat = false; + void PlugboardDrawNode(CompState& s, N& n, ImVec2& view_pos, ImGuiIO& io, ImGuiStyle& style, + bool& dragging_on_socket, ImVec2& source, bool& delete_request) { + Plugboard::NodeInstanceP ptr{&n}; + bool selected = std::ranges::find(s.plugboard.selected_nodes, ptr) != s.plugboard.selected_nodes.end(); const auto& pos = n.pos; const auto& name = n.name; ImGui::SetCursorPos(view_pos + pos); ImGui::PushID(&n); - ImGui::PushStyleColor(ImGuiCol_ChildBg, 0xAA080813); + ImGui::PushStyleColor(ImGuiCol_ChildBg, selected ? 0xAA202040 : 0xAA080813); static ImVec2 old_pos{}; if (ImGui::BeginChild(name, {}, ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_Border)) { if (ImGui::BeginTable(name, 3)) { @@ -295,9 +295,22 @@ namespace K::UI { ImGui::TableNextColumn(); ImGui::TableNextColumn(); ImGui::Button(name, {100.0f , 0.0f}); + if (ImGui::BeginPopupContextItem()) { + if (!s.plugboard.selected_nodes.empty() && ImGui::Button("Delete")) { + delete_request = true; + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } if (ImGui::IsItemClicked()) { old_pos = pos; - stat = true; + if (io.KeyCtrl && !selected) { + s.plugboard.selected_nodes.push_back(ptr); + } + else if (!selected) { + s.plugboard.selected_nodes.clear(); + s.plugboard.selected_nodes.push_back(ptr); + } } if (ImGui::IsItemActive()) { n.pos = old_pos + ImGui::GetMouseDragDelta(); @@ -312,7 +325,7 @@ namespace K::UI { ImGui::TableNextColumn(); } - PlugboardNodeDrawSockets(s, n, style, dragging_on_socket, source, link_pos); + PlugboardNodeDrawSockets(s, n, style, dragging_on_socket, source); ImGui::EndTable(); } @@ -320,7 +333,6 @@ namespace K::UI { ImGui::PopStyleColor(); ImGui::EndChild(); ImGui::PopID(); - return stat; } void TogglePlay() { @@ -352,7 +364,7 @@ namespace K::UI { view_count++; for (u32 i = s.layers.size() - 1; i != u32(-1); i--) { - if (std::find(s.disabled.begin(), s.disabled.end(), i) != s.disabled.end()) continue; + if (std::ranges::find(s.disabled, i) != s.disabled.end()) continue; if (s.current_frame > s.layers[i].out || s.current_frame < s.layers[i].in) continue; s.layers[i].track.GetFrame(s, Graphics::K_VIEW_COMP_COMPOSITE + view_count++, render_fb, s.width, @@ -417,6 +429,51 @@ namespace K::UI { }; void Composition(CompState& s) { + static bool layer_delete_requested = false; + if (layer_delete_requested) { + if (std::ranges::find(s.selected, s.active) != s.selected.end()) // unset active if active is selected + s.active = -1; + u32 deleted = 0, before_active = 0; // stupid counting tricks + const u32 sz = s.layers.size(); + for (u32 j = 0; j < sz; j++) { // im sure this isn't the best way to go about this + if (std::ranges::find(s.selected, j) != s.selected.end()) { + s.layers[j - deleted].track.Clear(); + for (auto& u : s.layers[j - deleted].track.uniforms) + VisualTrack::HideUniform(s, u); + s.layers.erase(s.layers.begin() + j - deleted); + std::erase(s.disabled, j); + if (static_cast(j) < s.active) + before_active++; + deleted++; + } + else if (auto it = std::ranges::find(s.disabled, j); it != s.disabled.end()) + *it -= deleted; + } + s.selected.clear(); + if (s.active != -1) + s.active -= static_cast(before_active); + + layer_delete_requested = false; + } + + static bool node_delete_requested = false; + if (node_delete_requested) { + for (auto p : s.plugboard.selected_nodes) { + std::visit([&s](auto&& arg){ + for (u32 i = 0; i < arg->in.size(); i++) + Plugboard::Disconnect(arg, i); + for (auto& socket : arg->out) + for (Plugboard::ConnectInfo& connection : socket.outgoing) + Plugboard::Disconnect(connection.p, connection.index); + + s.plugboard.nodes.Remove(arg); + }, p); + s.plugboard.RecollectChains(); + } + s.plugboard.selected_nodes.clear(); + node_delete_requested = false; + } + if (!ImGui::Begin("Composition", &draw_comp, ImGuiWindowFlags_NoScrollbar)) { ImGui::End(); return; @@ -456,38 +513,12 @@ namespace K::UI { const bool no_selection = s.selected.empty(); static String name{}; - if (ImGui::Button("Add Layer") && !name.empty()) { + if (ImGui::Button("Add Layer") && !name.empty()) AddTransformLayer(s, name); - } + ImGui::SameLine(); ImGui::SetNextItemWidth(250.0f); ImGui::InputText("##LayerName", &name); - ImGui::SameLine(); - ImGui::BeginDisabled(no_selection); - if (ImGui::Button("Delete Layer")) { // todo unregister from plugboard beforehand - if (std::find(s.selected.begin(), s.selected.end(), s.active) != s.selected.end()) - s.active = -1; - u32 deleted = 0, before_active = 0; - const u32 sz = s.layers.size(); - for (u32 i = 0; i < sz; i++) { - if (std::find(s.selected.begin(), s.selected.end(), i) != s.selected.end()) { - s.layers[i - deleted].track.Clear(); - s.layers.erase(s.layers.begin() + i - deleted); - std::erase(s.disabled, i); - if (static_cast(i) < s.active) - before_active++; - deleted++; - } - else if (std::find(s.disabled.begin(), s.disabled.end(), i) != s.disabled.end()) { - std::erase(s.disabled, i); - s.disabled.push_back(i - deleted); - } - } - s.selected.clear(); - s.active -= static_cast(before_active); - } - ImGui::EndDisabled(); - ImGui::SameLine(); ImGui::Checkbox("Chain Editor", &show_curve_editor); @@ -526,6 +557,8 @@ namespace K::UI { for (f32 y = std::fmodf(view_pos.y, grid_inc); y < h; y += grid_inc) window->DrawList->AddLine(window_pos + ImVec2(0.0f, y), window_pos + ImVec2(w, y), 0xFF222222, 1.0f); + // why are we re-getting positions on each frame? + // this is just convenient because sometimes we would be dragging on a socket! s.plugboard.links_pos.clear(); if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) { @@ -539,7 +572,7 @@ namespace K::UI { (([&](auto&& arg){ for (auto& node : arg) { PlugboardDrawNode>(s, node, view_pos, io, style, dragging_on_socket, - drag_source, s.plugboard.links_pos); + drag_source, node_delete_requested); } }(args)), ...); }, s.plugboard.nodes.nodes); @@ -551,8 +584,7 @@ namespace K::UI { ImGuiChildFlags_AutoResizeY); ImGui::PopStyleColor(); if (ImGui::BeginTable("Composition In", 3)) { - PlugboardNodeDrawSockets(s, s.plugboard.in, style, dragging_on_socket, drag_source, - s.plugboard.links_pos, false); + PlugboardNodeDrawSockets(s, s.plugboard.in, style, dragging_on_socket, drag_source, false); ImGui::EndTable(); } @@ -578,15 +610,13 @@ namespace K::UI { f32 view_amt = view_right - view_left, mouse_tl_x; f32 fr_step; - static Vector selected_chains{}; - // Mouse Handlers // Drag playhead/pan static bool tl_clear_selection_request = false; bool tl_clear_selection_request_this_frame = tl_clear_selection_request; static bool bg_drag_select_active = false; static Vector keys_selected_by_bg_drag{}; - auto draw_tl_bg_drag_area = [&view_width, &style, &view_amt, &s, &mouse_tl_x, &io](f32 h = 0.0f, f32 w = 0.0f) { + auto tl_bg_handler = [&view_width, &style, &view_amt, &s, &mouse_tl_x, &io](f32 h = 0.0f, f32 w = 0.0f) { static f32 pan_x, view_left_old, view_right_old; ImGui::InvisibleButton("##TL_BG", ImVec2{ w == 0.0f ? view_width + style.CellPadding.x * 2 : w, h == 0.0f ? row_height + style.CellPadding.y : h}); if (ImGui::IsItemClicked()) { @@ -620,6 +650,16 @@ namespace K::UI { view_right = 1.0f; } } + if (ImGui::IsKeyPressed(ImGuiKey_Delete) && ImGui::IsItemHovered()) { + for (auto& chain : s.plugboard.selected_nodes) { + if (auto *c = std::get_if(&chain)) { + auto& [segments, selected] = (*c)->extra.chain; + for (u32 i : selected) + segments.erase(segments.begin() + i); + selected.clear(); + } + } + } }; constexpr static f32 knob_width = 8.0f; @@ -649,7 +689,8 @@ namespace K::UI { if (is_sel) chain.selected.push_back(pos_index); - } else { + } + else { if (ImGui::IsItemClicked()) { started_dragging = true; // setup dragging & start on next frame @@ -665,8 +706,7 @@ namespace K::UI { &(key_loop_target->segments)[ii]); // This sucks pretty bad... chain.selected.push_back(ii); } - auto bg_drag_sel_it = std::find(keys_selected_by_bg_drag.begin(), - keys_selected_by_bg_drag.end(), + auto bg_drag_sel_it = std::ranges::find(keys_selected_by_bg_drag, &(key_loop_target->segments)[ii]); if (is_sel && bg_drag_sel_it != keys_selected_by_bg_drag.end() && !drag_rect.Contains(k_pos)) { @@ -779,8 +819,8 @@ namespace K::UI { for (u32 i = 0; i < s.layers.size(); i++) { auto& current_layer = s.layers[i]; - const bool selected = std::find(s.selected.begin(), s.selected.end(), i) != s.selected.end(), - disabled = std::find(s.disabled.begin(), s.disabled.end(), i) != s.disabled.end(); + const bool selected = std::ranges::find(s.selected, i) != s.selected.end(), + disabled = std::ranges::find(s.disabled, i) != s.disabled.end(); ImGui::PushID(static_cast(i)); ImGui::TableNextRow(ImGuiTableRowFlags_None, row_height); @@ -796,6 +836,13 @@ namespace K::UI { ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, {0.0f, (row_height - ImGui::GetTextLineHeight()) / 2.0f}); bool layer_open = ImGui::TreeNodeEx(current_layer.name.c_str(), flags); + if (ImGui::BeginPopupContextItem()) { + if (!s.selected.empty() && ImGui::Button("Delete")) { + layer_delete_requested = true; + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) { if (io.KeyCtrl) { if (selected) @@ -970,7 +1017,7 @@ namespace K::UI { auto& c = s.plugboard.nodes.Store(Plugboard::Chain{}); Plugboard::Connect(u.connection.p, u.connection.index, &c, 0); Plugboard::Connect(&c, 0, &s.plugboard.in, 0); - auto& nodes = std::get(u.connection.p)->show_nodes[u.connection.index]; + auto& nodes = s.plugboard.out.show_nodes[u.connection.index]; nodes.clear(); nodes.emplace_back(&c, 0); } @@ -993,24 +1040,12 @@ namespace K::UI { ImGui::SetNextItemWidth(-FLT_MIN); - if (connected_v.index() == 0) - std::visit([](auto&& arg) { - using T = std::decay_t; - if constexpr (std::is_same_v::type>) - ImGui::DragFloat("##", &arg, 0.005f); - else if constexpr (std::is_same_v::type>) - ImGui::DragInt("##", &arg, 0.005f); - else if constexpr (std::is_same_v::type>) - ImGui::DragFloat4("##", &arg.r, 0.005f); - else if constexpr (std::is_same_v::type>) - ImGui::DragFloat2("##", &arg.x, 0.005f); - else if constexpr (std::is_same_v::type>) - ImGui::DragFloat3("##", &arg.x, 0.005f); - }, std::get<0>(connected_v)); + if (auto *v = std::get_if::type>(&connected_v)) + DrawPlugboardVariableEditWidget(*v); ImGui::TableSetColumnIndex(3); ImGui::TableSetColumnIndex(4); - auto p = ImGui::GetCursorScreenPos(); + auto p = ImGui::GetCursorScreenPos(); // restored later // plot uniform if connected ImPlot::PushStyleVar(ImPlotStyleVar_PlotPadding, {0.0f, 0.0f}); @@ -1049,10 +1084,10 @@ namespace K::UI { ImGui::SetCursorScreenPos(p); - draw_tl_bg_drag_area(); + tl_bg_handler(); if (uniform_open && connected_v.index() == 1) { - for (const auto& info: std::get(u.connection.p)->show_nodes[u.connection.index]) { + for (const auto& info: s.plugboard.out.show_nodes[u.connection.index]) { ImGui::TableNextRow(ImGuiTableRowFlags_None, row_height); ImGui::TableSetColumnIndex(0); ImGui::TableSetColumnIndex(1); @@ -1060,15 +1095,15 @@ namespace K::UI { auto n_flags = ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_FramePadding; - auto chain_sel_it = std::find(selected_chains.begin(), selected_chains.end(), info.p); - if (chain_sel_it != selected_chains.end()) + auto chain_sel_it = std::ranges::find(s.plugboard.selected_nodes, info.p); + if (chain_sel_it != s.plugboard.selected_nodes.end()) n_flags |= ImGuiTreeNodeFlags_Selected; ImGui::TreeNodeEx(std::visit([](auto&& arg){ return arg->name; }, info.p), n_flags); if (ImGui::IsItemClicked()) { - if (chain_sel_it != selected_chains.end()) - selected_chains.erase(chain_sel_it); + if (chain_sel_it != s.plugboard.selected_nodes.end()) + s.plugboard.selected_nodes.erase(chain_sel_it); else - selected_chains.push_back(info.p); + s.plugboard.selected_nodes.push_back(info.p); } ImGui::TableSetColumnIndex(2); @@ -1094,8 +1129,8 @@ namespace K::UI { f32 v = std::get::type>( Plugboard::Eval(s, info)); if (ImGui::DragFloat("##value", &v, 0.005f)) { - if (std::find(selected_chains.begin(), selected_chains.end(), info.p) == selected_chains.end()) - selected_chains.push_back(info.p); + if (std::ranges::find(s.plugboard.selected_nodes, info.p) == s.plugboard.selected_nodes.end()) + s.plugboard.selected_nodes.push_back(info.p); auto l = std::lower_bound(chain.segments.begin(), chain.segments.end(), s.current_frame, [](const Plugboard::ChainSegment& a, i32 b) { return a.frame < b; }); @@ -1137,7 +1172,7 @@ namespace K::UI { const auto& [k, val, segment] = (key_loop_target->segments)[ii]; ImGui::PushID(k); - auto sel_it = std::find(key_loop_target->selected.begin(), key_loop_target->selected.end(), ii); + auto sel_it = std::ranges::find(key_loop_target->selected, ii); bool is_sel = sel_it != key_loop_target->selected.end(); i32 frame = k; @@ -1166,13 +1201,13 @@ namespace K::UI { begin_tl.y + row_height / 2.0f - 2.0f}, {TimelineFrameToScreenView(view_left, view_amt, view_width, nit->frame, s.frame_max) + begin_tl.x - kf_tab_width / 2.0f, begin_tl.y + row_height / 2.0f + 2.0f}, - std::find(chain.selected.begin(), chain.selected.end(), std::distance(chain.segments.begin(), it)) != chain.selected.end() && - std::find(chain.selected.begin(), chain.selected.end(), std::distance(chain.segments.begin(), nit)) != chain.selected.end() ? 0x88FFAAAA : 0x44AAAAAA); + std::ranges::find(chain.selected, std::distance(chain.segments.begin(), it)) != chain.selected.end() && + std::ranges::find(chain.selected, std::distance(chain.segments.begin(), nit)) != chain.selected.end() ? 0x88FFAAAA : 0x44AAAAAA); } } ImGui::SetCursorScreenPos(begin_tl); - draw_tl_bg_drag_area(); + tl_bg_handler(); } } ImGui::PopID(); @@ -1184,7 +1219,7 @@ namespace K::UI { std::visit([table_left, &s](auto &&arg) { using T = std::decay_t; if constexpr (std::is_same_v) - s.plugboard.links_pos[arg].sinks.push_back( + s.plugboard.links_pos[arg].sinks.push_back( // make nodes connect to collapsed layer row {table_left, ImGui::GetCursorScreenPos().y - row_height / 2.0f}); }, std::visit([&u](auto&& arg) -> auto& { return arg->in[u.connection.index].value; }, u.connection.p)); @@ -1200,7 +1235,7 @@ namespace K::UI { tl_end_begin = {tl_init_pos.x, ImGui::GetCursorScreenPos().y}; - if (move_from != -1 && move_to != -1) { + if (move_from != -1 && move_to != -1) { // do layer move if (no_selection) std::swap(s.layers[move_to], s.layers[move_from]); else { @@ -1243,7 +1278,7 @@ namespace K::UI { ImGui::SetCursorScreenPos(tl_end_begin); if (ImGui::BeginChild("TL Bottom Unfilled Drag Overlay")) { - draw_tl_bg_drag_area(ImGui::GetContentRegionAvail().y); + tl_bg_handler(ImGui::GetContentRegionAvail().y); } ImGui::EndChild(); @@ -1275,9 +1310,12 @@ namespace K::UI { dragging = false; } u32 keys = 0; - for (const auto& n : selected_chains) { - Plugboard::ConnectInfo i {n, 0}; // it's a chain -- 0 is out + for (const auto& n : s.plugboard.selected_nodes) { + if (!std::holds_alternative(n)) + continue; + auto& [chain, chain_copy] = std::get(n)->extra; + Plugboard::ConnectInfo i {n, 0}; // it's a chain -- 0 is out if (started_dragging) chain_copy = chain; @@ -1293,9 +1331,9 @@ namespace K::UI { auto& [k, val, segment] = (key_loop_target->segments)[ii]; ImGui::PushID(keys++); - auto sel_it = std::find(key_loop_target->selected.begin(), key_loop_target->selected.end(), ii); + auto sel_it = std::ranges::find(key_loop_target->selected, ii); bool is_sel = sel_it != key_loop_target->selected.end(); - bool is_nxt_sel = ii + 1 < key_loop_target->segments.size() && std::find(key_loop_target->selected.begin(), key_loop_target->selected.end(), ii + 1) != key_loop_target->selected.end(); + bool is_nxt_sel = ii + 1 < key_loop_target->segments.size() && std::ranges::find(key_loop_target->selected, ii + 1) != key_loop_target->selected.end(); i32 frame = k; f64 v = val; @@ -1401,7 +1439,7 @@ namespace K::UI { ImPlot::PopStyleColor(3); ImPlot::PopStyleVar(2); ImGui::SetCursorScreenPos(ImGui::GetCursorStartPos()); - draw_tl_bg_drag_area(ImGui::GetContentRegionAvail().y, ImGui::GetContentRegionAvail().x); + tl_bg_handler(ImGui::GetContentRegionAvail().y, ImGui::GetContentRegionAvail().x); } ImGui::EndChild(); ImGui::PopStyleColor(); @@ -1602,12 +1640,12 @@ namespace K::UI { if (ImGui::Button("Apply")) for (auto& layer : s.layers) for (auto& u : layer.track.uniforms) - for (auto& n : std::get(u.connection.p)->show_nodes[u.connection.index]) { + for (auto& n : s.plugboard.out.show_nodes[u.connection.index]) { auto& [chain, _] = std::get(n.p)->extra; for (auto it = chain.segments.begin(); it != chain.segments.end(); it++) { auto nit = std::next(it); - if (nit != chain.segments.end() && std::find(chain.selected.begin(), chain.selected.end(), std::distance(chain.segments.begin(), it)) != chain.selected.end() && - std::find(chain.selected.begin(), chain.selected.end(), std::distance(chain.segments.begin(), nit)) != chain.selected.end()) { + if (nit != chain.segments.end() && std::ranges::find(chain.selected, std::distance(chain.segments.begin(), it)) != chain.selected.end() && + std::ranges::find(chain.selected, std::distance(chain.segments.begin(), nit)) != chain.selected.end()) { it->interp = {type_current, {p2.x, p2.y, p3.x, p3.y}}; } } diff --git a/Keishiki/VisualTrack.cpp b/Keishiki/VisualTrack.cpp index e890d81..777aeb4 100644 --- a/Keishiki/VisualTrack.cpp +++ b/Keishiki/VisualTrack.cpp @@ -192,17 +192,22 @@ namespace K { ExposeUniform(s, uniforms[i]); } -/* void VisualTrack::HideUniform(CompState& s, Uniform& uu) { - uu.connection.p->disconnect(uu.connection.index); + void VisualTrack::HideUniform(CompState& s, Uniform& uu) { + Plugboard::Disconnect(uu.connection.p, uu.connection.index); + // decrement indices of uniforms referencing CompositionOut whose index is greater for (auto& l : s.layers) for (auto& u_other : l.track.uniforms) if (u_other.connection.index > uu.connection.index) u_other.connection.index--; - s.plugboard.comp_out.in.erase(s.plugboard.out_instance.node->in.begin() + uu.connection.index); - s.plugboard.out_instance.inputs_fed.erase(s.plugboard.out_instance.inputs_fed.begin() + uu.connection.index); - uu.connection = {nullptr, 0}; - }*/ + + if (std::holds_alternative(s.plugboard.out.in[uu.connection.index].value)) { + Plugboard::Disconnect(s.plugboard.out, uu.connection.index); + } + + s.plugboard.out.in.erase(s.plugboard.out.in.begin() + uu.connection.index); + uu.connection = {(Plugboard::Add*)(nullptr), 0}; + } void VisualTrack::AddUniform(const String& s, ShaderGraph::T_Map::type&& val) { for (auto& u : uniforms) diff --git a/Keishiki/ext/imgui b/Keishiki/ext/imgui index fdc084f..7237d3e 160000 --- a/Keishiki/ext/imgui +++ b/Keishiki/ext/imgui @@ -1 +1 @@ -Subproject commit fdc084f532189fda8474079f79e74fa5e3541c9f +Subproject commit 7237d3e5c3a6b837b7b457460877cf5eea8c3745 diff --git a/Keishiki/ext/plf_colony b/Keishiki/ext/plf_colony new file mode 160000 index 0000000..fbc8483 --- /dev/null +++ b/Keishiki/ext/plf_colony @@ -0,0 +1 @@ +Subproject commit fbc8483fb785c431dc4410396815abf886728c40 diff --git a/Keishiki/include/Common.h b/Keishiki/include/Common.h index ba66713..412bb08 100644 --- a/Keishiki/include/Common.h +++ b/Keishiki/include/Common.h @@ -22,7 +22,7 @@ namespace K { using String = std::string; using Byte = u8; using Char = char; - template using Vector = std::vector; + template using Vector = std::vector; template using Dict = std::unordered_map; inline void LogBase(const std::string_view& s, u8 level) { diff --git a/Keishiki/include/Graphics.h b/Keishiki/include/Graphics.h index bfde36d..cf93469 100644 --- a/Keishiki/include/Graphics.h +++ b/Keishiki/include/Graphics.h @@ -4,6 +4,7 @@ #include #include #include +#include #include @@ -129,11 +130,11 @@ namespace K::Graphics { Qcubed = Q * Q * Q, D = Qcubed - R * R; - f64 theta = acos(R / sqrt(Qcubed)); - f64 sqrtQ = sqrt(Q); - f64 r1 = -2.0 * sqrtQ * cos( theta / 3.0) - a1 / 3.0, - r2 = -2.0 * sqrtQ * cos((theta + 2.0 * std::numbers::pi) / 3.0) - a1 / 3.0, - r3 = -2.0 * sqrtQ * cos((theta + 4.0 * std::numbers::pi) / 3.0) - a1 / 3.0; + f64 theta = acos(R / std::sqrt(Qcubed)); + f64 sqrtQ = std::sqrt(Q); + f64 r1 = -2.0 * sqrtQ * std::cos( theta / 3.0) - a1 / 3.0, + r2 = -2.0 * sqrtQ * std::cos((theta + 2.0 * std::numbers::pi) / 3.0) - a1 / 3.0, + r3 = -2.0 * sqrtQ * std::cos((theta + 4.0 * std::numbers::pi) / 3.0) - a1 / 3.0; f64 e = std::pow(std::sqrt(-D) + std::abs(R), 1.0 / 3.0); e = (R > 0.0) ? -e : e; return Q == 0.0 && D == 0.0 ? -a1 / 3. : (D >= 0 ? CubicRealAccept(r1) ? r1 : (CubicRealAccept(r2) ? r2 : r3) : (e + Q / e) - a1 / 3.); diff --git a/Keishiki/include/Plugboard.h b/Keishiki/include/Plugboard.h index e2c1f3a..7e98f1c 100644 --- a/Keishiki/include/Plugboard.h +++ b/Keishiki/include/Plugboard.h @@ -3,7 +3,7 @@ #include "Graphics.h" #include #include -#include +#include #include #include #include @@ -583,11 +583,11 @@ namespace K::Plugboard { template N> auto& Store(const N& v) { - return std::get>(nodes).push_back(v); + return std::get>(nodes).insert(v); } template N> auto& Store(N&& v) { - return std::get>(nodes).emplace_back(std::forward(v)); + return *std::get>(nodes).emplace(std::forward(v)); } auto& Store(const std::variant& v) { return std::visit([this](auto&& arg) { return store(arg); }, v); @@ -596,7 +596,13 @@ namespace K::Plugboard { return std::visit([this](auto&& arg) { return store(std::forward(arg)); }, std::move(v)); } - std::tuple...> nodes; + template N> + auto Remove(N *p) { + auto& colony = std::get>(nodes); + return colony.erase(colony.get_iterator(p)); + } + + std::tuple...> nodes; template using NthTypeOf = typename std::tuple_element>::type; @@ -653,11 +659,31 @@ namespace K::Plugboard { Dict links_pos; // this is hilariously bad CompositionIn in; CompositionOut out; + + Vector selected_nodes{}; + void RecollectChains(); }; void Connect(NodeInstanceP from_instance, u8 input_index, NodeInstanceP to_instance, u8 to_out_index); + template + void Disconnect(N& instance, u8 input_index) { + if (auto *info = std::get_if(&instance.in[input_index].value)) { + std::visit([&](auto&& a) { + auto& v = a->out[info->index].outgoing; + for (auto it = v.begin(); it != v.end(); it++) { + if (N **p = std::get_if(&it->p)) { + if (*p == &instance) { + v.erase(it); + break; + } + } + } + }, info->p); + instance.in[input_index].value = ExpandVariant(instance.in[input_index].type); + } + } void Disconnect(NodeInstanceP from_instance, u8 input_index); diff --git a/Keishiki/include/VisualTrack.h b/Keishiki/include/VisualTrack.h index a3ee6c8..5b4eb16 100644 --- a/Keishiki/include/VisualTrack.h +++ b/Keishiki/include/VisualTrack.h @@ -35,7 +35,7 @@ namespace K { f32 view[16], f32 transform[16]); static void ExposeUniform(CompState& s, Uniform& uu); void ExposeUniform(CompState& s, u32 i); -// static void HideUniform(CompState& s, Uniform& uu); + static void HideUniform(CompState& s, Uniform& uu); void AddUniform(const String& s, ShaderGraph::T_Map::type&& val); void AddSampler(const String& s); diff --git a/README.md b/README.md index 8ede225..8c96f43 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ - [SDL2](https://www.libsdl.org/) - [stb](https://github.com/nothings/stb) - [SRELL](https://www.akenotsuki.com/misc/srell/en/) +- [plf::colony](https://plflib.org/colony.htm) ## Runtime Dependency - [ffmpeg](https://ffmpeg.org/) diff --git a/TODO.md b/TODO.md index 04cb5ed..2abafde 100644 --- a/TODO.md +++ b/TODO.md @@ -7,11 +7,11 @@ - Blender previews resource - Data models - Dump and read back state, (de)serialization!!! - - Pre-compose/Layer Groups (jokes -- can be completely UI side) + - Pre-compose/Layer Groups - Undo's - Node groups -- Deleting layers/nodes/keys/etc upkeep - Motion blur + - Text (idea: index-based evaluation in plugboard) - Shapes (idea: embed glisp :mmtroll:, need to inquire -- still a lot of friction for simple shapes if we don't also get the glisp gizmos) - External data driving (csv, json or something else?) -- use a node to select source