mirror of
https://github.com/SatDump/SatDump
synced 2026-08-13 17:47:30 -04:00
361 lines
No EOL
14 KiB
C++
361 lines
No EOL
14 KiB
C++
#include "common/widgets/CircularProgressBar.h"
|
|
#include "core/style.h"
|
|
#include "dsp/block.h"
|
|
#include "dsp/block_helpers.h"
|
|
#include "flowgraph.h"
|
|
#include "imgui/imgui.h"
|
|
#include "imgui/imnodes/imnodes.h"
|
|
#include "utils/string.h"
|
|
#include <mutex>
|
|
|
|
namespace satdump
|
|
{
|
|
namespace ndsp
|
|
{
|
|
namespace flowgraph
|
|
{
|
|
void Flowgraph::renderAddMenu(std::pair<const std::string, NodeInternalReg> &opt, std::vector<std::string> cats, int pos)
|
|
{
|
|
if (pos == (cats.size() - 1))
|
|
{
|
|
if (ImGui::MenuItem(cats[pos].c_str()))
|
|
{
|
|
auto mpos = ImGui::GetMousePos();
|
|
auto ptr = addNode(opt.first, opt.second.func(this));
|
|
ptr->pos_was_set = true;
|
|
ImNodes::SetNodeScreenSpacePos(ptr->id, mpos);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (ImGui::BeginMenu(cats[pos].c_str()))
|
|
{
|
|
renderAddMenu(opt, cats, pos + 1);
|
|
ImGui::EndMenu();
|
|
}
|
|
}
|
|
}
|
|
|
|
void Flowgraph::renderCatT(CatT &cats, bool searching)
|
|
{
|
|
for (auto &c : cats.cats)
|
|
{
|
|
if (searching)
|
|
ImGui::SetNextItemOpen(true);
|
|
|
|
if (ImGui::TreeNode(c.first.c_str()))
|
|
{
|
|
renderCatT(c.second, searching);
|
|
ImGui::TreePop();
|
|
}
|
|
}
|
|
|
|
for (auto &c : cats.sub)
|
|
{
|
|
if (ImGui::TreeNodeEx(c.first.c_str(), ImGuiTreeNodeFlags_Leaf))
|
|
ImGui::TreePop();
|
|
if (ImGui::IsItemClicked())
|
|
addNode(c.first, c.second.func(this));
|
|
}
|
|
}
|
|
|
|
void Flowgraph::renderAddMenuList(std::string search)
|
|
{
|
|
std::lock_guard<std::mutex> lg(flow_mtx);
|
|
|
|
// Extract categories
|
|
std::vector<NodeInternalReg> regs;
|
|
std::vector<std::vector<std::string>> categs;
|
|
for (auto &opt : node_internal_registry)
|
|
{
|
|
if (search.size() && !isStringPresent(opt.second.menuname, search))
|
|
continue;
|
|
|
|
regs.push_back(opt.second);
|
|
categs.push_back(splitString(opt.second.menuname, '/'));
|
|
}
|
|
|
|
CatT cats;
|
|
|
|
// Organize in a recursive fashion
|
|
for (int i = 0; i < regs.size(); i++)
|
|
{
|
|
CatT *cCats = &cats;
|
|
for (int c = 0; c < categs[i].size(); c++)
|
|
{
|
|
std::string cat = categs[i][c];
|
|
|
|
if (c == categs[i].size() - 1)
|
|
{
|
|
cCats->sub.emplace(cat, regs[i]);
|
|
}
|
|
else
|
|
{
|
|
if (!cCats->cats.count(cat))
|
|
cCats->cats.emplace(cat, CatT());
|
|
cCats = &cCats->cats[cat];
|
|
}
|
|
}
|
|
}
|
|
|
|
// Render
|
|
renderCatT(cats, search.size());
|
|
}
|
|
|
|
void Flowgraph::render()
|
|
{
|
|
std::lock_guard<std::mutex> lg(flow_mtx);
|
|
|
|
ImNodes::PushStyleVar(ImNodesStyleVar_PinCircleRadius, 6);
|
|
ImNodes::PushStyleVar(ImNodesStyleVar_LinkThickness, 4);
|
|
ImNodes::PushAttributeFlag(ImNodesAttributeFlags_EnableLinkDetachWithDragClick);
|
|
|
|
ImNodes::BeginNodeEditor();
|
|
ImNodes::MiniMap();
|
|
|
|
for (auto &n : nodes)
|
|
{
|
|
if (n->disabled)
|
|
style::beginDisabled();
|
|
|
|
ImNodes::BeginNode(n->id);
|
|
ImNodes::BeginNodeTitleBar();
|
|
ImGui::Text("%s", n->title.c_str());
|
|
ImNodes::EndNodeTitleBar();
|
|
|
|
// Update if needed
|
|
if (n->internal->render())
|
|
n->updateIO();
|
|
|
|
int in_pos = 0;
|
|
for (auto &io : n->node_io)
|
|
{
|
|
ImNodes::PushColorStyle(ImNodesCol_Pin, getColorFromDSPType(io.type));
|
|
if (io.is_out)
|
|
{
|
|
ImNodes::BeginOutputAttribute(io.id);
|
|
const float node_width = ImNodes::GetNodeDimensions(n->id).x;
|
|
const float label_width = ImGui::CalcTextSize(io.name.c_str()).x;
|
|
ImGui::Indent(node_width - label_width - 20 * ui_scale);
|
|
ImGui::Text("%s", io.name.c_str());
|
|
ImNodes::EndOutputAttribute();
|
|
}
|
|
else
|
|
{
|
|
ImNodes::BeginInputAttribute(io.id);
|
|
ImGui::Text("%s", io.name.c_str());
|
|
|
|
if (is_running && debug_mode)
|
|
{
|
|
if (in_pos < n->internal->blk->get_inputs().size())
|
|
{
|
|
auto f = n->internal->blk->get_inputs()[in_pos];
|
|
|
|
if (f.fifo)
|
|
{
|
|
float v = (float)f.fifo->size_approx() / (float)f.fifo->max_capacity();
|
|
ImGui::SameLine();
|
|
if (v > 1)
|
|
v = 1;
|
|
if (v < 0)
|
|
v = 0;
|
|
CircularProgressBar(f.name.c_str(), v, {20, 20}, style::theme.green);
|
|
}
|
|
}
|
|
}
|
|
|
|
ImNodes::EndInputAttribute();
|
|
in_pos++;
|
|
}
|
|
ImNodes::PopColorStyle();
|
|
}
|
|
|
|
ImNodes::EndNode();
|
|
|
|
if (!n->pos_was_set)
|
|
{
|
|
ImNodes::SetNodeGridSpacePos(n->id, {n->pos_x, n->pos_y});
|
|
n->pos_was_set = true;
|
|
}
|
|
|
|
auto pos = ImNodes::GetNodeGridSpacePos(n->id);
|
|
n->pos_x = pos.x;
|
|
n->pos_y = pos.y;
|
|
|
|
if (n->disabled)
|
|
style::endDisabled();
|
|
}
|
|
|
|
for (auto &l : links)
|
|
{
|
|
bool disabled = false;
|
|
|
|
BlockIOType type;
|
|
for (auto &n : nodes)
|
|
for (auto &io : n->node_io)
|
|
{
|
|
if (io.id == l.start)
|
|
{
|
|
type = io.type;
|
|
if (n->disabled)
|
|
disabled = true;
|
|
}
|
|
else if (io.id == l.end)
|
|
{
|
|
if (n->disabled)
|
|
disabled = true;
|
|
}
|
|
}
|
|
|
|
ImNodes::PushColorStyle(ImNodesCol_Link, disabled ? (ImColor)ImGui::GetColorU32(ImGuiCol_TextDisabled) : getColorFromDSPType(type));
|
|
ImNodes::Link(l.id, l.start, l.end);
|
|
ImNodes::PopColorStyle();
|
|
}
|
|
|
|
ImNodes::EndNodeEditor();
|
|
|
|
ImNodes::PopAttributeFlag();
|
|
ImNodes::PopStyleVar();
|
|
ImNodes::PopStyleVar();
|
|
|
|
// Lock down edition when running!
|
|
if (!is_running)
|
|
{
|
|
int start_att, end_att;
|
|
if (ImNodes::IsLinkCreated(&start_att, &end_att))
|
|
links.push_back({getNewLinkID(), start_att, end_att});
|
|
|
|
int link_id;
|
|
if (ImNodes::IsLinkDestroyed(&link_id))
|
|
{
|
|
auto iter = std::find_if(links.begin(), links.end(), [link_id](const Link &link) -> bool { return link.id == link_id; });
|
|
links.erase(iter);
|
|
}
|
|
|
|
///////////////////// Node Key Handlers
|
|
if (!ImGui::IsAnyItemActive() && ImGui::IsKeyPressed(ImGuiKey_Delete))
|
|
{
|
|
int node_s = ImNodes::NumSelectedNodes();
|
|
|
|
if (node_s > 0)
|
|
{
|
|
std::vector<int> nodes_ids(node_s);
|
|
ImNodes::GetSelectedNodes(nodes_ids.data());
|
|
|
|
for (auto &id : nodes_ids)
|
|
{
|
|
auto iter = std::find_if(nodes.begin(), nodes.end(), [id](const std::shared_ptr<Node> &node) -> bool { return node->id == id; });
|
|
|
|
for (auto &linkid : iter->get()->node_io)
|
|
{
|
|
auto liter = std::find_if(links.begin(), links.end(), [linkid](const Link &link) -> bool { return link.start == linkid.id || link.end == linkid.id; });
|
|
if (liter != links.end())
|
|
links.erase(liter);
|
|
}
|
|
|
|
nodes.erase(iter);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!ImGui::IsAnyItemActive() && ImGui::IsKeyPressed(ImGuiKey_D))
|
|
{
|
|
int node_s = ImNodes::NumSelectedNodes();
|
|
|
|
if (node_s > 0)
|
|
{
|
|
std::vector<int> nodes_ids(node_s);
|
|
ImNodes::GetSelectedNodes(nodes_ids.data());
|
|
|
|
for (auto &id : nodes_ids)
|
|
{
|
|
auto iter = std::find_if(nodes.begin(), nodes.end(), [id](const std::shared_ptr<Node> &node) -> bool { return node->id == id; });
|
|
iter->get()->disabled = !iter->get()->disabled;
|
|
}
|
|
}
|
|
}
|
|
///////////////////// Node Key Handlers end
|
|
|
|
if (ImGui::IsMouseClicked(ImGuiMouseButton_Right))
|
|
ImGui::OpenPopup("##popuprightclickflowgraph");
|
|
if (ImGui::BeginPopup("##popuprightclickflowgraph"))
|
|
{
|
|
if (ImGui::BeginMenu("Add Node"))
|
|
{
|
|
for (auto &opt : node_internal_registry)
|
|
{
|
|
std::vector<std::string> cats = splitString(opt.second.menuname, '/');
|
|
renderAddMenu(opt, cats, 0);
|
|
}
|
|
ImGui::EndMenu();
|
|
}
|
|
|
|
ImGui::EndPopup();
|
|
}
|
|
}
|
|
|
|
if (!is_running)
|
|
{
|
|
int id = 0;
|
|
if (ImNodes::IsNodeHovered(&id) && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
|
|
{
|
|
for (auto &n : nodes)
|
|
if (n->id == id)
|
|
n->show_vars_win = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Flowgraph::renderWindows()
|
|
{
|
|
std::lock_guard<std::mutex> lg(flow_mtx);
|
|
|
|
for (auto &n : nodes)
|
|
{
|
|
if (n->show_vars_win)
|
|
{
|
|
std::string name = n->internal->blk->d_id + "##nodevarspopup";
|
|
ImGui::Begin(name.c_str(), &n->show_vars_win, ImGuiWindowFlags_AlwaysAutoResize);
|
|
for (auto &v : n->vars)
|
|
{
|
|
ImGui::SeparatorText(v.first.c_str());
|
|
ImGui::InputText(std::string("##" + v.first).c_str(), &v.second);
|
|
ImGui::SameLine();
|
|
|
|
if (ImGui::Button(("Delete##" + name + v.first).c_str()))
|
|
{
|
|
n->vars.erase(v.first);
|
|
break;
|
|
}
|
|
}
|
|
|
|
ImGui::SeparatorText("Add others");
|
|
|
|
int nl = 1;
|
|
for (auto &v : n->internal->blk->get_cfg().items())
|
|
{
|
|
bool found = false;
|
|
for (auto &v2 : n->vars)
|
|
if (v2.first == v.key())
|
|
found = true;
|
|
if (found)
|
|
continue;
|
|
|
|
std::string bname = v.key() + "##nodeaddbtn";
|
|
if (ImGui::Button(bname.c_str()))
|
|
{
|
|
n->vars.emplace(v.key(), "");
|
|
}
|
|
|
|
if (nl && !(nl % 3 == 0))
|
|
ImGui::SameLine();
|
|
nl++;
|
|
}
|
|
|
|
ImGui::End();
|
|
}
|
|
}
|
|
}
|
|
} // namespace flowgraph
|
|
} // namespace ndsp
|
|
} // namespace satdump
|