diff --git a/Keishiki/Keishiki.cpp b/Keishiki/Keishiki.cpp index 5717060..a5f5ebf 100644 --- a/Keishiki/Keishiki.cpp +++ b/Keishiki/Keishiki.cpp @@ -91,8 +91,8 @@ namespace K { state.current_frame = 0; state.fps = 30.0f; state.frame_max = 200; - state.plugboard.in = Plugboard::MakeInstance(Plugboard::K_P_CompositionIn); - state.plugboard.out = Plugboard::MakeInstance(Plugboard::K_P_CompositionOut); +// state.plugboard.in = Plugboard::K_P_CompositionIn{}; +// state.plugboard.out = Plugboard::K_P_CompositionOut{}; K::UI::SetupViewport(state); return true; diff --git a/Keishiki/Plugboard.cpp b/Keishiki/Plugboard.cpp index 2fcbf33..4c80bbf 100644 --- a/Keishiki/Plugboard.cpp +++ b/Keishiki/Plugboard.cpp @@ -2,7 +2,7 @@ #include namespace K::Plugboard { - bool DetectCycle(const NodeInstance& n, Vector& nodes) { // true if cycle found + 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()) { nodes.push_back(info.p); @@ -15,50 +15,50 @@ namespace K::Plugboard { void CollectChains(ConnectInfo& in, Vector& chains) { chains.clear(); - if (std::holds_alternative(*in.p)) + if (std::holds_alternative(in.p)) chains.push_back(in); - DFS(*in.p, [&chains](ConnectInfo info){ - if (std::holds_alternative(*info.p)) + DFS(in.p, [&chains](ConnectInfo info){ + if (std::holds_alternative(info.p)) chains.push_back(info); return true; }); } - void Connect(NodeInstance& from_instance, u8 input_index, NodeInstance& to_instance, u8 to_out_index) { - Vector nodes{&from_instance}; + void Connect(NodeInstanceP from_instance, u8 input_index, NodeInstanceP to_instance, u8 to_out_index) { + Vector nodes{from_instance}; if (!DetectCycle(to_instance, nodes)) // no cycle std::visit([&](auto&& from, auto&& to) { - from.in[input_index].value = ConnectInfo{&to_instance, to_out_index}; - to.out[to_out_index].outgoing.push_back({&from_instance, input_index}); + from->in[input_index].value = ConnectInfo{to_instance, to_out_index}; + to->out[to_out_index].outgoing.push_back({from_instance, input_index}); }, from_instance, to_instance); else Log("Connect failed -- Cycle detected"); } - void Disconnect(NodeInstance& from_instance, u8 input_index) { + 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; + auto& v = a->out[arg.index].outgoing; for (auto it = v.begin(); it != v.end(); it++) { - if (it->p == &from_instance) { + if (it->p == from_instance) { v.erase(it); break; } } - }, *arg.p); - from.in[input_index].value = ExpandVariant(from.in[input_index].type); + }, 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); + }, from->in[input_index].value); }, from_instance); } - NodeInstance MakeInstance(K_P_Nodes i) { +/* NodeInstance MakeInstance(K_P_Nodes i) { static NodeInstance table[] = { Add{}, Negate{}, @@ -86,7 +86,7 @@ namespace K::Plugboard { }; 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{} }; @@ -297,7 +297,7 @@ namespace K::Plugboard { } T_Map::type Eval(const CompState& s, const ConnectInfo& info) { - return std::visit([&info, &s](auto&& arg) { return arg.fetch[info.index](s, arg); }, *info.p); + return std::visit([&info, &s](auto&& arg) { return arg->fetch[info.index](s, *arg); }, info.p); } T_Map::type ConvertValue(const T_Map::type& v, Type target, bool& good) { @@ -403,12 +403,11 @@ namespace K::Plugboard { } void Plugboard::RecollectChains() { - auto& node = std::get(out); - for (u32 i = 0; i < node.in.size(); i++) { - std::visit([i, &node](auto&& arg) { + for (u32 i = 0; i < out.in.size(); i++) { + std::visit([i, this](auto&& arg) { if constexpr (std::is_same_v, ConnectInfo>) - CollectChains(arg, node.show_nodes[i]); - }, node.in[i].value); + CollectChains(arg, out.show_nodes[i]); + }, out.in[i].value); } } } diff --git a/Keishiki/UI.cpp b/Keishiki/UI.cpp index 9e3235d..720fddc 100644 --- a/Keishiki/UI.cpp +++ b/Keishiki/UI.cpp @@ -187,7 +187,7 @@ namespace K::UI { } template - void PlugboardNodeDrawSockets(CompState& s, N& n, Plugboard::NodeInstance& instance, ImGuiStyle& style, + void PlugboardNodeDrawSockets(CompState& s, N& n, ImGuiStyle& style, bool& dragging_on_socket, ImVec2& source, Dict& link_pos, bool dummy = true) { i32 row = 1; @@ -208,14 +208,14 @@ namespace K::UI { } if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) { - Plugboard::ConnectInfo d{ &instance, out }; + Plugboard::ConnectInfo d{ &n, out }; ImGui::SetDragDropPayload("K_PLUG_OUT_TO_IN", &d, sizeof(d)); ImGui::EndDragDropSource(); } if (ImGui::BeginDragDropTarget()) { if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("K_PLUG_IN_TO_OUT")) { Plugboard::ConnectInfo in = *(const Plugboard::ConnectInfo*)payload->Data; - Plugboard::Connect(*in.p, in.index, instance, out); + Plugboard::Connect(in.p, in.index, &n, out); s.plugboard.RecollectChains(); } ImGui::EndDragDropTarget(); @@ -224,7 +224,7 @@ namespace K::UI { // Update link info if needed if (!n.out[out].outgoing.empty()) {\ - link_pos[{&instance, out}].source = ImGui::GetCursorScreenPos() + ImVec2{ImGui::GetFrameHeight() / 2, + link_pos[{&n, out}].source = ImGui::GetCursorScreenPos() + ImVec2{ImGui::GetFrameHeight() / 2, -ImGui::GetFrameHeight() / 2 - style.ItemInnerSpacing.y}; } @@ -236,14 +236,14 @@ namespace K::UI { ImGui::RadioButton("##In", false); if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) { - Plugboard::ConnectInfo d{ &instance, in }; + Plugboard::ConnectInfo d{ &n, in }; ImGui::SetDragDropPayload("K_PLUG_IN_TO_OUT", &d, sizeof(d)); ImGui::EndDragDropSource(); } if (ImGui::BeginDragDropTarget()) { if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("K_PLUG_OUT_TO_IN")) { Plugboard::ConnectInfo out = *(const Plugboard::ConnectInfo*)payload->Data; - Plugboard::Connect(instance, in, *out.p, out.index); + Plugboard::Connect(&n, in, out.p, out.index); s.plugboard.RecollectChains(); } ImGui::EndDragDropTarget(); @@ -279,15 +279,15 @@ namespace K::UI { } template - bool PlugboardDrawNode(CompState& s, N& n, Plugboard::NodeInstance& instance, ImVec2& view_pos, i32 id, ImGuiIO& io, ImGuiStyle& style, - bool& dragging_on_socket, ImVec2& source, Dict& link_pos, bool selected) { + 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; const auto& pos = n.pos; const auto& name = n.name; ImGui::SetCursorPos(view_pos + pos); - ImGui::PushID(id); - ImGui::PushStyleColor(ImGuiCol_ChildBg, selected ? 0xAA202040 : 0xAA080813); + ImGui::PushID(&n); + ImGui::PushStyleColor(ImGuiCol_ChildBg, 0xAA080813); static ImVec2 old_pos{}; if (ImGui::BeginChild(name, {}, ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_Border)) { if (ImGui::BeginTable(name, 3)) { @@ -304,7 +304,7 @@ namespace K::UI { } ImGui::TableNextColumn(); - if constexpr (std::is_same_v) { + if constexpr (requires (N node) { { node.Draw() } -> std::same_as; }) { ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::TableNextColumn(); @@ -312,7 +312,7 @@ namespace K::UI { ImGui::TableNextColumn(); } - PlugboardNodeDrawSockets(s, n, instance, style, dragging_on_socket, source, link_pos); + PlugboardNodeDrawSockets(s, n, style, dragging_on_socket, source, link_pos); ImGui::EndTable(); } @@ -428,25 +428,22 @@ namespace K::UI { static u32 node_current{}; if (ImGui::Button("Add")) { - auto n = Plugboard::MakeInstance(Plugboard::K_P_Nodes(node_current)); - s.plugboard.nodes.push_back(std::move(n)); + s.plugboard.nodes.Store(static_cast(node_current)); } ImGui::SameLine(); ImGui::SetNextItemWidth(150.0f); - std::visit([](auto&& arg) { - if (ImGui::BeginCombo("##Node", arg.name, ImGuiComboFlags_None)) { - for (u32 n = 0; n < Plugboard::K_P_Count; n++) { - const bool is_selected = (node_current == n); - if (ImGui::Selectable(std::visit([](auto&& a) { return a.name; }, Plugboard::MakeInstance(Plugboard::K_P_Nodes(node_current))), is_selected)) - node_current = n; + if (ImGui::BeginCombo("##Node", s.plugboard.nodes.GetName(static_cast(node_current)), ImGuiComboFlags_None)) { + for (u32 n = 0; n < Plugboard::K_P_Count; n++) { + const bool is_selected = (node_current == n); + if (ImGui::Selectable(s.plugboard.nodes.GetName(static_cast(n)), is_selected)) + node_current = n; - // Set the initial focus when opening the combo (scrolling + keyboard navigation focus) - if (is_selected) - ImGui::SetItemDefaultFocus(); - } - ImGui::EndCombo(); + // Set the initial focus when opening the combo (scrolling + keyboard navigation focus) + if (is_selected) + ImGui::SetItemDefaultFocus(); } - }, Plugboard::MakeInstance(Plugboard::K_P_Nodes(node_current))); // COPIES!!! + ImGui::EndCombo(); + } ImGui::SameLine(); if (ImGui::Button(playing ? "Pause" : "Play")) @@ -509,8 +506,6 @@ namespace K::UI { ImGui::PushStyleColor(ImGuiCol_ChildBg, 0xFF100500); ImGui::SetNextWindowSizeConstraints({ImGui::GetContentRegionAvail().x * .1f, -1}, { ImGui::GetContentRegionAvail().x * .8f, -1 }); if (ImGui::BeginChild("Nodes", {ImGui::GetContentRegionAvail().x * .4f, ImGui::GetContentRegionAvail().y}, ImGuiChildFlags_ResizeX, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar)) { - static u32 selected; - ImGuiWindow *window = ImGui::GetCurrentWindow(); ImVec2 window_pos = ImGui::GetWindowPos(); nodes_begin = window_pos; @@ -518,7 +513,7 @@ namespace K::UI { if (ImGui::BeginDragDropTargetCustom(window->ContentRegionRect, window->ID)) { if (const ImGuiPayload *payload = ImGui::AcceptDragDropPayload("K_PLUG_IN_TO_OUT")) { Plugboard::ConnectInfo in = *(const Plugboard::ConnectInfo *) payload->Data; - Plugboard::Disconnect(*in.p, in.index); + Plugboard::Disconnect(in.p, in.index); } ImGui::EndDragDropTarget(); } @@ -540,14 +535,15 @@ namespace K::UI { view_pos = old_view_pos + ImGui::GetMouseDragDelta(); } - u32 i = 0; - for (auto it = s.plugboard.nodes.begin(); it != s.plugboard.nodes.end(); it++, i++) - std::visit([&it, &i, &io, &style, &dragging_on_socket, &drag_source, &s](auto&& arg) { - using T = std::decay_t; - if (PlugboardDrawNode(s, arg, *it, view_pos, static_cast(i), io, style, dragging_on_socket, - drag_source, s.plugboard.links_pos, selected == i)) - selected = i; - }, *it); + std::apply([&](auto&&... args) { + (([&](auto&& arg){ + for (auto& node : arg) { + PlugboardDrawNode>(s, node, view_pos, io, style, dragging_on_socket, + drag_source, s.plugboard.links_pos); + } + }(args)), ...); + }, s.plugboard.nodes.nodes); + ImGui::SetCursorPos({}); ImGui::PushStyleColor(ImGuiCol_ChildBg, 0x33FFFFFF); ImGui::BeginChild("Composition In", {}, @@ -555,7 +551,7 @@ namespace K::UI { ImGuiChildFlags_AutoResizeY); ImGui::PopStyleColor(); if (ImGui::BeginTable("Composition In", 3)) { - PlugboardNodeDrawSockets(s, std::get(s.plugboard.in), s.plugboard.in, style, dragging_on_socket, drag_source, + PlugboardNodeDrawSockets(s, s.plugboard.in, style, dragging_on_socket, drag_source, s.plugboard.links_pos, false); ImGui::EndTable(); } @@ -582,7 +578,7 @@ namespace K::UI { f32 view_amt = view_right - view_left, mouse_tl_x; f32 fr_step; - static Vector selected_chains{}; + static Vector selected_chains{}; // Mouse Handlers // Drag playhead/pan @@ -950,13 +946,13 @@ namespace K::UI { if (ImGui::BeginDragDropTarget()) { if (const ImGuiPayload *payload = ImGui::AcceptDragDropPayload("K_PLUG_OUT_TO_IN")) { auto out = *(const Plugboard::ConnectInfo *) payload->Data; - Plugboard::Connect(*u.connection.p, u.connection.index, *out.p, out.index); + Plugboard::Connect(u.connection.p, u.connection.index, out.p, out.index); s.plugboard.RecollectChains(); } ImGui::EndDragDropTarget(); } - auto& connected_v = std::visit([&u](auto&& arg) -> auto& { return arg.in[u.connection.index].value; }, *u.connection.p); + auto& connected_v = std::visit([&u](auto&& arg) -> auto& { return arg->in[u.connection.index].value; }, u.connection.p); std::visit([&u, &dragging_on_socket, &drag_source, &s, &style, &io](auto &&arg) { using T = std::decay_t; @@ -971,13 +967,12 @@ namespace K::UI { } else if (ImGui::IsItemActive()) { if (ImGui::IsKeyPressed(ImGuiKey_LeftShift)) { - s.plugboard.nodes.push_back(Plugboard::MakeInstance(Plugboard::K_P_Chain)); - auto it = s.plugboard.nodes.rbegin(); - Plugboard::Connect(*u.connection.p, u.connection.index, *it, 0); - Plugboard::Connect(*it, 0, s.plugboard.in, 0); - auto& nodes = std::get(*u.connection.p).show_nodes[u.connection.index]; + 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]; nodes.clear(); - nodes.emplace_back(&*it, 0); + nodes.emplace_back(&c, 0); } else { dragging_on_socket = true; @@ -1057,7 +1052,7 @@ namespace K::UI { draw_tl_bg_drag_area(); 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: std::get(u.connection.p)->show_nodes[u.connection.index]) { ImGui::TableNextRow(ImGuiTableRowFlags_None, row_height); ImGui::TableSetColumnIndex(0); ImGui::TableSetColumnIndex(1); @@ -1068,7 +1063,7 @@ namespace K::UI { auto chain_sel_it = std::find(selected_chains.begin(), selected_chains.end(), info.p); if (chain_sel_it != selected_chains.end()) n_flags |= ImGuiTreeNodeFlags_Selected; - ImGui::TreeNodeEx(std::visit([](auto&& arg){ return arg.name; }, *info.p), n_flags); + 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); @@ -1077,14 +1072,14 @@ namespace K::UI { } ImGui::TableSetColumnIndex(2); - auto& [chain, chain_copy] = std::get(*info.p).extra; + auto& [chain, chain_copy] = std::get(info.p)->extra; if (tl_clear_selection_request) chain.selected.clear(); - bool m_empty = chain.segments.empty(), m_has_before = !m_empty && chain.segments.begin()->frame >= s.current_frame; + bool m_empty = chain.segments.empty(), m_has_before = !m_empty && chain.segments.begin()->frame < s.current_frame; - ImGui::BeginDisabled(m_empty || m_has_before); + ImGui::BeginDisabled(m_empty || !m_has_before); if (ImGui::Button("<")) { s.current_frame = std::prev(std::lower_bound(chain.segments.begin(), chain.segments.end(), s.current_frame, @@ -1191,7 +1186,7 @@ namespace K::UI { if constexpr (std::is_same_v) s.plugboard.links_pos[arg].sinks.push_back( {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)); + }, std::visit([&u](auto&& arg) -> auto& { return arg->in[u.connection.index].value; }, u.connection.p)); ImGui::PopStyleVar(); // FramePadding @@ -1282,7 +1277,7 @@ namespace K::UI { u32 keys = 0; for (const auto& n : selected_chains) { Plugboard::ConnectInfo i {n, 0}; // it's a chain -- 0 is out - auto& [chain, chain_copy] = std::get(*n).extra; + auto& [chain, chain_copy] = std::get(n)->extra; if (started_dragging) chain_copy = chain; @@ -1304,14 +1299,14 @@ namespace K::UI { i32 frame = k; f64 v = val; - i32 drag_off_x; // data coords + f64 drag_off_x; // data coords f64 drag_off_y; // data coords ImPlotPoint mouse_pos = ImPlot::PixelsToPlot(io.MousePos), mouse_clicked_pos = ImPlot::PixelsToPlot(io.MouseClickedPos[ImGuiMouseButton_Left]); - drag_off_x = static_cast(std::round(mouse_pos.x - mouse_clicked_pos.x)); + drag_off_x = mouse_pos.x - mouse_clicked_pos.x; drag_off_y = mouse_pos.y - mouse_clicked_pos.y; if (dragging && is_sel) { - frame += drag_off_x; + frame += static_cast(std::round(drag_off_x)); v += drag_off_y; } @@ -1333,7 +1328,7 @@ namespace K::UI { i32 frame_nxt = k_nxt; f64 v_nxt = val_nxt; if (dragging && is_nxt_sel) { - frame_nxt += drag_off_x; + frame_nxt += static_cast(std::round(drag_off_x)); v_nxt += drag_off_y; } @@ -1518,7 +1513,7 @@ namespace K::UI { ImGui::TableHeadersRow(); i32 i = 0; for (auto it = s.layers[s.active].track.uniforms.begin(); it != s.layers[s.active].track.uniforms.end();) { - if (it->connection.p == nullptr) + if (std::visit([](auto&& arg){ return arg == nullptr; }, it->connection.p)) continue; ImGui::TableNextRow(); @@ -1535,9 +1530,9 @@ namespace K::UI { if constexpr (std::is_same_v::type>) DrawPlugboardVariableEditWidget(arg); else - ImGui::Text("Plugged into %s!", std::visit([](auto&& d) { return d.name; }, *arg.p)); - }, n.in[it->connection.index].value); - }, *it->connection.p); + ImGui::Text("Plugged into %s!", std::visit([](auto&& d) { return d->name; }, arg.p)); + }, n->in[it->connection.index].value); + }, it->connection.p); ImGui::TableNextColumn(); if (ImGui::Button("X")) { bgfx::destroy(it->handle); @@ -1607,8 +1602,8 @@ 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]) { - auto& [chain, _] = std::get(*n.p).extra; + for (auto& n : std::get(u.connection.p)->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() && diff --git a/Keishiki/VisualTrack.cpp b/Keishiki/VisualTrack.cpp index 2f275a7..e890d81 100644 --- a/Keishiki/VisualTrack.cpp +++ b/Keishiki/VisualTrack.cpp @@ -48,7 +48,7 @@ namespace K { for (auto& u : uniforms) { f32 pack[4]{}; ShaderGraph::T_Map::type val_target; - if (u.connection.p == nullptr) + if (std::visit([](auto&& arg){ return arg == nullptr; }, u.connection.p)) val_target = u.val; else { std::visit([&u, &s, &val_target](auto&& arg) { @@ -61,7 +61,7 @@ namespace K { } else if constexpr (std::is_same_v::type>) val_target = PlugboardValToShader(arg); - }, std::visit([&u](auto&& arg) { return arg.in[u.connection.index].value; }, *u.connection.p)); + }, std::visit([&u](auto&& arg) { return arg->in[u.connection.index].value; }, u.connection.p)); } std::visit([&pack](auto&& arg) { using T = std::decay_t; @@ -182,11 +182,10 @@ namespace K { } void VisualTrack::ExposeUniform(CompState& s, Uniform& uu) { - auto& n = std::get(s.plugboard.out); - uu.connection = {&s.plugboard.out, static_cast(n.in.size())}; + uu.connection = {&s.plugboard.out, static_cast(s.plugboard.out.in.size())}; // this is shaky and bug prone -- relies on the shader types being in line with plugboard types - n.in.push_back(Plugboard::InSocket{ uu.name, Plugboard::Type(uu.val.index()), ShaderValToPlugboard(uu.val)}); - n.show_nodes.push_back({}); + s.plugboard.out.in.push_back(Plugboard::InSocket{ uu.name, Plugboard::Type(uu.val.index()), ShaderValToPlugboard(uu.val)}); + s.plugboard.out.show_nodes.emplace_back(); } void VisualTrack::ExposeUniform(CompState& s, u32 i) { @@ -212,7 +211,7 @@ namespace K { for (auto& ss : samplers) if (ss.name == s) return; - uniforms.emplace_back(Uniform{s, bgfx::createUniform(("__" + s).c_str(), bgfx::UniformType::Vec4), val, {nullptr, 0}}); + uniforms.emplace_back(Uniform{s, bgfx::createUniform(("__" + s).c_str(), bgfx::UniformType::Vec4), val, {{}, 0}}); } void VisualTrack::AddSampler(const String& s) { diff --git a/Keishiki/include/Plugboard.h b/Keishiki/include/Plugboard.h index 3707ba0..e2c1f3a 100644 --- a/Keishiki/include/Plugboard.h +++ b/Keishiki/include/Plugboard.h @@ -10,6 +10,15 @@ #include namespace K::Plugboard { + template + concept Node = requires(T node) { + { T::name } -> std::same_as; + { node.in.size() } -> std::convertible_to; + { node.out.size() } -> std::convertible_to; + { node.fetch.size() } -> std::convertible_to; // in the future enforce same_as, tuple_size> if both are static arrays + { node.pos } -> std::convertible_to; + }; + enum Type { T_Float, T_Int, @@ -82,32 +91,6 @@ namespace K::Plugboard { struct CompositionIn; struct CompositionOut; - using NodeInstance = std::variant< - Add, - Negate, - Subtract, - Multiply, - Divide, - Sign, - Sin, - Cos, - Tan, - Arcsin, - Arccos, - Arctan, - Atan2, - Minimum, - Maximum, - Power, - SquareRoot, - NaturalLogarithm, - AbsoluteValue, - Interpolation, - Chain, - CompositionIn, - CompositionOut - >; - enum K_P_Nodes { K_P_Add = 0, K_P_Negate, @@ -135,7 +118,38 @@ namespace K::Plugboard { K_P_Count }; - NodeInstance MakeInstance(K_P_Nodes i); + template + struct NodeInstanceV { + using NodeInstanceP = std::variant; + static_assert(std::variant_size_v == K_P_Count); + }; + + using NodeVar = NodeInstanceV< + Add, + Negate, + Subtract, + Multiply, + Divide, + Sign, + Sin, + Cos, + Tan, + Arcsin, + Arccos, + Arctan, + Atan2, + Minimum, + Maximum, + Power, + SquareRoot, + NaturalLogarithm, + AbsoluteValue, + Interpolation, + Chain, + CompositionIn, + CompositionOut>; + + using NodeInstanceP = NodeVar::NodeInstanceP; enum K_Interpolation { K_I_CubicBezier, @@ -315,7 +329,7 @@ namespace K::Plugboard { }; struct ConnectInfo { - NodeInstance *p; // NOTE: so NodeInstances must be stored in a list, otherwise vector realloc leads to UAF + NodeInstanceP p; // NOTE: so NodeInstances must be stored in a list, otherwise vector realloc leads to UAF u32 index; auto operator<=>(const ConnectInfo&) const = default; // too-modern c++ black magic ? }; @@ -332,42 +346,10 @@ namespace K::Plugboard { Vector outgoing; }; - template - requires requires(F func, ConnectInfo p) { - { func(p) } -> std::same_as; // abort condition -- continue if true - } - bool DFS(const NodeInstance& n, F&& f) { // returns false if aborted - return std::visit([&f](auto&& node) { - for (const auto& socket : node.in) - if (!std::visit([&f](auto&& info) { - if constexpr (std::is_same_v, ConnectInfo>) { - if (f(info)) - return DFS(*info.p, f); - else - return false; - } - else return true; - }, socket.value)) - return false; - return true; - }, n); - } - - void CollectChains(ConnectInfo& in, Vector& chains); - - template - concept Node = requires(T node) { - { T::name } -> std::same_as; - { node.in.size() } -> std::convertible_to; - { node.out.size() } -> std::convertible_to; - { node.fetch.size() } -> std::convertible_to; // in the future enforce same_as, tuple_size> if both are static arrays - { node.pos } -> std::convertible_to; - }; - struct ConnectInfoHasher { std::size_t operator()(const ConnectInfo& k) const { - return std::hash()(k.p) ^ std::hash()(k.index); + return std::hash()(k.p) ^ std::hash()(k.index); } }; @@ -592,14 +574,113 @@ namespace K::Plugboard { ImVec2 pos; }; + template + concept SameAsAny = (... or std::same_as); + + template + struct TNodeList { + static_assert(sizeof...(Ns) == K_P_Count); + + template N> + auto& Store(const N& v) { + return std::get>(nodes).push_back(v); + } + template N> + auto& Store(N&& v) { + return std::get>(nodes).emplace_back(std::forward(v)); + } + auto& Store(const std::variant& v) { + return std::visit([this](auto&& arg) { return store(arg); }, v); + } + auto& Store(std::variant&& v) { + return std::visit([this](auto&& arg) { return store(std::forward(arg)); }, std::move(v)); + } + + std::tuple...> nodes; + + template using NthTypeOf = + typename std::tuple_element>::type; + + template + void Store(K_P_Nodes i, std::index_sequence) { + ((Is == i ? Store(NthTypeOf{}) : NthTypeOf{}), ...); + } + + void Store(K_P_Nodes i) { + Store(i, std::index_sequence_for{}); + } + + template + char const *GetName(K_P_Nodes i, std::index_sequence) { + char const *p = nullptr; + ((Is == i ? p = NthTypeOf::name : nullptr), ...); + return p; + } + + char const *GetName(K_P_Nodes i) { + return GetName(i, std::index_sequence_for{}); + } + }; + + using NodeList = TNodeList< + Add, + Negate, + Subtract, + Multiply, + Divide, + Sign, + Sin, + Cos, + Tan, + Arcsin, + Arccos, + Arctan, + Atan2, + Minimum, + Maximum, + Power, + SquareRoot, + NaturalLogarithm, + AbsoluteValue, + Interpolation, + Chain, + CompositionIn, + CompositionOut + >; + struct Plugboard { - std::list nodes; // OK complexity wise since we would usually traverse the entire thing anyway, locality will likely be bad + NodeList nodes; // OK complexity wise since we would usually traverse the entire thing anyway, locality will likely be bad Dict links_pos; // this is hilariously bad - NodeInstance in, out; + CompositionIn in; + CompositionOut out; void RecollectChains(); }; - void Connect(NodeInstance& from_instance, u8 input_index, NodeInstance& to_instance, u8 to_out_index); + void Connect(NodeInstanceP from_instance, u8 input_index, NodeInstanceP to_instance, u8 to_out_index); - void Disconnect(NodeInstance& from_instance, u8 input_index); + void Disconnect(NodeInstanceP from_instance, u8 input_index); + + + template + requires requires(F func, ConnectInfo p) { + { func(p) } -> std::same_as; // abort condition -- continue if true + } + bool DFS(const NodeInstanceP n, F&& f) { // returns false if aborted + return std::visit([&f](auto&& node) { + for (const auto& socket : node->in) + if (!std::visit([&f](auto&& info) { + if constexpr (std::is_same_v, ConnectInfo>) { + if (f(info)) + return DFS(info.p, f); + else + return false; + } + else return true; + }, socket.value)) + return false; + return true; + }, n); + } + + void CollectChains(ConnectInfo& in, Vector& chains); } diff --git a/TODO.md b/TODO.md index 8af0362..04cb5ed 100644 --- a/TODO.md +++ b/TODO.md @@ -1,13 +1,4 @@ # NOW -## UI -- Keys - - deletion - - copy & paste -- Undo's - - Once this is done we can move the plugboard loop detection/chain finding out of the eval loop -- Node groups -- Simple export - ## Compositor - Video! - Integrate with samplers @@ -17,12 +8,18 @@ - Data models - Dump and read back state, (de)serialization!!! - Pre-compose/Layer Groups (jokes -- can be completely UI side) + - 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 +## UI +- Copy & Paste +- Simple export + # Later ## IO - File dialogues pending SDL3 @@ -47,3 +44,4 @@ - Wait for SDL3! - SDL_mixer will be able to do all of wav ogg flac mp3 opus, we live in good times - output needs to be handled differently (if we care at all -- likely not) +z \ No newline at end of file