0.5.0
Loading...
Searching...
No Matches
NodeEditorApplication.cpp
Go to the documentation of this file.
1// This file is part of INSTINCT, the INS Toolkit for Integrated
2// Navigation Concepts and Training by the Institute of Navigation of
3// the University of Stuttgart, Germany.
4//
5// This Source Code Form is subject to the terms of the Mozilla Public
6// License, v. 2.0. If a copy of the MPL was not distributed with this
7// file, You can obtain one at https://mozilla.org/MPL/2.0/.
8
10
11#include <fmt/ranges.h>
12
13#include <imgui_node_editor.h>
14#include <imgui_node_editor_internal.h>
15namespace ed = ax::NodeEditor;
16
17#include <imgui_internal.h>
18#include <imgui_stdlib.h>
19
20#include "ImGuiFileDialog.h"
21
22#include "implot.h"
23#include <implot_internal.h>
24
28
31
33
35
40
42
43#include "internal/Node/Pin.hpp"
45
47namespace nm = NAV::NodeManager;
48#include "NodeRegistry.hpp"
49
53
54#include "util/Json.hpp"
55#include "util/StringUtil.hpp"
56
57#include <string>
58#include <array>
59#include <vector>
60#include <map>
61#include <algorithm>
62#include <utility>
63#include <filesystem>
64
65#include "internal/CMakeRC.hpp"
66
67namespace NAV::gui
68{
69namespace
70{
71
72ax::NodeEditor::EditorContext* m_Editor = nullptr;
73
74} // namespace
75} // namespace NAV::gui
76
78{
79 LOG_TRACE("called");
80
81 ed::Config config;
82
83 // config.SettingsFile = "INSTINCT.json";
84 // config.UserPointer = this;
85
86 // Stops the Editor from creating a log file, as we do it ourselves
87 // clang-format off
88 config.SaveSettings = [](const char* /*data*/, size_t /*size*/,
89 ed::SaveReasonFlags /*reason*/, void* /*userPointer*/) -> bool {
90 // clang-format on
91
92 return false;
93 };
94
95 // Trigger the changed bar on the node overview list when a node is moved
96 // clang-format off
97 config.SaveNodeSettings = [](ed::NodeId nodeId, const char* /*data*/, size_t /*size*/,
98 ed::SaveReasonFlags /*reason*/, void* /*userPointer*/) -> bool {
99 // clang-format on
100
101 // auto* self = static_cast<NodeEditorApplication*>(userPointer);
102
104 gui::TouchNode(nodeId);
105
106 return true;
107 };
108
109 m_Editor = ed::CreateEditor(&config);
110 LOG_DATA("Editor created");
111 ed::SetCurrentEditor(m_Editor);
112 LOG_DATA("Editor set as current");
113
114 ImGui::GetStyle().FrameRounding = 4.0F;
115 ed::GetStyle().FlowDuration = 1.0F;
116
117 ImPlot::CreateContext();
118 LOG_DATA("Context created");
119 ImPlot::GetStyle().Use24HourClock = true;
120
121 imPlotReferenceStyle = ImPlot::GetStyle();
122
123 std::filesystem::path imPlotConfigFilepath = flow::GetConfigPath();
124 LOG_DATA("Config path found: {}", imPlotConfigFilepath);
125 if (std::filesystem::path inputPath{ ConfigManager::Get<std::string>("implot-config") };
126 inputPath.is_relative())
127 {
128 imPlotConfigFilepath /= inputPath;
129 }
130 else
131 {
132 imPlotConfigFilepath = inputPath;
133 }
134 std::ifstream imPlotConfigFilestream(imPlotConfigFilepath);
135
136 if (!imPlotConfigFilestream.good())
137 {
138 LOG_ERROR("The ImPlot style config file could not be loaded: {}", imPlotConfigFilepath);
139 }
140 else
141 {
142 json j;
143 imPlotConfigFilestream >> j;
144
145 if (j.contains("implot") && j.at("implot").contains("style"))
146 {
147 j.at("implot").at("style").get_to(ImPlot::GetStyle());
148 }
149 }
150 LOG_DATA("Config read");
151
152 // Add custom colormap and set as default. ConfigManager::LoadGlobalSettings then overrides this default selection if it is saved
153 ImPlotContext& gp = *ImPlot::GetCurrentContext();
154 ImVector<ImVec4> custom;
155 for (int c = 0; c < gp.ColormapData.GetKeyCount(ImPlotColormap_Deep); ++c)
156 {
157 custom.push_back(ImGui::ColorConvertU32ToFloat4(gp.ColormapData.GetKeyColor(ImPlotColormap_Deep, c)));
158 }
159 custom.push_back(ImColor(37, 109, 227));
160 custom.push_back(ImColor(239, 100, 21));
161 custom.push_back(ImColor(21, 228, 69));
162 custom.push_back(ImColor(239, 20, 28));
163 custom.push_back(ImColor(100, 64, 217));
164 custom.push_back(ImColor(164, 78, 2));
165 custom.push_back(ImColor(232, 39, 176));
166 custom.push_back(ImColor(255, 207, 31));
167 custom.push_back(ImColor(54, 228, 174));
168 ImPlot::AddColormap("DeepEx", custom.Data, custom.Size, true);
169 ImPlot::BustItemCache();
170 gp.Style.Colormap = gp.ColormapData.Count - 1;
171 ImPlot::BustItemCache();
172 LOG_DATA("Colormap created");
173
175 LOG_DATA("Global settings loaded");
176
177 auto fs = cmrc::instinct::get_filesystem();
178
179 if (fs.is_file("resources/images/BlueprintBackground.png"))
180 {
181 auto fd = fs.open("resources/images/BlueprintBackground.png");
182
183 LOG_DEBUG("Generating Texture for Blueprint Background ({} byte)", fd.size());
184
185 auto is = cmrc::memstream(const_cast<char*>(fd.begin()), // NOLINT(cppcoreguidelines-pro-type-const-cast)
186 const_cast<char*>(fd.end())); // NOLINT(cppcoreguidelines-pro-type-const-cast)
187
188 std::vector<char> buffer;
189 buffer.resize(fd.size(), '\0');
190
191 is.read(buffer.data(),
192 static_cast<std::streamsize>(buffer.size()));
193
194 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
195 m_HeaderBackground = LoadTexture(reinterpret_cast<const void*>(const_cast<const char*>(buffer.data())),
196 static_cast<int>(fd.size()));
197 }
198 else
199 {
200 m_HeaderBackground = LoadTexture("resources/images/BlueprintBackground.png");
201 }
202
203 if (fs.is_file("resources/images/INSTINCT_Logo_Text_white_small.png"))
204 {
205 auto fd = fs.open("resources/images/INSTINCT_Logo_Text_white_small.png");
206
207 LOG_DEBUG("Generating Texture for INSTINCT Logo (white) ({} byte)", fd.size());
208
209 auto is = cmrc::memstream(const_cast<char*>(fd.begin()), // NOLINT(cppcoreguidelines-pro-type-const-cast)
210 const_cast<char*>(fd.end())); // NOLINT(cppcoreguidelines-pro-type-const-cast)
211
212 std::vector<char> buffer;
213 buffer.resize(fd.size(), '\0');
214
215 is.read(buffer.data(),
216 static_cast<std::streamsize>(buffer.size()));
217
218 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
219 m_InstinctLogo.at(0) = LoadTexture(reinterpret_cast<const void*>(const_cast<const char*>(buffer.data())),
220 static_cast<int>(fd.size()));
221 }
222 else
223 {
224 m_InstinctLogo.at(0) = LoadTexture("resources/images/INSTINCT_Logo_Text_white_small.png");
225 }
226
227 if (fs.is_file("resources/images/INSTINCT_Logo_Text_black_small.png"))
228 {
229 auto fd = fs.open("resources/images/INSTINCT_Logo_Text_black_small.png");
230
231 LOG_DEBUG("Generating Texture for INSTINCT Logo (black) ({} byte)", fd.size());
232
233 auto is = cmrc::memstream(const_cast<char*>(fd.begin()), // NOLINT(cppcoreguidelines-pro-type-const-cast)
234 const_cast<char*>(fd.end())); // NOLINT(cppcoreguidelines-pro-type-const-cast)
235
236 std::vector<char> buffer;
237 buffer.resize(fd.size(), '\0');
238
239 is.read(buffer.data(),
240 static_cast<std::streamsize>(buffer.size()));
241
242 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
243 m_InstinctLogo.at(1) = LoadTexture(reinterpret_cast<const void*>(const_cast<const char*>(buffer.data())),
244 static_cast<int>(fd.size()));
245 }
246 else
247 {
248 m_InstinctLogo.at(1) = LoadTexture("resources/images/INSTINCT_Logo_Text_black_small.png");
249 }
250
251 if (fs.is_file("resources/images/INS_logo_rectangular_white_small.png"))
252 {
253 auto fd = fs.open("resources/images/INS_logo_rectangular_white_small.png");
254
255 LOG_DEBUG("Generating Texture for INS Logo (white) ({} byte)", fd.size());
256
257 auto is = cmrc::memstream(const_cast<char*>(fd.begin()), // NOLINT(cppcoreguidelines-pro-type-const-cast)
258 const_cast<char*>(fd.end())); // NOLINT(cppcoreguidelines-pro-type-const-cast)
259
260 std::vector<char> buffer;
261 buffer.resize(fd.size(), '\0');
262
263 is.read(buffer.data(),
264 static_cast<std::streamsize>(buffer.size()));
265
266 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
267 m_InsLogo.at(0) = LoadTexture(reinterpret_cast<const void*>(const_cast<const char*>(buffer.data())),
268 static_cast<int>(fd.size()));
269 }
270 else
271 {
272 m_InsLogo.at(0) = LoadTexture("resources/images/INS_logo_rectangular_white_small.png");
273 }
274
275 if (fs.is_file("resources/images/INS_logo_rectangular_black_small.png"))
276 {
277 auto fd = fs.open("resources/images/INS_logo_rectangular_black_small.png");
278
279 LOG_DEBUG("Generating Texture for INS Logo (black) ({} byte)", fd.size());
280
281 auto is = cmrc::memstream(const_cast<char*>(fd.begin()), // NOLINT(cppcoreguidelines-pro-type-const-cast)
282 const_cast<char*>(fd.end())); // NOLINT(cppcoreguidelines-pro-type-const-cast)
283
284 std::vector<char> buffer;
285 buffer.resize(fd.size(), '\0');
286
287 is.read(buffer.data(),
288 static_cast<std::streamsize>(buffer.size()));
289
290 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
291 m_InsLogo.at(1) = LoadTexture(reinterpret_cast<const void*>(const_cast<const char*>(buffer.data())),
292 static_cast<int>(fd.size()));
293 }
294 else
295 {
296 m_InsLogo.at(1) = LoadTexture("resources/images/INS_logo_rectangular_black_small.png");
297 }
298
299 if (fs.is_file("resources/images/ic_save_white_24dp.png"))
300 {
301 auto fd = fs.open("resources/images/ic_save_white_24dp.png");
302
303 LOG_DEBUG("Generating Texture for Save icon ({} byte)", fd.size());
304
305 auto is = cmrc::memstream(const_cast<char*>(fd.begin()), // NOLINT(cppcoreguidelines-pro-type-const-cast)
306 const_cast<char*>(fd.end())); // NOLINT(cppcoreguidelines-pro-type-const-cast)
307
308 std::vector<char> buffer;
309 buffer.resize(fd.size(), '\0');
310
311 is.read(buffer.data(),
312 static_cast<std::streamsize>(buffer.size()));
313
314 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
315 m_SaveButtonImage = LoadTexture(reinterpret_cast<const void*>(const_cast<const char*>(buffer.data())),
316 static_cast<int>(fd.size()));
317 }
318 else
319 {
320 m_SaveButtonImage = LoadTexture("resources/images/ic_save_white_24dp.png");
321 }
322
323 if (fs.is_file("resources/images/Rose-rhodonea-curve-7x9-chart-improved.jpg"))
324 {
325 auto fd = fs.open("resources/images/Rose-rhodonea-curve-7x9-chart-improved.jpg");
326
327 LOG_DEBUG("Generating Texture for Rose figure ({} byte)", fd.size());
328
329 auto is = cmrc::memstream(const_cast<char*>(fd.begin()), // NOLINT(cppcoreguidelines-pro-type-const-cast)
330 const_cast<char*>(fd.end())); // NOLINT(cppcoreguidelines-pro-type-const-cast)
331
332 std::vector<char> buffer;
333 buffer.resize(fd.size(), '\0');
334
335 is.read(buffer.data(),
336 static_cast<std::streamsize>(buffer.size()));
337
338 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
339 m_RoseFigure = LoadTexture(reinterpret_cast<const void*>(const_cast<const char*>(buffer.data())),
340 static_cast<int>(fd.size()));
341 }
342 else
343 {
344 m_RoseFigure = LoadTexture("resources/images/Rose-rhodonea-curve-7x9-chart-improved.jpg");
345 }
346 LOG_DATA("Finished NodeEditorApplication OnStart()");
347}
348
350{
351 LOG_TRACE("called");
352
354
356
358
359 auto releaseTexture = [this](ImTextureID& id) {
360 if (id)
361 {
362 DestroyTexture(id);
363 id = nullptr;
364 }
365 };
366
367 releaseTexture(m_InsLogo.at(0));
368 releaseTexture(m_InsLogo.at(1));
369 releaseTexture(m_InstinctLogo.at(0));
370 releaseTexture(m_InstinctLogo.at(1));
371 releaseTexture(m_HeaderBackground);
372 releaseTexture(m_SaveButtonImage);
373 releaseTexture(m_RoseFigure);
374
375 if (m_Editor)
376 {
377 ed::DestroyEditor(m_Editor);
378 m_Editor = nullptr;
379 }
380 if (ImPlotContext* ctx = ImPlot::GetCurrentContext())
381 {
382 ImPlot::DestroyContext(ctx);
383 }
384}
385
387{
388 LOG_TRACE("called");
389
391 {
393 return false;
394 }
395
396 return true;
397}
398
400{
401 const auto& io = ImGui::GetIO();
402 if (!io.KeyCtrl && !io.KeyAlt && !io.KeyShift && !io.KeySuper)
403 {
404 if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Escape)))
405 {
407 return;
408 }
409 }
410
411 ImGui::PushFont(WindowFont());
412 ImGui::OpenPopup("Quit with unsaved changes?");
413 if (ImGui::BeginPopupModal("Quit with unsaved changes?", nullptr, ImGuiWindowFlags_AlwaysAutoResize))
414 {
415 ImGui::Text("Do you want to save your changes or discard them?");
416 if (ImGui::Button("Save"))
417 {
418 if (flow::GetCurrentFilename().empty())
419 {
420 ImGuiFileDialog::Instance()->OpenModal("Save Flow", "Save Flow", ".flow", (flow::GetFlowPath() / ".").string(), 1, nullptr, ImGuiFileDialogFlags_ConfirmOverwrite);
421 ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtention, ".flow", ImVec4(0.0F, 1.0F, 0.0F, 0.9F));
422 }
423 else
424 {
428 Quit();
429 ImGui::CloseCurrentPopup();
430 }
431 }
432
433 if (ImGuiFileDialog::Instance()->Display("Save Flow", ImGuiWindowFlags_NoCollapse, ImVec2(600, 400)))
434 {
435 if (ImGuiFileDialog::Instance()->IsOk())
436 {
437 flow::SetCurrentFilename(ImGuiFileDialog::Instance()->GetFilePathName());
439 }
440
442 ImGuiFileDialog::Instance()->Close();
443 Quit();
444 ImGui::CloseCurrentPopup();
445 }
446 ImGui::SameLine();
447 if (ImGui::Button("Discard"))
448 {
451 Quit();
452 ImGui::CloseCurrentPopup();
453 }
454 ImGui::SameLine();
455 if (ImGui::Button("Cancel"))
456 {
458 ImGui::CloseCurrentPopup();
459 }
460 ImGui::EndPopup();
461 }
462 ImGui::PopFont();
463}
464
466{
467 ImGui::PushFont(WindowFont());
468 ImGuiFileDialog::Instance()->OpenDialog("Save Flow", "Save Flow", ".flow", (flow::GetFlowPath() / ".").string(), 1, nullptr, ImGuiFileDialogFlags_ConfirmOverwrite);
469 ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtention, ".flow", ImVec4(0.0F, 1.0F, 0.0F, 0.9F));
470
471 const auto& io = ImGui::GetIO();
472 if (!io.KeyCtrl && !io.KeyAlt && !io.KeyShift && !io.KeySuper)
473 {
474 if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Escape)))
475 {
477 ImGuiFileDialog::Instance()->Close();
478 ImGui::PopFont();
479 return;
480 }
481 }
482
483 if (ImGuiFileDialog::Instance()->Display("Save Flow", ImGuiWindowFlags_NoCollapse, ImVec2(600, 400)))
484 {
485 if (ImGuiFileDialog::Instance()->IsOk())
486 {
487 flow::SetCurrentFilename(ImGuiFileDialog::Instance()->GetFilePathName());
489 }
490
492 ImGuiFileDialog::Instance()->Close();
493 }
494 ImGui::PopFont();
495}
496
498{
499 const auto& io = ImGui::GetIO();
500 if (!io.KeyCtrl && !io.KeyAlt && !io.KeyShift && !io.KeySuper)
501 {
502 if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Escape)))
503 {
505 return;
506 }
507 }
508
509 ImGui::PushFont(WindowFont());
510 ImGui::OpenPopup("Clear Nodes and discard unsaved changes?");
511 if (ImGui::BeginPopupModal("Clear Nodes and discard unsaved changes?", nullptr, ImGuiWindowFlags_AlwaysAutoResize))
512 {
513 ImGui::Text("Do you want to save your changes before clearing the nodes?");
514 if (ImGui::Button("Save"))
515 {
516 if (flow::GetCurrentFilename().empty())
517 {
518 ImGuiFileDialog::Instance()->OpenModal("Save Flow", "Save Flow", ".flow", (flow::GetFlowPath() / ".").string(), 1, nullptr, ImGuiFileDialogFlags_ConfirmOverwrite);
519 ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtention, ".flow", ImVec4(0.0F, 1.0F, 0.0F, 0.9F));
520 }
521 else
522 {
528 ImGui::CloseCurrentPopup();
529 }
530 }
531
532 if (ImGuiFileDialog::Instance()->Display("Save Flow", ImGuiWindowFlags_NoCollapse, ImVec2(600, 400)))
533 {
534 if (ImGuiFileDialog::Instance()->IsOk())
535 {
536 flow::SetCurrentFilename(ImGuiFileDialog::Instance()->GetFilePathName());
538
539 ImGuiFileDialog::Instance()->Close();
543 }
544
546
547 ImGui::CloseCurrentPopup();
548 }
549 ImGui::SameLine();
550 if (ImGui::Button("Discard"))
551 {
556 ImGui::CloseCurrentPopup();
557 }
558 ImGui::SameLine();
559 if (ImGui::Button("Cancel"))
560 {
562 ImGui::CloseCurrentPopup();
563 }
564 ImGui::EndPopup();
565 }
566 ImGui::PopFont();
567}
568
570{
571 ImGui::PushFont(WindowFont());
573 {
574 ImGui::OpenPopup("Discard unsaved changes?");
575 if (ImGui::BeginPopupModal("Discard unsaved changes?", nullptr, ImGuiWindowFlags_AlwaysAutoResize))
576 {
577 ImGui::Text("Do you want to save your changes or discard them before loading?");
578 if (ImGui::Button("Save"))
579 {
580 if (flow::GetCurrentFilename().empty())
581 {
582 ImGuiFileDialog::Instance()->OpenModal("Save Flow##Load", "Save Flow", ".flow", (flow::GetFlowPath() / ".").string(), 1, nullptr, ImGuiFileDialogFlags_ConfirmOverwrite);
583 ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtention, ".flow", ImVec4(0.0F, 1.0F, 0.0F, 0.9F));
584 }
585 else
586 {
589 ImGui::CloseCurrentPopup();
590 }
591 }
592
593 if (ImGuiFileDialog::Instance()->Display("Save Flow##Load", ImGuiWindowFlags_NoCollapse, ImVec2(600, 400)))
594 {
595 if (ImGuiFileDialog::Instance()->IsOk())
596 {
597 flow::SetCurrentFilename(ImGuiFileDialog::Instance()->GetFilePathName());
599 }
600
602 ImGuiFileDialog::Instance()->Close();
603 ImGui::CloseCurrentPopup();
604 }
605 ImGui::SameLine();
606 if (ImGui::Button("Discard"))
607 {
609 ImGui::CloseCurrentPopup();
610 }
611 ImGui::SameLine();
612 if (ImGui::Button("Cancel"))
613 {
615 ImGui::CloseCurrentPopup();
616 }
617 ImGui::EndPopup();
618 }
619 }
620 else
621 {
622 ImGuiFileDialog::Instance()->OpenDialog("Load Flow", "Load Flow", ".flow", (flow::GetFlowPath() / ".").string(), 1, nullptr);
623 ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtention, ".flow", ImVec4(0.0F, 1.0F, 0.0F, 0.9F));
624
625 static bool loadSuccessful = true;
626
627 auto& io = ImGui::GetIO();
628 if (!io.KeyCtrl && !io.KeyAlt && !io.KeyShift && !io.KeySuper)
629 {
630 if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Escape)))
631 {
633 loadSuccessful = true;
634 ImGuiFileDialog::Instance()->Close();
635 ImGui::PopFont();
636 return;
637 }
638 }
639
640 if (ImGuiFileDialog::Instance()->Display("Load Flow", ImGuiWindowFlags_NoCollapse, ImVec2(600, 400)))
641 {
642 if (ImGuiFileDialog::Instance()->IsOk())
643 {
644 loadSuccessful = flow::LoadFlow(ImGuiFileDialog::Instance()->GetFilePathName());
645 if (loadSuccessful)
646 {
648 frameCountNavigate = ImGui::GetFrameCount();
649 ImGuiFileDialog::Instance()->Close();
650 }
651 }
652 else
653 {
655 ImGuiFileDialog::Instance()->Close();
656 }
657 }
658 if (!loadSuccessful)
659 {
660 ImGui::OpenPopup("Could not open file");
661 }
662
663 if (ImGui::BeginPopupModal("Could not open file", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize))
664 {
665 ImGui::Text("Could not open the flow file. This can have the following reasons:");
666 ImGui::Text("- the filename specified is invalid");
667 ImGui::Text("- the program has insufficient permissions to access the file");
668 ImGui::Text("- the provided file was not a valid flow file");
669 ImGui::Text("Read the log output for further information");
670 if (ImGui::Button("Close"))
671 {
672 loadSuccessful = true;
673 ImGui::CloseCurrentPopup();
674 }
675 ImGui::EndPopup();
676 }
677 }
678 ImGui::PopFont();
679}
680
682{
683 ImGui::PushFont(WindowFont());
684 const char* title = renameNode->kind == Node::Kind::GroupBox ? "Rename Group Box" : "Rename Node";
685 ImGui::OpenPopup(title);
686 if (ImGui::BeginPopupModal(title, nullptr, ImGuiWindowFlags_AlwaysAutoResize))
687 {
688 static std::string nameBackup = renameNode->name;
689 if (nameBackup.empty())
690 {
691 nameBackup = renameNode->name;
692 }
693
694 auto& io = ImGui::GetIO();
695 if (!io.KeyCtrl && !io.KeyAlt && !io.KeyShift && !io.KeySuper)
696 {
697 if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Escape)))
698 {
699 if (renameNode)
700 {
701 renameNode->name = nameBackup;
702 }
703 nameBackup.clear();
704 renameNode = nullptr;
705 ImGui::CloseCurrentPopup();
706 ImGui::EndPopup();
707 ImGui::PopFont();
708 return;
709 }
710 }
711
712 if (ImGui::InputTextMultiline(fmt::format("##{}", size_t(renameNode->id)).c_str(), &renameNode->name, ImVec2(0, 65), ImGuiInputTextFlags_CtrlEnterForNewLine | ImGuiInputTextFlags_EnterReturnsTrue))
713 {
714 nameBackup.clear();
715 renameNode = nullptr;
716 ImGui::CloseCurrentPopup();
717 }
718 ImGui::SameLine();
719 gui::widgets::HelpMarker("Hold SHIFT or use mouse to select text.\n"
720 "CTRL+Left/Right to word jump.\n"
721 "CTRL+A or double-click to select all.\n"
722 "CTRL+X,CTRL+C,CTRL+V clipboard.\n"
723 "CTRL+Z,CTRL+Y undo/redo.\n"
724 "ESCAPE to revert.");
725 if (ImGui::Button("Accept"))
726 {
727 nameBackup.clear();
728 renameNode = nullptr;
730 ImGui::CloseCurrentPopup();
731 }
732 ImGui::SameLine();
733 if (ImGui::Button("Cancel"))
734 {
735 if (renameNode)
736 {
737 renameNode->name = nameBackup;
738 }
739 nameBackup.clear();
740 renameNode = nullptr;
741 ImGui::CloseCurrentPopup();
742 }
743 ImGui::EndPopup();
744 }
745 ImGui::PopFont();
746}
747
749{
750 ImGui::PushFont(WindowFont());
751 const char* title = "Rename Pin";
752 ImGui::OpenPopup(title);
753 if (ImGui::BeginPopupModal(title, nullptr, ImGuiWindowFlags_AlwaysAutoResize))
754 {
755 static std::string nameBackup = renamePin->name;
756 if (nameBackup.empty())
757 {
758 nameBackup = renamePin->name;
759 }
760
761 auto& io = ImGui::GetIO();
762 if (!io.KeyCtrl && !io.KeyAlt && !io.KeyShift && !io.KeySuper)
763 {
764 if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Escape)))
765 {
766 if (renamePin)
767 {
768 renamePin->name = nameBackup;
769 }
770 nameBackup.clear();
771 renamePin = nullptr;
772 ImGui::CloseCurrentPopup();
773 ImGui::EndPopup();
774 ImGui::PopFont();
775 return;
776 }
777 }
778
779 if (ImGui::InputTextMultiline(fmt::format("##{}", size_t(renamePin->id)).c_str(), &renamePin->name, ImVec2(0, 65), ImGuiInputTextFlags_CtrlEnterForNewLine | ImGuiInputTextFlags_EnterReturnsTrue))
780 {
781 nameBackup.clear();
782 renamePin = nullptr;
783 ImGui::CloseCurrentPopup();
784 }
785 ImGui::SameLine();
786 gui::widgets::HelpMarker("Hold SHIFT or use mouse to select text.\n"
787 "CTRL+Left/Right to word jump.\n"
788 "CTRL+A or double-click to select all.\n"
789 "CTRL+X,CTRL+C,CTRL+V clipboard.\n"
790 "CTRL+Z,CTRL+Y undo/redo.\n"
791 "ESCAPE to revert.");
792 if (ImGui::Button("Accept"))
793 {
794 nameBackup.clear();
795 renamePin = nullptr;
797 ImGui::CloseCurrentPopup();
798 }
799 ImGui::SameLine();
800 if (ImGui::Button("Cancel"))
801 {
802 if (renamePin)
803 {
804 renamePin->name = nameBackup;
805 }
806 nameBackup.clear();
807 renamePin = nullptr;
808 ImGui::CloseCurrentPopup();
809 }
810 ImGui::EndPopup();
811 }
812 ImGui::PopFont();
813}
814
816{
817 bool firstFrame = ImGui::GetFrameCount() == 1;
818
819 if (frameCountNavigate && ImGui::GetFrameCount() - frameCountNavigate > 3)
820 {
822 ed::NavigateToContent();
823 }
824
825 switch (globalAction)
826 {
828 Quit();
829 break;
832 break;
835 break;
838 break;
841 break;
842 default:
843 break;
844 }
845
846 gui::UpdateTouch(deltaTime);
847
848 if (ed::AreShortcutsEnabled())
849 {
851 }
852
853 ImGui::PushFont(PanelFont());
855 menuBarHeight = ImGui::GetCursorPosY();
856 ImGui::PopFont();
857
858 ed::SetCurrentEditor(m_Editor);
859
860 static ed::NodeId contextNodeId = 0;
861 static ed::LinkId contextLinkId = 0;
862 static ed::PinId contextPinId = 0;
863 static bool createNewNode = false;
864 static Pin* newNodeLinkPin = nullptr;
865
866 bool leftPaneActive = false;
867 if (!hideLeftPane)
868 {
869 gui::widgets::Splitter("Main Splitter", true, SPLITTER_THICKNESS, &leftPaneWidth, &rightPaneWidth, 25.0F, 50.0F);
870
871 ImGui::PushFont(PanelFont());
873 ImGui::PopFont();
874
875 ImGui::SameLine(0.0F, 12.0F);
876 }
877
878 // ToolTips have to be shown outside of the NodeEditor Context, so save the Tooltip and push it afterwards
879 std::string tooltipText;
880
881 ImGui::BeginGroup();
882
883 bool darkMode = ax::NodeEditor::GetStyle().Colors[ax::NodeEditor::StyleColor_Bg].x
884 + ax::NodeEditor::GetStyle().Colors[ax::NodeEditor::StyleColor_Bg].y
885 + ax::NodeEditor::GetStyle().Colors[ax::NodeEditor::StyleColor_Bg].z
886 > 2.0F;
887
889 {
890 float blueprintHeight = ImGui::GetContentRegionAvail().y - bottomViewHeight + (isUsingBigPanelFont() ? 48.5F : 28.5F);
891 ImGui::PushStyleColor(ImGuiCol_Separator, IM_COL32_BLACK_TRANS);
892 gui::widgets::Splitter("Log Splitter", false, 6.0F, &blueprintHeight, &bottomViewHeight, 400.0F, BOTTOM_VIEW_UNCOLLAPSED_MIN_HEIGHT);
893 ImGui::PopStyleColor();
894 }
895
896 ed::Begin("Node editor", ImVec2(0, ImGui::GetContentRegionAvail().y - bottomViewHeight + SPLITTER_THICKNESS));
897 {
898 static Pin* newLinkPin = nullptr;
899
900 auto cursorTopLeft = ImGui::GetCursorScreenPos();
901
902 static util::BlueprintNodeBuilder builder(m_HeaderBackground, GetTextureWidth(m_HeaderBackground), GetTextureHeight(m_HeaderBackground));
903
904 auto textColor = ax::NodeEditor::GetStyle().Colors[ax::NodeEditor::StyleColor_NodeBg].x
905 + ax::NodeEditor::GetStyle().Colors[ax::NodeEditor::StyleColor_NodeBg].y
906 + ax::NodeEditor::GetStyle().Colors[ax::NodeEditor::StyleColor_NodeBg].z
907 > 2.0F
908 ? IM_COL32(0, 0, 0, 255)
909 : IM_COL32(255, 255, 255, 255);
910
911 for (auto* node : nm::m_Nodes()) // Blueprint || Simple
912 {
913 if (node->kind != Node::Kind::Blueprint && node->kind != Node::Kind::Simple) // NOLINT(misc-redundant-expression) - false positive warning
914 {
915 continue;
916 }
917
918 const auto isSimple = node->kind == Node::Kind::Simple;
919
920 bool hasOutputDelegates = false;
921 for (const auto& output : node->outputPins)
922 {
923 if (output.type == Pin::Type::Delegate)
924 {
925 hasOutputDelegates = true;
926 }
927 }
928
929 builder.Begin(node->id);
930
931 if (!isSimple) // Header Text for Blueprint Nodes
932 {
933 if (node->isDisabled()) // Node disabled
934 {
935 builder.Header(ImColor(192, 192, 192)); // Silver
936 }
937 else if (node->isInitialized())
938 {
939 builder.Header(ImColor(128, 255, 128)); // Light green
940 }
941 else
942 {
943 builder.Header(ImColor(255, 128, 128)); // Light red
944 }
945 ImGui::Spring(0);
946 ImGui::PushStyleColor(ImGuiCol_Text, textColor);
947 ImGui::TextUnformatted(node->name.c_str());
948 ImGui::PopStyleColor();
949 ImGui::Spring(1);
950 if (node->getState() == Node::State::DoInitialize)
951 {
952 gui::widgets::Spinner(("##Spinner " + node->nameId()).c_str(), ImColor(144, 202, 238), 10.0F, 1.0F);
953 }
954 else if (node->getState() == Node::State::Initializing)
955 {
956 gui::widgets::Spinner(("##Spinner " + node->nameId()).c_str(), ImColor(144, 238, 144), 10.0F, 1.0F);
957 }
958 else if (node->getState() == Node::State::DoDeinitialize)
959 {
960 gui::widgets::Spinner(("##Spinner " + node->nameId()).c_str(), ImColor(255, 222, 122), 10.0F, 1.0F);
961 }
962 else if (node->getState() == Node::State::Deinitializing)
963 {
964 gui::widgets::Spinner(("##Spinner " + node->nameId()).c_str(), ImColor(255, 160, 122), 10.0F, 1.0F);
965 }
966 else
967 {
968 ImGui::Dummy(ImVec2(ImGui::GetStyle().ItemSpacing.x + 12.0F + ImGui::GetStyle().FramePadding.x, 26));
969 }
970 if (hasOutputDelegates)
971 {
972 ImGui::BeginVertical("delegates", ImVec2(0, 26));
973 ImGui::Spring(1, 0);
974 for (const auto& output : node->outputPins)
975 {
976 if (output.type != Pin::Type::Delegate)
977 {
978 continue;
979 }
980
981 auto alpha = ImGui::GetStyle().Alpha;
982 if (newLinkPin && newLinkPin->kind == Pin::Kind::Input && &output != newLinkPin
983 && !reinterpret_cast<InputPin*>(newLinkPin)->canCreateLink(output))
984 {
985 alpha = alpha * (48.0F / 255.0F);
986 }
987
988 ed::BeginPin(output.id, ed::PinKind::Output);
989 ed::PinPivotAlignment(ImVec2(1.0F, 0.5F));
990 ed::PinPivotSize(ImVec2(0, 0));
991 ImGui::BeginHorizontal(output.id.AsPointer());
992 ImGui::PushStyleVar(ImGuiStyleVar_Alpha, alpha);
993 // if (!output.name.empty())
994 // {
995 // ImGui::PushStyleColor(ImGuiCol_Text, textColor);
996 // ImGui::TextUnformatted(output.name.c_str());
997 // ImGui::PopStyleColor();
998 // ImGui::Spring(0);
999 // }
1000 output.drawPinIcon(output.isPinLinked(), static_cast<int>(alpha * 255));
1001 ImGui::Spring(0, ImGui::GetStyle().ItemSpacing.x / 2);
1002 ImGui::EndHorizontal();
1003 ImGui::PopStyleVar();
1004 ed::EndPin();
1005
1006 // DrawItemRect(ImColor(255, 0, 0));
1007 }
1008 ImGui::Spring(1, 0);
1009 ImGui::EndVertical();
1010 ImGui::Spring(0, ImGui::GetStyle().ItemSpacing.x / 2);
1011 }
1012 else
1013 {
1014 ImGui::Spring(0);
1015 }
1016 builder.EndHeader();
1017 }
1018
1019 for (auto& input : node->inputPins) // Input Pins
1020 {
1021 auto alpha = ImGui::GetStyle().Alpha;
1022 if (newLinkPin && newLinkPin->kind == Pin::Kind::Output && &input != newLinkPin
1023 && !reinterpret_cast<OutputPin*>(newLinkPin)->canCreateLink(input))
1024 {
1025 alpha = alpha * (48.0F / 255.0F);
1026 }
1027
1028 builder.Input(input.id);
1029 ImGui::PushStyleVar(ImGuiStyleVar_Alpha, alpha);
1030 input.drawPinIcon(input.isPinLinked(), static_cast<int>(alpha * 255));
1031 if (ImGui::IsItemHovered()) { tooltipText = fmt::format("{}", fmt::join(input.dataIdentifier, "\n")); }
1032 if (_showQueueSizeOnPins && input.type == Pin::Type::Flow && input.isPinLinked())
1033 {
1034 auto cursor = ImGui::GetCursorPos();
1035 std::string text = fmt::format("{}{}", input.queue.size(), input.queueBlocked ? "*" : "");
1036 auto* drawList = ImGui::GetWindowDrawList();
1037 drawList->AddText(ImVec2(cursor.x - 26.0F, cursor.y + 2.F), IM_COL32(255, 0, 0, 255), text.c_str());
1038 }
1039
1040 ImGui::Spring(0);
1041 if (!input.name.empty())
1042 {
1043 bool noneType = input.type == Pin::Type::None;
1044 if (noneType)
1045 {
1046 ImGui::BeginDisabled();
1047 }
1048 ImGui::PushStyleColor(ImGuiCol_Text, textColor);
1049 ImGui::TextUnformatted(input.name.c_str());
1050 ImGui::PopStyleColor();
1051 if (noneType)
1052 {
1053 ImGui::EndDisabled();
1054 }
1055 ImGui::Spring(0);
1056 }
1057 ImGui::PopStyleVar();
1059 }
1060
1061 if (isSimple) // Middle Text for Simple Nodes
1062 {
1063 builder.Middle();
1064
1065 ImGui::Spring(1, 0);
1066 ImGui::PushStyleColor(ImGuiCol_Text, textColor);
1067 ImGui::TextUnformatted(node->name.c_str());
1068 ImGui::PopStyleColor();
1069 ImGui::Spring(1, 0);
1070 }
1071
1072 for (const auto& output : node->outputPins) // Output Pins
1073 {
1074 if (!isSimple && output.type == Pin::Type::Delegate)
1075 {
1076 continue;
1077 }
1078
1079 auto alpha = ImGui::GetStyle().Alpha;
1080 if (newLinkPin && newLinkPin->kind == Pin::Kind::Input && &output != newLinkPin
1081 && !reinterpret_cast<InputPin*>(newLinkPin)->canCreateLink(output))
1082 {
1083 alpha = alpha * (48.0F / 255.0F);
1084 }
1085
1086 ImGui::PushStyleVar(ImGuiStyleVar_Alpha, alpha);
1087 builder.Output(output.id);
1088 if (!output.name.empty())
1089 {
1090 ImGui::Spring(0);
1091 bool noneType = output.type == Pin::Type::None;
1092 if (noneType)
1093 {
1094 ImGui::BeginDisabled();
1095 }
1096 ImGui::PushStyleColor(ImGuiCol_Text, textColor);
1097 ImGui::TextUnformatted(output.name.c_str());
1098 ImGui::PopStyleColor();
1099 if (noneType)
1100 {
1101 ImGui::EndDisabled();
1102 }
1103 }
1104 ImGui::Spring(0);
1105 output.drawPinIcon(output.isPinLinked(), static_cast<int>(alpha * 255));
1106 if (ImGui::IsItemHovered()) { tooltipText = fmt::format("{}", fmt::join(output.dataIdentifier, "\n")); }
1107 if (_showQueueSizeOnPins && output.type == Pin::Type::Flow && output.isPinLinked())
1108 {
1109 auto cursor = ImGui::GetCursorPos();
1110 std::string text = fmt::format("{}", output.noMoreDataAvailable ? "F" : "P");
1111 auto* drawList = ImGui::GetWindowDrawList();
1112 drawList->AddText(ImVec2(cursor.x - 26.0F, cursor.y + 2.F), IM_COL32(255, 0, 0, 255), text.c_str());
1113 }
1114
1115 ImGui::PopStyleVar();
1117 }
1118
1119 builder.End();
1120 }
1121
1122 for (const auto* node : nm::m_Nodes()) // GroupBox
1123 {
1124 if (node->kind != Node::Kind::GroupBox)
1125 {
1126 continue;
1127 }
1128
1129 ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.75F);
1130 ed::PushStyleColor(ed::StyleColor_NodeBg, m_colors[COLOR_GROUP_HEADER_BG]);
1131 ed::PushStyleColor(ed::StyleColor_NodeBorder, m_colors[COLOR_GROUP_OUTER_BORDER]);
1132 ed::BeginNode(node->id);
1133 ImGui::PushID(node->id.AsPointer());
1134 ImGui::BeginVertical("content");
1135 ImGui::BeginHorizontal("horizontal");
1136 ImGui::Spring(1);
1137 ImGui::PushStyleColor(ImGuiCol_Text, m_colors[COLOR_GROUP_HEADER_TEXT]);
1138 ImGui::TextUnformatted(node->name.c_str());
1139 ImGui::PopStyleColor();
1140 ImGui::Spring(1);
1141 ImGui::EndHorizontal();
1142 ed::Group(node->_size);
1143 ImGui::EndVertical();
1144 ImGui::PopID();
1145 ed::EndNode();
1146 ed::PopStyleColor(2);
1147 ImGui::PopStyleVar();
1148 }
1149
1150 for (const auto* node : nm::m_Nodes())
1151 {
1152 for (const auto& output : node->outputPins) // Output Pins
1153 {
1154 auto color = output.getIconColor();
1155 if (output.type == Pin::Type::Flow)
1156 {
1157 color = darkMode ? ImColor{ 0, 0, 0 } : ImColor{ 255, 255, 255 };
1158 }
1159
1160 for (const auto& link : output.links)
1161 {
1162 ed::Link(link.linkId, output.id, link.connectedPinId, color, 2.0F * defaultFontRatio());
1163 }
1164 }
1165 }
1166
1167 if (!createNewNode)
1168 {
1169 if (ed::BeginCreate(ImColor(255, 255, 255), 2.0F))
1170 {
1171 auto showLabel = [](const char* label, ImColor color) {
1172 ImGui::SetCursorPosY(ImGui::GetCursorPosY() - ImGui::GetTextLineHeight());
1173 auto size = ImGui::CalcTextSize(label);
1174
1175 auto padding = ImGui::GetStyle().FramePadding;
1176 auto spacing = ImGui::GetStyle().ItemSpacing;
1177
1178 ImGui::SetCursorPos(ImGui::GetCursorPos() + ImVec2(spacing.x, -spacing.y));
1179
1180 auto rectMin = ImGui::GetCursorScreenPos() - padding;
1181 auto rectMax = ImGui::GetCursorScreenPos() + size + padding;
1182
1183 auto* drawList = ImGui::GetWindowDrawList();
1184 drawList->AddRectFilled(rectMin, rectMax, color, size.y * 0.15F);
1185 ImGui::TextUnformatted(label);
1186 };
1187
1188 ed::PinId startPinId = 0;
1189 ed::PinId endPinId = 0;
1190 if (ed::QueryNewLink(&startPinId, &endPinId))
1191 {
1192 Pin* startPin = nm::FindInputPin(startPinId);
1193 Pin* endPin = nm::FindInputPin(endPinId);
1194 if (!startPin) { startPin = nm::FindOutputPin(startPinId); }
1195 if (!endPin) { endPin = nm::FindOutputPin(endPinId); }
1196
1197 newLinkPin = startPin ? startPin : endPin;
1198
1199 if (startPin && startPin->kind == Pin::Kind::Input)
1200 {
1201 std::swap(startPin, endPin);
1202 std::swap(startPinId, endPinId);
1203 }
1204
1205 if (startPin && endPin)
1206 {
1207 if (endPin == startPin)
1208 {
1209 ed::RejectNewItem(ImColor(255, 0, 0), 2.0F);
1210 }
1211 else if (endPin->kind == startPin->kind)
1212 {
1213 showLabel("x Incompatible Pin Kind", ImColor(45, 32, 32, 180));
1214 ed::RejectNewItem(ImColor(255, 0, 0), 2.0F);
1215 }
1216 else if (endPin->parentNode == startPin->parentNode)
1217 {
1218 showLabel("x Cannot connect to self", ImColor(45, 32, 32, 180));
1219 ed::RejectNewItem(ImColor(255, 0, 0), 1.0F);
1220 }
1221 else if (endPin->type != startPin->type)
1222 {
1223 showLabel("x Incompatible Pin Type", ImColor(45, 32, 32, 180));
1224 ed::RejectNewItem(ImColor(255, 128, 128), 1.0F);
1225 }
1226 else if (reinterpret_cast<InputPin*>(endPin)->isPinLinked())
1227 {
1228 showLabel("End Pin already linked", ImColor(45, 32, 32, 180));
1229 ed::RejectNewItem(ImColor(255, 128, 128), 1.0F);
1230 }
1231 else if (startPin->type == Pin::Type::Flow
1233 {
1234 showLabel(fmt::format("The data type [{}]\ncan't be linked to [{}]",
1235 fmt::join(startPin->dataIdentifier, ","),
1236 fmt::join(endPin->dataIdentifier, ","))
1237 .c_str(),
1238 ImColor(45, 32, 32, 180));
1239 ed::RejectNewItem(ImColor(255, 128, 128), 1.0F);
1240 }
1241 else if (startPin->type == Pin::Type::Delegate
1242 && (startPin->parentNode == nullptr
1243 || std::ranges::find(endPin->dataIdentifier, startPin->parentNode->type()) == endPin->dataIdentifier.end()))
1244 {
1245 if (startPin->parentNode != nullptr)
1246 {
1247 showLabel(fmt::format("The delegate type [{}]\ncan't be linked to [{}]",
1248 startPin->parentNode->type(),
1249 fmt::join(endPin->dataIdentifier, ","))
1250 .c_str(),
1251 ImColor(45, 32, 32, 180));
1252 }
1253
1254 ed::RejectNewItem(ImColor(255, 128, 128), 1.0F);
1255 }
1256 else if ((startPin->type == Pin::Type::Object || startPin->type == Pin::Type::Matrix) // NOLINT(misc-redundant-expression) - false positive warning
1258 {
1259 showLabel(fmt::format("The data type [{}]\ncan't be linked to [{}]",
1260 fmt::join(startPin->dataIdentifier, ","),
1261 fmt::join(endPin->dataIdentifier, ","))
1262 .c_str(),
1263 ImColor(45, 32, 32, 180));
1264 ed::RejectNewItem(ImColor(255, 128, 128), 1.0F);
1265 }
1266 else
1267 {
1268 showLabel("+ Create Link", ImColor(32, 45, 32, 180));
1269 if (ed::AcceptNewItem(ImColor(128, 255, 128), 4.0F))
1270 {
1271 reinterpret_cast<OutputPin*>(startPin)->createLink(*reinterpret_cast<InputPin*>(endPin));
1272 }
1273 }
1274 }
1275 }
1276
1277 ed::PinId pinId = 0;
1278 if (ed::QueryNewNode(&pinId))
1279 {
1280 newLinkPin = nm::FindInputPin(pinId);
1281 if (!newLinkPin) { newLinkPin = nm::FindOutputPin(pinId); }
1282
1283 if (newLinkPin && newLinkPin->kind == Pin::Kind::Input && reinterpret_cast<InputPin*>(newLinkPin)->isPinLinked())
1284 {
1285 showLabel("End Pin already linked", ImColor(45, 32, 32, 180));
1286 ed::RejectNewItem(ImColor(255, 128, 128), 1.0F);
1287 }
1288 else
1289 {
1290 if (newLinkPin)
1291 {
1292 showLabel("+ Create Node", ImColor(32, 45, 32, 180));
1293 }
1294
1295 if (ed::AcceptNewItem())
1296 {
1297 createNewNode = true;
1298 newNodeLinkPin = nm::FindInputPin(pinId);
1299 if (!newNodeLinkPin) { newNodeLinkPin = nm::FindOutputPin(pinId); }
1300 newLinkPin = nullptr;
1301 ed::Suspend();
1302 ImGui::OpenPopup("Create New Node");
1303 ed::Resume();
1304 }
1305 }
1306 }
1307 }
1308 else
1309 {
1310 newLinkPin = nullptr;
1311 }
1312
1313 ed::EndCreate();
1314
1315 if (ed::BeginDelete())
1316 {
1317 ed::LinkId linkId = 0;
1318 while (ed::QueryDeletedLink(&linkId))
1319 {
1320 if (ed::AcceptDeletedItem())
1321 {
1322 bool deleted = false;
1323 for (auto* node : nm::m_Nodes())
1324 {
1325 if (deleted) { break; }
1326 for (auto& output : node->outputPins)
1327 {
1328 if (deleted) { break; }
1329 for (const auto& link : output.links)
1330 {
1331 if (link.linkId == linkId)
1332 {
1333 output.deleteLink(*link.getConnectedPin());
1334 deleted = true;
1335 break;
1336 }
1337 }
1338 }
1339 }
1340 }
1341 }
1342
1343 ed::NodeId nodeId = 0;
1344 while (ed::QueryDeletedNode(&nodeId))
1345 {
1346 if (Node* node = nm::FindNode(nodeId))
1347 {
1348 if (node->isTransient())
1349 {
1350 break;
1351 }
1352 }
1353 if (ed::AcceptDeletedItem())
1354 {
1355 nm::DeleteNode(nodeId);
1356 }
1357 }
1358 }
1359 ed::EndDelete();
1360 }
1361
1362 ImGui::SetCursorScreenPos(cursorTopLeft);
1363 }
1364
1365 // Shortcut enable/disable
1366 ax::NodeEditor::EnableShortcuts(ed::IsActive() || leftPaneActive);
1367
1368 // auto openPopupPosition = ImGui::GetMousePos();
1369 ed::Suspend();
1370 if (ed::ShowNodeContextMenu(&contextNodeId))
1371 {
1372 ImGui::OpenPopup("Node Context Menu");
1373 }
1374 else if (ed::ShowPinContextMenu(&contextPinId))
1375 {
1376 ImGui::OpenPopup("Pin Context Menu");
1377 }
1378 else if (ed::ShowLinkContextMenu(&contextLinkId) && ed::IsActive())
1379 {
1380 ImGui::OpenPopup("Link Context Menu");
1381 }
1382 else if (ed::ShowBackgroundContextMenu() && ed::IsActive())
1383 {
1384 ImGui::OpenPopup("Create New Node");
1385 newNodeLinkPin = nullptr;
1386 }
1387 else if (ed::NodeId doubleClickedNodeId = ed::GetDoubleClickedNode())
1388 {
1389 Node* node = nm::FindNode(doubleClickedNodeId);
1390 node->_showConfig = true;
1391 node->_configWindowFocus = true;
1392 }
1393 ed::Resume();
1394
1395 ed::Suspend();
1396 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8, 8));
1397 static Node* renameNode = nullptr;
1398 static Pin* renamePin = nullptr;
1399 if (ImGui::BeginPopup("Node Context Menu"))
1400 {
1401 auto* node = nm::FindNode(contextNodeId);
1402
1403 ImGui::TextUnformatted("Node Context Menu");
1404 ImGui::Separator();
1405 if (node)
1406 {
1407 ImGui::Text("ID: %lu", size_t(node->id));
1408 ImGui::Text("Type: %s", node->type().c_str());
1409 ImGui::Text("Kind: %s", std::string(node->kind).c_str());
1410 ImGui::Text("Inputs: %lu", node->inputPins.size());
1411 ImGui::Text("Outputs: %lu", node->outputPins.size());
1412 ImGui::Text("State: %s", Node::toString(node->getState()).c_str());
1413 ImGui::Text("Mode: %s", node->getMode() == Node::Mode::POST_PROCESSING ? "Post-processing" : "Real-time");
1414 ImGui::Separator();
1415 if (node->kind != Node::Kind::GroupBox)
1416 {
1417 if (ImGui::MenuItem(node->isInitialized() ? "Reinitialize" : "Initialize", "",
1418 false, node->isInitialized() || node->getState() == Node::State::Deinitialized))
1419 {
1420 if (node->isInitialized()) { node->doReinitialize(); }
1421 else { node->doInitialize(); }
1422 }
1423 if (ImGui::MenuItem("Deinitialize", "", false, node->isInitialized()))
1424 {
1425 node->doDeinitialize();
1426 }
1427 if (ImGui::MenuItem("Wake Worker"))
1428 {
1429 node->wakeWorker();
1430 }
1431 ImGui::Separator();
1432 if (node->_hasConfig && ImGui::MenuItem("Configure", "", false))
1433 {
1434 node->_showConfig = true;
1435 node->_configWindowFocus = true;
1436 }
1437 if (ImGui::MenuItem(node->isDisabled() ? "Enable" : "Disable", "", false, !node->isTransient()))
1438 {
1439 if (node->isDisabled())
1440 {
1441 node->doEnable();
1442 }
1443 else
1444 {
1445 node->doDisable();
1446 }
1448 }
1449 }
1450 if (ImGui::MenuItem("Rename"))
1451 {
1452 renameNode = node;
1453 }
1454 ImGui::Separator();
1455 if (ImGui::MenuItem("Delete", "", false, !node->isTransient()))
1456 {
1457 ed::DeleteNode(contextNodeId);
1458 }
1459 }
1460 else
1461 {
1462 ImGui::Text("Unknown node: %lu", size_t(contextNodeId));
1463 }
1464
1465 ImGui::EndPopup();
1466 }
1467
1468 if (renameNode) // Popup for renaming a node
1469 {
1470 ShowRenameNodeRequest(renameNode);
1471 }
1472
1473 if (ImGui::BeginPopup("Pin Context Menu"))
1474 {
1475 ImGui::TextUnformatted("Pin Context Menu");
1476 ImGui::Separator();
1477 if (auto* pin = nm::FindInputPin(contextPinId))
1478 {
1479 ImGui::Text("ID: %lu", size_t(pin->id));
1480 ImGui::Text("Node: %s", pin->parentNode ? std::to_string(size_t(pin->parentNode->id)).c_str() : "<none>");
1481 ImGui::Text("Type: %s", std::string(pin->type).c_str());
1482 ImGui::Separator();
1483 if (pin->isPinLinked())
1484 {
1485 const auto& link = pin->link;
1486 ImGui::SetNextItemOpen(true, ImGuiCond_Appearing);
1487 if (ImGui::TreeNode(fmt::format("LinkId: {}", size_t(link.linkId)).c_str()))
1488 {
1489 ImGui::BulletText("Connected Node: %s", link.connectedNode->nameId().c_str());
1490 ImGui::BulletText("Connected Pin: %s (%zu)", link.getConnectedPin()->name.c_str(),
1491 size_t(link.getConnectedPin()->id));
1492 ImGui::TreePop();
1493 }
1494 }
1495 else { ImGui::TextUnformatted("Link: Not linked"); }
1496 ImGui::Separator();
1497 if (ImGui::TreeNode(fmt::format("Queue: {}", pin->queue.size()).c_str()))
1498 {
1499 ImGui::BeginChild("QueueItems", ImVec2(ImGui::GetContentRegionAvail().x, 150), false, ImGuiWindowFlags_None);
1500 for (size_t i = 0; i < pin->queue.size(); i++) // NOLINT(modernize-loop-convert)
1501 {
1502 ImGui::Text("%s", std::string(pin->queue.at(i)->insTime.toYMDHMS()).c_str());
1503 }
1504 ImGui::EndChild();
1505 ImGui::TreePop();
1506 }
1507
1508 ImGui::Text("Queue blocked: %s", pin->queueBlocked ? "true" : "false");
1509 ImGui::Text("Temporal check: %s", pin->neededForTemporalQueueCheck ? "true" : "false");
1510 ImGui::Text("Drop queue: %s", pin->dropQueueIfNotFirable ? "true" : "false");
1511 ImGui::Separator();
1512 if (ImGui::MenuItem("Rename")) { renamePin = pin; }
1513 }
1514 else if (auto* pin = nm::FindOutputPin(contextPinId))
1515 {
1516 ImGui::Text("ID: %lu", size_t(pin->id));
1517 ImGui::Text("Node: %s", pin->parentNode ? std::to_string(size_t(pin->parentNode->id)).c_str() : "<none>");
1518 ImGui::Text("Type: %s", std::string(pin->type).c_str());
1519 ImGui::Text("Data available: %s", pin->noMoreDataAvailable ? "No" : "Yes");
1520 if (!pin->blocksConnectedNodeFromFinishing)
1521 {
1522 ImGui::TextUnformatted("Does not block connected pin from finishing!!!");
1523 }
1524 ImGui::Separator();
1525 if (pin->isPinLinked())
1526 {
1527 ImGui::SetNextItemOpen(true, ImGuiCond_Appearing);
1528 if (ImGui::TreeNode(fmt::format("Links: {}", pin->links.size()).c_str()))
1529 {
1530 for (const auto& link : pin->links)
1531 {
1532 ImGui::SetNextItemOpen(true, ImGuiCond_Appearing);
1533 if (ImGui::TreeNode(fmt::format("LinkId: {}", size_t(link.linkId)).c_str()))
1534 {
1535 ImGui::BulletText("Connected Node: %s", link.connectedNode->nameId().c_str());
1536 ImGui::BulletText("Connected Pin: %s (%zu)", link.getConnectedPin()->name.c_str(),
1537 size_t(link.getConnectedPin()->id));
1538 ImGui::TreePop();
1539 }
1540 }
1541 ImGui::TreePop();
1542 }
1543 }
1544 else { ImGui::TextUnformatted("Link: Not linked"); }
1545 ImGui::Separator();
1546 if (ImGui::MenuItem("Rename"))
1547 {
1548 renamePin = pin;
1549 }
1550 }
1551 else
1552 {
1553 ImGui::Text("Unknown pin: %lu", size_t(contextPinId));
1554 }
1555
1556 ImGui::EndPopup();
1557 }
1558
1559 if (renamePin) // Popup for renaming a pin
1560 {
1561 ShowRenamePinRequest(renamePin);
1562 }
1563
1564 if (ImGui::BeginPopup("Link Context Menu"))
1565 {
1566 ax::NodeEditor::PinId startPinId = 0;
1567 ax::NodeEditor::PinId endPinId = 0;
1568 for (const auto* node : nm::m_Nodes())
1569 {
1570 if (startPinId) { break; }
1571 for (const auto& output : node->outputPins)
1572 {
1573 if (startPinId) { break; }
1574 for (const auto& link : output.links)
1575 {
1576 if (link.linkId == contextLinkId)
1577 {
1578 startPinId = output.id;
1579 endPinId = link.connectedPinId;
1580 break;
1581 }
1582 }
1583 }
1584 }
1585
1586 ImGui::TextUnformatted("Link Context Menu");
1587 ImGui::Separator();
1588 if (startPinId)
1589 {
1590 ImGui::Text("ID: %lu", size_t(contextLinkId));
1591 ImGui::Text("From: %lu", size_t(startPinId));
1592 ImGui::Text("To: %lu", size_t(endPinId));
1593 }
1594 else
1595 {
1596 ImGui::Text("Unknown link: %lu", size_t(contextLinkId));
1597 }
1598 ImGui::Separator();
1599 if (ImGui::MenuItem("Delete"))
1600 {
1601 ed::DeleteLink(contextLinkId);
1602 }
1603 ImGui::EndPopup();
1604 }
1605
1606 static bool setKeyboardFocus = true;
1607 static ImVec2 newNodeSpawnPos{ -1, -1 };
1608 if (ImGui::BeginPopup("Create New Node"))
1609 {
1610 if (newNodeSpawnPos.x == -1 || newNodeSpawnPos.y == -1)
1611 {
1612 auto viewRect = reinterpret_cast<ax::NodeEditor::Detail::EditorContext*>(ed::GetCurrentEditor())->GetViewRect();
1613 newNodeSpawnPos = ImGui::GetMousePos();
1614 newNodeSpawnPos.x -= leftPaneWidth + SPLITTER_THICKNESS + 10.0F;
1615 newNodeSpawnPos.y -= menuBarHeight;
1616
1617 newNodeSpawnPos *= ed::GetCurrentZoom();
1618
1619 newNodeSpawnPos += viewRect.GetTL();
1620
1621 LOG_DEBUG("New Node will spawn at {}x{} - Zoom {}", newNodeSpawnPos.x, newNodeSpawnPos.y, ed::GetCurrentZoom());
1622 }
1623
1624 if (setKeyboardFocus)
1625 {
1626 ImGui::SetKeyboardFocusHere(0);
1627 }
1628 static ImGuiTextFilter filter;
1629
1630 filter.Draw("##NewNodeFilter");
1631
1632 if (setKeyboardFocus)
1633 {
1634 filter.Clear();
1635 setKeyboardFocus = false;
1636 }
1637
1638 Node* node = nullptr;
1639 for (const auto& [category, nodeInfoList] : NAV::NodeRegistry::RegisteredNodes())
1640 {
1641 // Prevent category from showing, if it is empty
1642 bool categoryHasItems = false;
1643 for (const auto& nodeInfo : nodeInfoList)
1644 {
1645 if (nodeInfo.hasCompatiblePin(newNodeLinkPin)
1646 && (filter.PassFilter(nodeInfo.type.c_str()) || filter.PassFilter(category.c_str())))
1647 {
1648 categoryHasItems = true;
1649 break;
1650 }
1651 }
1652 if (categoryHasItems)
1653 {
1654 ImGui::SetNextItemOpen(true, ImGuiCond_Once);
1655 if (ImGui::TreeNode((category + "##NewNodeTree").c_str()))
1656 {
1657 for (const auto& nodeInfo : nodeInfoList)
1658 {
1659 const auto& displayName = nodeInfo.type;
1660 const auto& constructor = nodeInfo.constructor;
1661 ImGui::Indent();
1662 if (nodeInfo.hasCompatiblePin(newNodeLinkPin)
1663 && (filter.PassFilter(nodeInfo.type.c_str()) || filter.PassFilter(category.c_str()))
1664 && ImGui::MenuItem(displayName.c_str()))
1665 {
1666 filter.Clear();
1667 node = constructor();
1668 nm::AddNode(node);
1669 }
1670 ImGui::Unindent();
1671 }
1672 ImGui::TreePop();
1673 }
1674 }
1675 }
1676
1677 if (node)
1678 {
1679 createNewNode = false;
1680
1681 ed::SetNodePosition(node->id, newNodeSpawnPos);
1682 newNodeSpawnPos = { -1, -1 };
1683
1684 if (auto* startPin = newNodeLinkPin)
1685 {
1686 if (startPin->kind == Pin::Kind::Input)
1687 {
1688 for (auto& pin : node->outputPins)
1689 {
1690 if (reinterpret_cast<InputPin*>(startPin)->canCreateLink(pin))
1691 {
1692 pin.createLink(*reinterpret_cast<InputPin*>(startPin));
1693 break;
1694 }
1695 }
1696 }
1697 else // if (startPin->kind == Pin::Kind::Output)
1698 {
1699 for (auto& pin : node->inputPins)
1700 {
1701 if (reinterpret_cast<OutputPin*>(startPin)->canCreateLink(pin))
1702 {
1703 reinterpret_cast<OutputPin*>(startPin)->createLink(pin);
1704 break;
1705 }
1706 }
1707 }
1708 }
1709 }
1710
1711 ImGui::EndPopup();
1712 }
1713 else
1714 {
1715 setKeyboardFocus = true;
1716 createNewNode = false;
1717 newNodeSpawnPos = { -1, -1 };
1718 }
1719 ImGui::PopStyleVar();
1720
1721 for (auto* node : nm::m_Nodes()) // Config Windows for nodes
1722 {
1723 if (node->_hasConfig && node->_showConfig)
1724 {
1725 ImVec2 center(ImGui::GetIO().DisplaySize.x * 0.5F, ImGui::GetIO().DisplaySize.y * 0.5F);
1726 if (node->_configWindowFocus)
1727 {
1728 ImGui::SetNextWindowCollapsed(false);
1729 ImGui::SetNextWindowFocus();
1730 node->_configWindowFocus = false;
1731 }
1732 ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5F, 0.5F));
1733 ImGui::SetNextWindowSize(node->_guiConfigDefaultWindowSize, ImGuiCond_FirstUseEver);
1734 if (!node->_configWindowMutex.try_lock())
1735 {
1736 ImGui::SetNextWindowCollapsed(true, ImGuiCond_Always);
1737 node->_configWindowForceCollapse = true;
1738 }
1739 else
1740 {
1741 node->_configWindowMutex.unlock();
1742 if (node->_configWindowForceCollapse)
1743 {
1744 LOG_TRACE("Setting next window collapsed: {}", node->_configWindowIsCollapsed);
1745 ImGui::SetNextWindowCollapsed(node->_configWindowIsCollapsed);
1746 node->_configWindowForceCollapse = false;
1747 }
1748 }
1749 if (ImGui::Begin(fmt::format("{} ({})", node->nameId(), node->type()).c_str(), &(node->_showConfig),
1750 ImGuiWindowFlags_None))
1751 {
1752 ImGui::PushFont(WindowFont());
1753 bool locked = node->_lockConfigDuringRun && (node->callbacksEnabled || FlowExecutor::isRunning());
1754 if (locked) { ImGui::BeginDisabled(); }
1755 if (!node->_configWindowForceCollapse)
1756 {
1757 node->_configWindowIsCollapsed = false;
1758 node->guiConfig();
1759 }
1760 if (locked) { ImGui::EndDisabled(); }
1761 ImGui::PopFont();
1762 }
1763 else // Window is collapsed
1764 {
1765 if (!node->_configWindowForceCollapse) { node->_configWindowIsCollapsed = true; }
1766 if (ImGui::IsWindowFocused())
1767 {
1768 ed::EnableShortcuts(true);
1769 }
1770 }
1771
1772 ImGui::End();
1773 }
1774 }
1775 ed::Resume();
1776
1777 ed::End();
1778
1779 ImGui::PushFont(PanelFont());
1780 ImGui::Indent(SPLITTER_THICKNESS);
1781 ImGui::SetNextItemOpen(true, ImGuiCond_Once);
1782 if (ImGui::BeginTabBar("BottomViewTabBar"))
1783 {
1784 bool noItemSelected = bottomViewSelectedTab == BottomViewTabItem::None;
1785 if (noItemSelected)
1786 {
1787 ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.0F);
1788 }
1789 if (ImGui::BeginTabItem("▼"))
1790 {
1793 ImGui::EndTabItem();
1794 }
1795 else
1796 {
1798 }
1799 if (noItemSelected)
1800 {
1801 ImGui::PopStyleVar();
1802 }
1803
1804 if (ImGui::BeginTabItem("Log Output", nullptr, firstFrame ? ImGuiTabItemFlags_SetSelected : ImGuiTabItemFlags_None))
1805 {
1806 static int scrollToBottom = 0;
1808 {
1809 scrollToBottom = 2;
1810 }
1812
1813 static bool autoScroll = true;
1814 static ImGuiTextFilter textFilter;
1815
1816 // Options menu
1817 if (ImGui::BeginPopup("Options"))
1818 {
1819 ImGui::Checkbox("Auto-scroll", &autoScroll);
1820 ImGui::EndPopup();
1821 }
1822
1823 // Main window
1824 if (ImGui::Button("Options"))
1825 {
1826 ImGui::OpenPopup("Options");
1827 }
1828 ImGui::SameLine();
1829 ImGui::SetNextItemWidth(100.0F * panelFontRatio());
1830 static int logLevelFilterSelected = spdlog::level::info;
1831 if (ImGui::BeginCombo("##LogLevelCombo", spdlog::level::to_string_view(static_cast<spdlog::level::level_enum>(logLevelFilterSelected)).begin()))
1832 {
1833 for (int n = spdlog::level::debug; n < spdlog::level::critical; n++)
1834 {
1835 const bool is_selected = (logLevelFilterSelected == n);
1836 if (ImGui::Selectable(spdlog::level::to_string_view(static_cast<spdlog::level::level_enum>(n)).begin(), is_selected))
1837 {
1838 logLevelFilterSelected = n;
1839 }
1840
1841 // Set the initial focus when opening the combo (scrolling + keyboard navigation focus)
1842 if (is_selected)
1843 {
1844 ImGui::SetItemDefaultFocus();
1845 }
1846 }
1847 ImGui::EndCombo();
1848 }
1849 ImGui::SameLine();
1850 textFilter.Draw("Filter", -100.0F);
1851
1852 ImGui::Separator();
1853 ImGui::BeginChild("scrolling", ImVec2(0, 0), false, ImGuiWindowFlags_HorizontalScrollbar);
1854 {
1855 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
1856 ImGui::PushFont(MonoFont());
1857
1858 auto logMessages = Logger::GetRingBufferSink()->last_formatted();
1859
1860 for (auto& logLine : logMessages)
1861 {
1862 for (int n = logLevelFilterSelected; n < spdlog::level::n_levels; n++)
1863 {
1864 if (logLine.find(fmt::format("] [{}]", spdlog::level::to_short_c_str(static_cast<spdlog::level::level_enum>(n)))) != std::string::npos)
1865 {
1866 if (!textFilter.IsActive() || textFilter.PassFilter(logLine.c_str()))
1867 {
1868 // str::replace(logLine, "[T]", "\033[30m[T]\033[0m");
1869 str::replace(logLine, "[D]", "\033[36m[D]\033[0m");
1870 str::replace(logLine, "[I]", "\033[32m[I]\033[0m");
1871 str::replace(logLine, "[W]", "\033[33m[W]\033[0m");
1872 str::replace(logLine, "[E]", "\033[31m[E]\033[0m");
1873 ImGui::TextAnsiUnformatted(logLine.c_str());
1874 // ImGui::TextAnsiUnformatted("\033[31;1;4mHello\033[0mtt");
1875 }
1876 break;
1877 }
1878 }
1879 }
1880 ImGui::PopFont();
1881 ImGui::PopStyleVar();
1882
1883 if (scrollToBottom)
1884 {
1885 ImGui::SetScrollHereY(1.0F);
1886 scrollToBottom--;
1887 }
1888 else if (autoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY())
1889 {
1890 ImGui::SetScrollHereY(1.0F);
1891 }
1892 }
1893 ImGui::EndChild();
1894
1895 ImGui::EndTabItem();
1896 }
1897 ImGui::EndTabBar();
1898 }
1899 ImGui::Unindent();
1900 ImGui::PopFont();
1901
1902 ImGui::EndGroup();
1903
1905
1906 // Push the Tooltip on the stack if needed
1907 if (!tooltipText.empty()) { ImGui::SetTooltip("%s", tooltipText.c_str()); }
1908
1909 ImGui::PushFont(WindowFont());
1911 ImGui::PopFont();
1912
1913 std::string title = (flow::HasUnsavedChanges() ? "● " : "")
1914 + (flow::GetCurrentFilename().empty() ? "" : flow::GetCurrentFilename() + " - ")
1915 + "INSTINCT";
1916 SetTitle(title.c_str());
1917}
1918
1920{
1921 return defaultFontSize.at(isUsingBigDefaultFont() ? 1 : 0) / defaultFontSize[0];
1922}
1923
1925{
1926 return windowFontSize.at(isUsingBigWindowFont() ? 1 : 0) / windowFontSize[0];
1927}
1928
1930{
1931 return panelFontSize.at(isUsingBigPanelFont() ? 1 : 0) / panelFontSize[0];
1932}
1933
1935{
1936 return monoFontSize.at(isUsingBigMonoFont() ? 1 : 0) / monoFontSize[0];
1937}
1938
1940{
1941 return headerFontSize.at(isUsingBigHeaderFont() ? 1 : 0) / headerFontSize[0];
1942}
Handles compiling resources into the binary.
Config management for the Project.
Handles Flow animations.
Flow Executor Thread.
Save/Load the Nodes.
nlohmann::json json
json namespace
@ Clear
Clear the flow.
@ QuitUnsaved
Quit the program without saving.
@ Quit
Quit the program.
@ Load
Load a flow.
@ None
None.
@ SaveAs
Save the flow as filename.
Global windows.
Text Help Marker (?) with Tooltip.
Defines how to save certain datatypes to json.
Left Pane where Nodes and Selection is shown.
#define LOG_DEBUG
Debug information. Should not be called on functions which receive observations (spamming)
Definition Logger.hpp:67
#define LOG_DATA
All output which occurs repeatedly every time observations are received.
Definition Logger.hpp:29
#define LOG_ERROR
Error occurred, which stops part of the program to work, but not everything.
Definition Logger.hpp:73
#define LOG_TRACE
Detailled info to trace the execution of the program. Should not be called on functions which receive...
Definition Logger.hpp:65
Main Menu Bar.
Manages all Nodes.
Utility class which specifies available nodes.
Node Class.
Pin class.
Defines all available shortcuts.
Spinner to show that something is done.
Screen Divider.
Utility functions for working with std::strings.
Text which can be colored by Ansi codes.
Touch Event Tracker.
static const std::shared_ptr< spdlog::sinks::ringbuffer_sink_mt > & GetRingBufferSink()
Returns the ring buffer sink.
Definition Logger.cpp:181
Input pins of nodes.
Definition Pin.hpp:491
bool isPinLinked() const
Checks if the pin is linked.
Definition Pin.cpp:355
bool canCreateLink(const OutputPin &other) const
Checks if this pin can connect to the provided pin.
Definition Pin.cpp:350
Abstract parent class for all nodes.
Definition Node.hpp:92
@ DoInitialize
Node should be initialized.
Definition Node.hpp:179
@ Initializing
Node is currently initializing.
Definition Node.hpp:180
@ DoDeinitialize
Node should be deinitialized.
Definition Node.hpp:182
@ Deinitializing
Node is currently deinitializing.
Definition Node.hpp:183
@ Deinitialized
Node is deinitialized (red)
Definition Node.hpp:178
std::vector< OutputPin > outputPins
List of output pins.
Definition Node.hpp:399
Kind kind
Kind of the Node.
Definition Node.hpp:393
std::vector< InputPin > inputPins
List of input pins.
Definition Node.hpp:397
@ POST_PROCESSING
Node running in post-processing mode.
Definition Node.hpp:192
bool _configWindowFocus
Flag if the config window should be focused.
Definition Node.hpp:445
bool _showConfig
Flag if the config window is shown.
Definition Node.hpp:435
std::string name
Name of the Node.
Definition Node.hpp:395
static std::string toString(State state)
Converts the state into a printable text.
Definition Node.cpp:263
ax::NodeEditor::NodeId id
Unique Id of the Node.
Definition Node.hpp:391
virtual std::string type() const =0
String representation of the Class Type.
Output pins of nodes.
Definition Pin.hpp:338
bool canCreateLink(const InputPin &other) const
Checks if this pin can connect to the provided pin.
Definition Pin.cpp:239
Pins in the GUI for information exchange.
Definition Pin.hpp:43
Node * parentNode
Reference to the parent node.
Definition Pin.hpp:307
std::string name
Name of the Pin.
Definition Pin.hpp:299
ax::NodeEditor::PinId id
Unique Id of the Pin.
Definition Pin.hpp:297
std::vector< std::string > dataIdentifier
One or multiple Data Identifiers (Unique name which is used for data flows)
Definition Pin.hpp:305
Type type
Type of the Pin.
Definition Pin.hpp:301
static bool dataIdentifierHaveCommon(const std::vector< std::string > &a, const std::vector< std::string > &b)
Checks if the first list of data identifiers has a common entry with the second.
Definition Pin.cpp:59
Kind kind
Kind of the Pin (Input/Output)
Definition Pin.hpp:303
static ImPlotStyle imPlotReferenceStyle
Default style of the ImPlot library to compare changes against.
static float bottomViewHeight
Height of the log viewer.
static float windowFontRatio()
Ratio to multiply for GUI window elements.
static std::array< ImTextureID, 2 > m_InstinctLogo
Pointer to the texture for the instinct logo.
static float leftPaneWidth
Width of the left pane.
static constexpr float SPLITTER_THICKNESS
Thickness of the splitter between left and right pane.
GlobalActions globalAction
Global action to execute.
bool OnQuitRequest() override
Called when the user request the application to close.
static void ShowRenameNodeRequest(Node *&renameNode)
Shows a PopupModal where the user can rename the node.
static std::vector< ImVec4 > m_colors
Color settings.
static float defaultFontRatio()
Ratio to multiply for default GUI elements.
static bool hideLeftPane
Hide left pane.
static float monoFontRatio()
Ratio to multiply for log output GUI elements.
static ImTextureID m_RoseFigure
Pointer to the texture for the rose figure (ImuSimulator node)
static std::array< ImTextureID, 2 > m_InsLogo
Pointer to the texture for the INS logo.
void OnStart() override
Called when the application is started.
void ShowSaveAsRequested()
Shows a PopupModel where the user can select a path to save his flow to.
static bool _showQueueSizeOnPins
Shows the queue size on the pins (every frame the queue mutex will be locked)
void ShowLoadRequested()
Shows a PopupModel loading a new flow.
void ShowQuitRequested()
Shows a PopupModal asking the user if he wants to quit with unsaved changes.
void OnStop() override
Called when the application is stopped.
ImTextureID m_HeaderBackground
Pointer to the texture for the node headers.
static void ShowRenamePinRequest(Pin *&renamePin)
Shows a PopupModal where the user can rename the pin.
void ShowClearNodesRequested()
Shows a PopupModel to clear the current flow.
static std::vector< const char * > m_colorsNames
Color names.
void OnFrame(float deltaTime) override
Called on every frame.
static float rightPaneWidth
Width of the right pane.
static constexpr float BOTTOM_VIEW_COLLAPSED_MIN_HEIGHT
Minimal height of the bottom view if it is collapsed.
static float panelFontRatio()
Ratio to multiply for GUI editor elements.
static constexpr float BOTTOM_VIEW_UNCOLLAPSED_MIN_HEIGHT
Minimal height of the bottom view if it is not collapsed.
static ImTextureID m_SaveButtonImage
Pointer to the texture for the save button.
int frameCountNavigate
Frame counter to block the navigate to content function till nodes are correctly loaded.
BottomViewTabItem bottomViewSelectedTab
Selected Tab item in the bottom view.
static float headerFontRatio()
Ratio to multiply for node header elements.
@ COLOR_GROUP_OUTER_BORDER
Color of the group outer border.
@ COLOR_GROUP_HEADER_BG
Color of the group header background.
@ COLOR_GROUP_HEADER_TEXT
Color of the group header text.
static float menuBarHeight
Height of the menu bar on top.
static void EndOutput()
Ends building the output pin.
void Output(ax::NodeEditor::PinId id)
Begins building an output pin.
void Begin(ax::NodeEditor::NodeId id)
Begins building a node.
void Middle()
Begins building of the middle of the node.
void Input(ax::NodeEditor::PinId id)
Begins building an input pin.
void Header(const ImVec4 &color=ImVec4(1, 1, 1, 1))
Begins building the header.
static void EndInput()
Ends building the input pin.
void TextAnsiUnformatted(const char *text, const char *text_end=nullptr)
Displays an unformatted ansi text.
void LoadGlobalSettings()
Loads the global settings.
const T & Get(const std::string &key, const T &&defaultValue)
Retrieves the value of a corresponding key from the configuration, if one exists.
void SaveGlobalSettings()
Saves the global settings.
void ProcessQueue()
Triggers the queued flow animations.
bool isRunning() noexcept
Checks if the thread is running.
void stop()
Stops the Thread.
bool DeleteNode(ax::NodeEditor::NodeId nodeId)
Delete the node provided by id.
InputPin * FindInputPin(ax::NodeEditor::PinId id)
Finds the Pin for the PinId.
OutputPin * FindOutputPin(ax::NodeEditor::PinId id)
Finds the Pin for the PinId.
void DeleteAllNodes()
Delete all nodes.
const std::vector< Node * > & m_Nodes()
List of all registered Nodes.
void AddNode(Node *node)
Add the provided node object to the list of nodes.
Node * FindNode(ax::NodeEditor::NodeId id)
Finds the Node for the NodeId.
bool NodeDataTypeAnyIsChildOf(const std::vector< std::string > &childTypes, const std::vector< std::string > &parentTypes)
Checks if any of the provided child types is a child of any of the provided parent types.
const std::map< std::string, std::vector< NodeInfo > > & RegisteredNodes()
Reference to List of all registered Nodes.
void SetCurrentFilename(const std::string &newFilename)
Set the current filename of the open flow.
bool LoadFlow(const std::string &filepath)
Loads the flow from the specified file.
std::filesystem::path GetConfigPath()
Get the path where config files are searched.
void SaveFlowAs(const std::string &filepath)
Saves the current flow into the specified file.
void DiscardChanges()
Discards the unsaved changes flag. Does not really discard the changes.
std::string GetCurrentFilename()
Get the current filename of the open flow.
void ApplyChanges()
Signals that there have been changes to the flow.
std::filesystem::path GetFlowPath()
Get the path where flow files are searched.
bool HasUnsavedChanges()
Checks if the currently open flow has unsaved changes.
void ShowMainMenuBar(GlobalActions &globalAction)
Shows the main menu bar and moves down the cursor.
bool ShowLeftPane(float paneWidth)
Shows the left overview pane.
Definition LeftPane.cpp:27
void Spinner(const char *label, const ImU32 &color, float radius, float thickness=1.0F)
Shows a Spinner to signal that work is done.
Definition Spinner.cpp:15
bool Splitter(const char *str_id, bool split_vertically, float thickness, float *size1, float *size2, float min_size1, float min_size2, float splitter_long_axis_size=-1.0F)
Vertical or horizontal Screen Divider.
Definition Splitter.cpp:16
void HelpMarker(const char *desc, const char *symbol="(?)")
Text Help Marker, e.g. '(?)', with Tooltip.
void renderGlobalWindows(std::vector< ImVec4 > &colors, const std::vector< const char * > &colorNames)
Called every frame to render global windows.
Definition Global.cpp:32
void checkShortcuts(GlobalActions &globalAction)
Checks if a shortcut was pressed.
Definition Shortcuts.cpp:23
void TouchNode(ax::NodeEditor::NodeId id)
Trigger a touch event on the specified node.
void UpdateTouch(float deltaTime)
Updates the touch events for all nodes.
static bool replace(std::string &str, const std::string &from, const std::string &to, CaseSensitivity cs=RespectCase)
Replaces the first occurrence of a search pattern with another sequence.
@ Blueprint
Node with header.
Definition Node.hpp:100
@ GroupBox
Group box which can group other nodes and drag them together.
Definition Node.hpp:102
@ Simple
Node without header, which displays its name in the center of the content.
Definition Node.hpp:101
@ Input
Input Pin.
Definition Pin.hpp:171
@ Output
Output Pin.
Definition Pin.hpp:170
@ Delegate
Reference to the Node object.
Definition Pin.hpp:59
@ None
Not initialized.
Definition Pin.hpp:51
@ Matrix
Matrix Object.
Definition Pin.hpp:58
@ Flow
NodeData Trigger.
Definition Pin.hpp:52
@ Object
Generic Object.
Definition Pin.hpp:57