14#include <imgui_node_editor.h>
43std::vector<NAV::Node*> m_nodes;
46bool unsavedChanges =
false;
48constexpr int loadingFramesToWait = 2;
50std::string currentFilename;
51std::filesystem::path programRootPath;
54size_t currentRotatedParentFolderNumber;
56int loadingFrameCount = 0;
74 return { GetNextId() };
79 return { GetNextId() };
84 return { GetNextId() };
93 m_nodes.push_back(node);
98 pin.parentNode = node;
102 pin.parentNode = node;
105 m_NextId = std::max(m_NextId,
size_t(node->
id) + 1);
108 m_NextId = std::max(m_NextId,
size_t(pin.id) + 1);
112 m_NextId = std::max(m_NextId,
size_t(pin.id) + 1);
123 pin.parentNode = node;
127 pin.parentNode = node;
132 m_NextId = std::max(m_NextId,
size_t(pin.id) + 1);
136 m_NextId = std::max(m_NextId,
size_t(pin.id) + 1);
142 LOG_TRACE(
"called for node with id {}",
size_t(nodeId));
144 auto it = std::ranges::find_if(m_nodes, [nodeId](
const auto& node) {
return node->id == nodeId; });
145 if (it != m_nodes.end())
157 if (inputPin.isPinLinked())
159 inputPin.deleteLink();
164 if (outputPin.isPinLinked())
166 outputPin.deleteLinks();
184 while (!m_nodes.empty())
196 m_NextId = std::max(m_NextId,
size_t(linkId) + 1);
201 for (
auto& node : m_nodes)
214 if (!
id) {
return nullptr; }
216 for (
auto& node : m_nodes)
219 for (
auto& pin : node->outputPins)
221 if (pin.id ==
id) {
return &pin; }
230 if (!
id) {
return nullptr; }
232 for (
auto& node : m_nodes)
235 for (
auto& pin : node->inputPins)
237 if (pin.id ==
id) {
return &pin; }
247 for (
auto* node : m_nodes)
251 node->callbacksEnabled =
true;
259 for (
auto* node : m_nodes)
261 node->callbacksEnabled =
false;
268 for (
auto* node : m_nodes)
270 for (
auto& inputPin : node->inputPins)
272 inputPin.queue.clear();
280 bool nodeCouldNotInitialize =
false;
284 for (
auto* node : m_nodes)
286 if (node && node->kind !=
Node::Kind::GroupBox && !node->isDisabled() && !node->isInitialized())
288 if (!node->doInitialize(
true))
290 LOG_ERROR(
"Node '{}' could not initialize.", node->nameId());
291 nodeCouldNotInitialize =
true;
296 return !nodeCouldNotInitialize;
303 for (
auto* node : m_nodes)
305 if (node && node->kind !=
Node::Kind::GroupBox && !node->isDisabled() && !node->isInitialized())
307 node->doInitialize();
314 if (currentFilename.empty())
326 std::ofstream filestream(filepath);
328 if (!filestream.good())
330 std::cerr <<
"Save Flow error: Could not open file: " << filepath;
335 for (
const auto& node :
m_Nodes())
337 j[
"nodes"][
"node-" + std::to_string(
size_t(node->id))] = *node;
338 j[
"nodes"][
"node-" + std::to_string(
size_t(node->id))][
"data"] = node->save();
340 for (
const auto& outputPin : node->outputPins)
342 for (
const auto& link : outputPin.links)
344 auto& jLink = j[
"links"][
"link-" + std::to_string(
size_t(link.linkId))];
345 jLink[
"id"] = size_t(link.linkId);
346 jLink[
"startPinId"] = size_t(outputPin.id);
347 jLink[
"endPinId"] = size_t(link.connectedPinId);
353 j[
"implot"][
"style"] = ImPlot::GetStyle();
357 j[
"fonts"][
"useBigDefaultFont"] = gui::NodeEditorApplication::isUsingBigDefaultFont();
358 j[
"fonts"][
"useBigWindowFont"] = gui::NodeEditorApplication::isUsingBigWindowFont();
359 j[
"fonts"][
"useBigPanelFont"] = gui::NodeEditorApplication::isUsingBigPanelFont();
360 j[
"fonts"][
"useBigMonoFont"] = gui::NodeEditorApplication::isUsingBigMonoFont();
367 j[
"gridLinesEnabled"] = ed::GetStyle().Colors[ed::StyleColor_Grid].w;
368 j[
"transparentWindows"] = ImGui::GetStyle().Colors[ImGuiCol_WindowBg].w;
379 filestream << std::setw(4) << j << std::endl;
381 unsavedChanges =
false;
386 LOG_TRACE(
"called for path {}", filepath);
387 bool loadSuccessful =
true;
391 std::ifstream filestream(filepath);
393 if (!filestream.good())
395 LOG_ERROR(
"Load Flow error: Could not open file: {}", filepath);
410 CallPreInitCallback();
419 loadSuccessful =
false;
430 loadingFrameCount = ImGui::GetFrameCount();
432 unsavedChanges =
false;
433 currentFilename = filepath;
435 std::string path = filepath;
439 if (path.starts_with(
'\\') || path.starts_with(
'/')) { path = path.substr(1); }
442 LOG_INFO(
"Loaded flow file: {}", path);
444 catch (
const std::exception& e)
446 LOG_ERROR(
"Loading flow file failed with error: {}", e.what());
447 loadSuccessful =
false;
450 return loadSuccessful;
455 bool loadSuccessful =
true;
457 if (j.contains(
"implot"))
461 if (j.at(
"implot").contains(
"prefereFlowOverGlobal"))
468 inputPath.is_relative())
470 filepath /= inputPath;
474 filepath = inputPath;
481 if (j.at(
"implot").contains(
"style"))
483 j.at(
"implot").at(
"style").get_to(ImPlot::GetStyle());
493 if (j.contains(
"colormaps"))
505 if (j.contains(
"fonts"))
507 if (j.at(
"fonts").contains(
"useBigDefaultFont"))
509 gui::NodeEditorApplication::swapDefaultFont(j.at(
"fonts").at(
"useBigDefaultFont").get<
bool>());
511 if (j.at(
"fonts").contains(
"useBigWindowFont"))
513 gui::NodeEditorApplication::swapWindowFont(j.at(
"fonts").at(
"useBigWindowFont").get<
bool>());
515 if (j.at(
"fonts").contains(
"useBigPanelFont"))
517 gui::NodeEditorApplication::swapPanelFont(j.at(
"fonts").at(
"useBigPanelFont").get<
bool>());
519 if (j.at(
"fonts").contains(
"useBigMonoFont"))
521 gui::NodeEditorApplication::swapMonoFont(j.at(
"fonts").at(
"useBigMonoFont").get<
bool>());
524 if (j.contains(
"leftPane"))
532 if (j.contains(
"lightMode"))
537 if (j.contains(
"gridLinesEnabled")) { j.at(
"gridLinesEnabled").get_to(ed::GetStyle().Colors[ed::StyleColor_Grid].w); }
538 if (j.contains(
"transparentWindows")) { j.at(
"transparentWindows").get_to(ImGui::GetStyle().Colors[ImGuiCol_WindowBg].w); }
541 if (j.contains(
"nodes"))
543 for (
const auto& nodeJson : j.at(
"nodes"))
545 if (!nodeJson.contains(
"type"))
547 LOG_ERROR(
"Node does not contain a type");
550 Node* node =
nullptr;
553 for (
const auto& nodeInfo : registeredNode.second)
555 if (nodeInfo.type == nodeJson.at(
"type").get<std::string>())
557 node = nodeInfo.constructor();
568 LOG_ERROR(
"Node type ({}) is not a valid type.", nodeJson.at(
"type").get<std::string>());
569 loadSuccessful =
false;
574 auto newNodeId = node->
id;
576 nodeJson.get_to<
Node>(*node);
577 if (nodeJson.contains(
"data"))
579 node->
restore(nodeJson.at(
"data"));
582 nodeJson.get_to<
Node>(*node);
586 node->
id = newNodeId;
601 ed::SetNodePosition(node->
id, nodeJson.at(
"pos").get<ImVec2>());
605 ed::SetGroupSize(node->
id, node->
getSize());
612 std::set<Node*> newlyLinkedNodes;
614 if (j.contains(
"links"))
616 for (
size_t i = 0; i < 2; i++)
618 for (
const auto& linkJson : j.at(
"links"))
620 auto linkId = linkJson.at(
"id").get<
size_t>();
621 auto startPinId = linkJson.at(
"startPinId").get<
size_t>();
622 auto endPinId = linkJson.at(
"endPinId").get<
size_t>();
630 for (
auto& inputPin : node->inputPins)
632 if (endPinId ==
size_t(inputPin.id)) { endPin = &inputPin; }
637 for (
auto& outputPin : node->outputPins)
639 if (startPinId ==
size_t(outputPin.id)) { startPin = &outputPin; }
642 if (startPin && endPin) {
break; }
644 if (startPin && endPin)
648 loadSuccessful =
false;
651 newlyLinkedNodes.insert(startPin->
parentNode);
652 newlyLinkedNodes.insert(endPin->parentNode);
657 if (j.contains(
"nodes"))
659 for (
auto* node : newlyLinkedNodes)
661 if (j.at(
"nodes").contains(
"node-" + std::to_string(
size_t(node->id))))
663 LOG_DEBUG(
"Calling restoreAtferLink() for new node '{}'", node->nameId());
665 const auto& nodeJson = j.at(
"nodes").at(
"node-" + std::to_string(
size_t(node->id)));
666 if (nodeJson.contains(
"data"))
668 node->restoreAtferLink(nodeJson.at(
"data"));
674 return loadSuccessful;
679 return unsavedChanges;
685 if (ImGui::GetCurrentContext() && ImGui::GetFrameCount() - loadingFrameCount >= loadingFramesToWait)
687 unsavedChanges =
true;
693 unsavedChanges =
false;
698 return currentFilename;
703 currentFilename = newFilename;
708 return programRootPath;
713 LOG_DEBUG(
"Program root path set to {}", newRootPath);
714 programRootPath = newRootPath;
722 outputPath.is_relative())
724 filepath /= outputPath;
728 filepath = outputPath;
733 filepath /= fmt::format(
"{:04d}", currentRotatedParentFolderNumber);
741 currentRotatedParentFolderNumber = 0;
742 for (
int i = 10000; i >= 0; --i)
744 std::filesystem::path outputDir{ programRootPath };
747 outputPath.is_relative())
749 outputDir /= outputPath;
753 outputDir = outputPath;
755 outputDir /= fmt::format(
"{:04d}", i);
756 if (std::filesystem::exists(outputDir))
758 currentRotatedParentFolderNumber =
static_cast<size_t>(i + 1);
770 inputPath.is_relative())
772 filepath /= inputPath;
776 filepath = inputPath;
787 inputPath.is_relative())
789 filepath /= inputPath;
793 filepath = inputPath;
808std::vector<std::pair<ax::NodeEditor::PinId, NAV::InputPin::WatcherCallback>> watcherPinList;
809std::vector<std::pair<ax::NodeEditor::LinkId, NAV::InputPin::WatcherCallback>> watcherLinkList;
811std::function<void()> preInitCallback =
nullptr;
812std::function<void()> cleanupCallback =
nullptr;
816void NAV::flow::RegisterWatcherCallbackToInputPin(ax::NodeEditor::PinId
id,
const InputPin::WatcherCallback& callback)
818 watcherPinList.emplace_back(
id, callback);
821void NAV::flow::RegisterWatcherCallbackToLink(ax::NodeEditor::LinkId
id,
const InputPin::WatcherCallback& callback)
823 watcherLinkList.emplace_back(
id, callback);
826void NAV::flow::ApplyWatcherCallbacks()
828 for (
auto& [linkId, callback] : watcherLinkList)
830 for (
auto& node : m_nodes)
832 for (
size_t pinIdx = 0; pinIdx < node->inputPins.size(); pinIdx++)
834 auto& pin = node->inputPins[pinIdx];
835 if (pin.isPinLinked() && pin.link.linkId == linkId)
837 LOG_DEBUG(
"Adding watcher callback on node '{}' on pin with index {}", pin.parentNode->nameId(), pinIdx);
838 pin.watcherCallbacks.emplace_back(callback);
844 for (
auto& [
id, callback] : watcherPinList)
846 for (
auto& node : m_nodes)
848 for (
size_t pinIdx = 0; pinIdx < node->inputPins.size(); pinIdx++)
850 auto& pin = node->inputPins[pinIdx];
853 LOG_DEBUG(
"Adding watcher callback on node '{}' on pin with index {}", pin.parentNode->nameId(), pinIdx);
854 pin.watcherCallbacks.emplace_back(callback);
861void NAV::flow::RegisterPreInitCallback(std::function<
void()> callback)
863 preInitCallback = std::move(callback);
866void NAV::flow::CallPreInitCallback()
874void NAV::flow::RegisterCleanupCallback(std::function<
void()> callback)
876 cleanupCallback = std::move(callback);
878void NAV::flow::CallCleanupCallback()
886void NAV::flow::ClearRegisteredCallbacks()
888 watcherPinList.clear();
889 watcherLinkList.clear();
890 preInitCallback =
nullptr;
891 cleanupCallback =
nullptr;
Common logging variables like time into run and local positions.
Config management for the Project.
nlohmann::json json
json namespace
GlobalActions
Possible Global Actions to perform in the GUI.
@ SaveAs
Save the flow as filename.
ImPlot style editor window.
Defines how to save certain datatypes to json.
#define LOG_DEBUG
Debug information. Should not be called on functions which receive observations (spamming)
#define LOG_ERROR
Error occurred, which stops part of the program to work, but not everything.
#define LOG_INFO
Info to the user on the state of the program.
#define LOG_TRACE
Detailled info to trace the execution of the program. Should not be called on functions which receive...
Utility class which specifies available nodes.
static void restore(const json &j)
Read info from a json object.
static json save()
Returns a json object of the common log.
Abstract parent class for all nodes.
bool isInitialized() const
Checks if the node is initialized.
bool doDeinitialize(bool wait=false)
Asks the node worker to deinitialize the node.
virtual void restore(const json &j)
Restores the node from a json object.
const ImVec2 & getSize() const
Get the size of the node.
std::vector< OutputPin > outputPins
List of output pins.
std::vector< InputPin > inputPins
List of input pins.
std::string nameId() const
Node name and id.
ax::NodeEditor::NodeId id
Unique Id of the Node.
bool createLink(InputPin &endPin, ax::NodeEditor::LinkId linkId=0)
Creates a link from this pin to another, calling all node specific callbacks.
Node * parentNode
Reference to the parent node.
static float bottomViewHeight
Height of the log viewer.
static float leftPaneWidth
Width of the left pane.
static std::vector< ImVec4 > m_colors
Color settings.
static bool hideLeftPane
Hide left pane.
static float rightPaneWidth
Width of the right pane.
static bool hideFPS
Hide FPS counter.
const T & Get(const std::string &key, const T &&defaultValue)
Retrieves the value of a corresponding key from the configuration, if one exists.
bool isRunning() noexcept
Checks if the thread is running.
void stop()
Stops the Thread.
const std::map< std::string, std::vector< NodeInfo > > & RegisteredNodes()
Reference to List of all registered Nodes.
void UpdateNode(Node *node)
Update the provided node object.
InputPin * FindInputPin(ax::NodeEditor::PinId id)
Finds the Pin for the PinId.
ax::NodeEditor::LinkId GetNextLinkId()
Generates a new link id.
const std::vector< Node * > & m_Nodes()
List of all registered Nodes.
Node * FindNode(ax::NodeEditor::NodeId id)
Finds the Node for the NodeId.
void SetProgramRootPath(const std::filesystem::path &newRootPath)
Set the program root path.
bool LoadJson(const json &j, bool requestNewIds=false)
Loads the nodes and links from the specified json object.
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.
void DisableAllCallbacks()
Disables all Node callbacks.
std::filesystem::path GetConfigPath()
Get the path where config files are searched.
std::filesystem::path GetOutputPath()
Get the path where logs and outputs are stored.
void DeleteAllNodes()
Delete all nodes.
void SaveFlowAs(const std::string &filepath)
Saves the current flow into the specified file.
std::filesystem::path GetInputPath()
Get the path where data files are searched.
void DiscardChanges()
Discards the unsaved changes flag. Does not really discard the changes.
bool DeleteNode(ax::NodeEditor::NodeId nodeId)
Delete the node provided by id.
ax::NodeEditor::PinId GetNextPinId()
Generates a new pin id.
std::string GetCurrentFilename()
Get the current filename of the open flow.
void ApplyChanges()
Signals that there have been changes to the flow.
ax::NodeEditor::NodeId GetNextNodeId()
Generates a new node id.
void AddLink(ax::NodeEditor::LinkId linkId)
Adds the link.
std::filesystem::path GetFlowPath()
Get the path where flow files are searched.
void ClearAllNodeQueues()
Clears all nodes queues.
OutputPin * FindOutputPin(ax::NodeEditor::PinId id)
Finds the Pin for the PinId.
void EnableAllCallbacks()
Enables all Node callbacks.
std::filesystem::path GetProgramRootPath()
Get the program root path.
bool HasUnsavedChanges()
Checks if the currently open flow has unsaved changes.
void SaveFlow(GlobalActions &globalAction)
Saves the current flow into a file.
void SetOutputPath()
Set the path where logs and outputs are stored.
void AddNode(Node *node)
Add the provided node object to the list of nodes.
bool InitializeAllNodes()
Initializes all nodes.
void InitializeAllNodesAsync()
Initializes all nodes in a separate thread.
bool prefereFlowOverGlobal
If true, the ImPlot config from the flow file will be preferred over the global settings file.
bool nodeEditorLightMode
If true, light mode is selected.
void ApplyDarkLightMode(std::vector< ImVec4 > &colors, bool transparentWindows)
bool saveConfigInFlow
If true, the ImPlot config will be saved into the flow file.
std::vector< Colormap > ColormapsFlow
Flow colormaps.
@ GroupBox
Group box which can group other nodes and drag them together.