0.3.0
Loading...
Searching...
No Matches
GlobalActions.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
9#include "GlobalActions.hpp"
10
12namespace nm = NAV::NodeManager;
13
15
17
18#include <imgui_node_editor.h>
19#include <imgui_node_editor_internal.h>
20namespace ed = ax::NodeEditor;
21
22#include <nlohmann/json.hpp>
23using json = nlohmann::json; ///< json namespace
24#include "util/Json.hpp"
25
27
28#include <vector>
29#include <deque>
30#include <limits>
31#include <iterator>
32
33namespace NAV::gui
34{
35namespace
36{
37/// @brief Specifies if the elements in the clipboard are cutted or copied
38bool elementsCutted = false;
39/// @brief Clipboard storage
40json clipboard;
41
42// /// @brief Maximum size of the action list
43// constexpr size_t ACTION_LIST_MAX_SIZE = 20;
44
45/// @brief Current action in the action list
46size_t currentAction = 0;
47/// @brief List of actions performed by the user
48std::deque<json> actionList;
49
50} // namespace
51} // namespace NAV::gui
52
54{
55 return static_cast<bool>(ed::GetSelectedNodes(nullptr, ed::GetSelectedObjectCount()));
56}
57
59{
60 return !clipboard.empty();
61}
62
64{
65 std::vector<ax::NodeEditor::NodeId> selectedNodeIds;
66 selectedNodeIds.resize(static_cast<size_t>(ed::GetSelectedObjectCount()));
67
68 auto selectedNodesCount = ed::GetSelectedNodes(selectedNodeIds.data(), ed::GetSelectedObjectCount());
69 selectedNodeIds.resize(static_cast<size_t>(selectedNodesCount));
70
71 clipboard.clear();
73
74 for (const auto& nodeId : selectedNodeIds)
75 {
76 const NAV::Node* node = nm::FindNode(nodeId);
77
78 clipboard["nodes"]["node-" + std::to_string(size_t(node->id))] = *node;
79 clipboard["nodes"]["node-" + std::to_string(size_t(node->id))]["data"] = node->save();
80
81 for (const auto& outputPin : node->outputPins)
82 {
83 for (const auto& link : outputPin.links)
84 {
85 auto& j = clipboard["links"]["link-" + std::to_string(size_t(link.linkId))];
86 j["id"] = size_t(link.linkId);
87 j["startPinId"] = size_t(outputPin.id);
88 j["endPinId"] = size_t(link.connectedPinId);
89 }
90 }
91
92 nm::DeleteNode(nodeId);
93 }
94
95 elementsCutted = true;
96
99}
100
102{
103 std::vector<ax::NodeEditor::NodeId> selectedNodeIds;
104 selectedNodeIds.resize(static_cast<size_t>(ed::GetSelectedObjectCount()));
105
106 auto selectedNodesCount = ed::GetSelectedNodes(selectedNodeIds.data(), ed::GetSelectedObjectCount());
107 selectedNodeIds.resize(static_cast<size_t>(selectedNodesCount));
108
109 clipboard.clear();
110
111 for (const auto& nodeId : selectedNodeIds)
112 {
113 const NAV::Node* node = nm::FindNode(nodeId);
114
115 clipboard["nodes"]["node-" + std::to_string(size_t(node->id))] = *node;
116 clipboard["nodes"]["node-" + std::to_string(size_t(node->id))]["data"] = node->save();
117
118 for (const auto& outputPin : node->outputPins)
119 {
120 for (const auto& link : outputPin.links)
121 {
122 auto& j = clipboard["links"]["link-" + std::to_string(size_t(link.linkId))];
123 j["id"] = size_t(link.linkId);
124 j["startPinId"] = size_t(outputPin.id);
125 j["endPinId"] = size_t(link.connectedPinId);
126 }
127 }
128 }
129
130 elementsCutted = false;
131}
132
134{
135 // Store the node count to later iterate over the new nodes
136 auto nodeCountBeforeLoad = nm::m_Nodes().size();
137
139
140 LOG_DEBUG("Pasting clipboard {}", clipboard.dump(4));
141
142 flow::LoadJson(clipboard, !elementsCutted);
143
144 // Find Top Left Position of all new nodes to move them to the mouse cursor
145 ImVec2 leftTopMostPos{ std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity() };
146 if (clipboard.contains("nodes"))
147 {
148 for (const auto& nodeJson : clipboard.at("nodes"))
149 {
150 ImVec2 pos;
151 if (nodeJson.contains("pos"))
152 {
153 nodeJson.at("pos").get_to(pos);
154
155 leftTopMostPos.x = std::min(pos.x, leftTopMostPos.x);
156 leftTopMostPos.y = std::min(pos.y, leftTopMostPos.y);
157 }
158 }
159 }
160
161 // Get Mouse Position in editor coordinates
162 auto viewRect = reinterpret_cast<ax::NodeEditor::Detail::EditorContext*>(ed::GetCurrentEditor())->GetViewRect();
163 ImVec2 mousePos = ImGui::GetMousePos();
166 mousePos *= ed::GetCurrentZoom();
167 mousePos += viewRect.GetTL();
168
169 // Move the Nodes relative to the current mouse position
170 for (size_t i = nodeCountBeforeLoad; i < nm::m_Nodes().size(); i++)
171 {
172 auto* node = nm::m_Nodes().at(i);
173 ed::SetNodePosition(node->id, mousePos + (ed::GetNodePosition(node->id) - leftTopMostPos));
174 }
175
176 // Collect the node ids which get new links to call the restoreAfterLinks function on them
177 std::map<size_t, ed::NodeId> newlyLinkedNodes;
178
179 // Recreate links
180 if (clipboard.contains("links"))
181 {
182 for (const auto& linkJson : clipboard.at("links"))
183 {
184 auto startPinId = linkJson.at("startPinId").get<size_t>();
185 auto endPinId = linkJson.at("endPinId").get<size_t>();
186
187 size_t startPinOldParentNodeId = 0;
188 size_t startPinParentNodeIndex = 0;
189 size_t startPinIndex = 0;
190 Pin::Kind startPinKind = Pin::Kind::None;
191
192 size_t endPinOldParentNodeId = 0;
193 size_t endPinParentNodeIndex = 0;
194 size_t endPinIndex = 0;
195 Pin::Kind endPinKind = Pin::Kind::None;
196
197 // Search for the nodes and pins which where connected by the old link
198 if (clipboard.contains("nodes"))
199 {
200 size_t nodeIndex = 0;
201 for (const auto& nodeJson : clipboard.at("nodes"))
202 {
203 if (nodeJson.contains("inputPins"))
204 {
205 size_t pinIndex = 0;
206 for (const auto& pinJson : nodeJson.at("inputPins"))
207 {
208 if (pinJson.at("id").get<size_t>() == startPinId)
209 {
210 startPinOldParentNodeId = nodeJson.at("id");
211 startPinParentNodeIndex = nodeCountBeforeLoad + nodeIndex;
212 startPinIndex = pinIndex;
213 startPinKind = Pin::Kind::Input;
214 }
215 if (pinJson.at("id").get<size_t>() == endPinId)
216 {
217 endPinOldParentNodeId = nodeJson.at("id");
218 endPinParentNodeIndex = nodeCountBeforeLoad + nodeIndex;
219 endPinIndex = pinIndex;
220 endPinKind = Pin::Kind::Input;
221 }
222 pinIndex++;
223 }
224 }
225 if (nodeJson.contains("outputPins"))
226 {
227 size_t pinIndex = 0;
228 for (const auto& pinJson : nodeJson.at("outputPins"))
229 {
230 if (pinJson.at("id").get<size_t>() == startPinId)
231 {
232 startPinOldParentNodeId = nodeJson.at("id");
233 startPinParentNodeIndex = nodeCountBeforeLoad + nodeIndex;
234 startPinIndex = pinIndex;
235 startPinKind = Pin::Kind::Output;
236 }
237 if (pinJson.at("id").get<size_t>() == endPinId)
238 {
239 endPinOldParentNodeId = nodeJson.at("id");
240 endPinParentNodeIndex = nodeCountBeforeLoad + nodeIndex;
241 endPinIndex = pinIndex;
242 endPinKind = Pin::Kind::Output;
243 }
244 pinIndex++;
245 }
246 }
247 nodeIndex++;
248 }
249 }
250
251 if (startPinKind != Pin::Kind::None && endPinKind != Pin::Kind::None)
252 {
253 if (startPinKind == Pin::Kind::Output && endPinKind == Pin::Kind::Input)
254 {
255 auto& startPin = nm::m_Nodes().at(startPinParentNodeIndex)->outputPins.at(startPinIndex);
256 auto& endPin = nm::m_Nodes().at(endPinParentNodeIndex)->inputPins.at(endPinIndex);
257
258 if (!endPin.isPinLinked())
259 {
260 startPin.createLink(endPin);
261 }
262 }
263
264 newlyLinkedNodes[startPinOldParentNodeId] = nm::m_Nodes().at(startPinParentNodeIndex)->id;
265 newlyLinkedNodes[endPinOldParentNodeId] = nm::m_Nodes().at(endPinParentNodeIndex)->id;
266 }
267 }
268 }
269 if (clipboard.contains("nodes"))
270 {
271 for (auto [oldId, newId] : newlyLinkedNodes)
272 {
273 auto* node = nm::FindNode(newId);
274
275 if (clipboard.at("nodes").contains("node-" + std::to_string(oldId)))
276 {
277 [[maybe_unused]] auto* oldNode = nm::FindNode(oldId);
278
279 LOG_DEBUG("Calling restoreAtferLink() for new node '{}', which was copied from node '{}'", node->nameId(), oldNode->nameId());
280
281 const auto& nodeJson = clipboard.at("nodes").at("node-" + std::to_string(oldId));
282 if (nodeJson.contains("data"))
283 {
284 node->restoreAtferLink(nodeJson.at("data"));
285 }
286 }
287 }
288 }
289
290 elementsCutted = false;
291
292 NAV::flow::loadingFrameCount = ImGui::GetFrameCount();
295}
296
298{
299 return false;
300 // return currentAction > 0;
301}
302
304{
305 return false;
306 // return currentAction + 1 < actionList.size();
307}
308
310{
311 actionList.clear();
312 currentAction = 0;
313}
314
315// namespace NAV::gui
316// {
317// namespace
318// {
319// void restoreAction(const json& /* target */)
320// {
321// // TODO: Compare against current config and only load the nodes/links which were changed
322// // json current;
323// // for (const auto& node : nm::m_Nodes())
324// // {
325// // current["nodes"]["node-" + std::to_string(size_t(node->id))] = *node;
326// // current["nodes"]["node-" + std::to_string(size_t(node->id))]["data"] = node->save();
327// // }
328// // for (const auto& link : nm::m_Links())
329// // {
330// // current["links"]["link-" + std::to_string(size_t(link.id))] = link;
331// // }
332
333// NAV::flow::saveLastActions = false;
334// nm::DeleteAllNodes();
335
336// NAV::flow::LoadJson(target);
337// if (!target["unsavedChanges"].get<bool>())
338// {
339// NAV::flow::DiscardChanges();
340// }
341// else
342// {
343// NAV::flow::ApplyChanges();
344// }
345
346// NAV::flow::loadingFrameCount = ImGui::GetFrameCount();
347// NAV::flow::saveLastActions = true;
348
349// nm::InitializeAllNodesAsync();
350// }
351// } // namespace
352// } // namespace NAV::gui
353
355{
356 // LOG_DEBUG("Undoing last action");
357
358 // restoreAction(actionList.at(--currentAction));
359}
360
362{
363 // LOG_DEBUG("Redoing last action");
364
365 // restoreAction(actionList.at(++currentAction));
366}
367
369{
370 // LOG_DEBUG("Saving last action to action list");
371
372 // // TODO: Check if event was triggered by a slider and discard the save, because it triggers it every step
373
374 // if (actionList.size() > ACTION_LIST_MAX_SIZE) // List is full
375 // {
376 // LOG_TRACE("Action list full, therefore discarding first element.");
377 // actionList.pop_front();
378 // if (currentAction)
379 // {
380 // currentAction--;
381 // }
382 // }
383 // while (currentAction + 1 < actionList.size())
384 // {
385 // LOG_TRACE("Discarding element which is past the current action");
386 // actionList.pop_back();
387 // }
388
389 // json j;
390 // for (const auto* node : nm::m_Nodes())
391 // {
392 // j["nodes"]["node-" + std::to_string(size_t(node->id))] = *node;
393 // j["nodes"]["node-" + std::to_string(size_t(node->id))]["data"] = node->save();
394
395 // for (const auto& outputPin : node->outputPins)
396 // {
397 // for (const auto& link : outputPin.links)
398 // {
399 // auto& j = clipboard["links"]["link-" + std::to_string(size_t(link.linkId))];
400 // j["id"] = size_t(link.linkId);
401 // j["startPinId"] = size_t(outputPin.id);
402 // j["endPinId"] = size_t(link.connectedPinId);
403 // }
404 // }
405 // }
406
407 // j["unsavedChanges"] = NAV::flow::HasUnsavedChanges();
408
409 // if (!actionList.empty())
410 // {
411 // currentAction++;
412 // }
413 // actionList.push_back(j);
414}
Config management for the Project.
Save/Load the Nodes.
nlohmann::json json
json namespace
Global Gui Actions.
Defines how to save certain datatypes to json.
#define LOG_DEBUG
Debug information. Should not be called on functions which receive observations (spamming)
Definition Logger.hpp:67
Manages all Nodes.
Abstract parent class for all nodes.
Definition Node.hpp:92
std::vector< OutputPin > outputPins
List of output pins.
Definition Node.hpp:399
virtual json save() const
Saves the node into a json object.
Definition Node.cpp:60
ax::NodeEditor::NodeId id
Unique Id of the Node.
Definition Node.hpp:391
static float leftPaneWidth
Width of the left pane.
static constexpr float SPLITTER_THICKNESS
Thickness of the splitter between left and right pane.
static float menuBarHeight
Height of the menu bar on top.
bool DeleteNode(ax::NodeEditor::NodeId nodeId)
Delete the node provided by id.
const std::vector< Node * > & m_Nodes()
List of all registered Nodes.
Node * FindNode(ax::NodeEditor::NodeId id)
Finds the Node for the NodeId.
bool saveLastActions
Whether actions should be saved to the last actions list.
bool LoadJson(const json &j, bool requestNewIds=false)
Loads the nodes and links from the specified json object.
int loadingFrameCount
Frame Count when changes were loaded to prevent nodes moving from triggering unsaved changes.
bool canPasteFlowElements()
Checks if elements can be pasted.
void copyFlowElements()
Copies the currently selected elements.
bool canUndoLastAction()
Checks if an action can be undone.
bool canCutOrCopyFlowElements()
Checks if elements can be cutted/copied.
bool canRedoLastAction()
Checks if an action can be redone.
void pasteFlowElements()
Pastes the copied/cutted elements.
void clearLastActionList()
Clears the list of last actions.
void cutFlowElements()
Cuts the currently selected elements.
void redoLastAction()
Redo the last action.
void undoLastAction()
Undo the last action.
void saveLastAction()
Saves the last action to the action list.
Kind of the Pin (Input/Output)
Definition Pin.hpp:165
@ Input
Input Pin.
Definition Pin.hpp:171
@ Output
Output Pin.
Definition Pin.hpp:170
@ None
None.
Definition Pin.hpp:169