INSTINCT Code Coverage Report


Directory: src/
File: internal/Node/Pin.cpp
Date: 2025-11-25 23:34:18
Exec Total Coverage
Lines: 135 229 59.0%
Functions: 27 35 77.1%
Branches: 166 345 48.1%

Line Branch Exec Source
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 "internal/Node/Pin.hpp"
10
11 #include "internal/Node/Node.hpp"
12 #include "internal/gui/widgets/PinIcon.hpp"
13 #include "internal/gui/NodeEditorApplication.hpp"
14 #include "internal/FlowManager.hpp"
15
16 #include "util/Assert.h"
17
18 #include "NodeRegistry.hpp"
19
20 #include <imgui_node_editor.h>
21 #include <algorithm>
22
23 // ###########################################################################################################
24 // Pin
25 // ###########################################################################################################
26
27 296 bool NAV::Pin::canCreateLink(const OutputPin& startPin, const InputPin& endPin)
28 {
29 296 bool dataTypesMatch = true;
30
31 296 if (startPin.type == Pin::Type::Flow
32
7/8
✓ Branch 0 taken 274 times.
✓ Branch 1 taken 22 times.
✓ Branch 3 taken 274 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 272 times.
✓ Branch 7 taken 2 times.
✓ Branch 8 taken 294 times.
296 && !NAV::NodeRegistry::NodeDataTypeAnyIsChildOf(startPin.dataIdentifier, endPin.dataIdentifier))
33 {
34 2 dataTypesMatch = false;
35 }
36
37 if (startPin.type == Pin::Type::Delegate
38
3/4
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 295 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
297 && (startPin.parentNode == nullptr
39
6/12
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 times.
✓ Branch 10 taken 1 times.
✓ Branch 11 taken 295 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 296 times.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
297 || std::ranges::find(endPin.dataIdentifier, startPin.parentNode->type()) == endPin.dataIdentifier.end()))
40 {
41 dataTypesMatch = false;
42 }
43
44
2/2
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 281 times.
578 if ((startPin.type == Pin::Type::Object || startPin.type == Pin::Type::Matrix) // NOLINT(misc-redundant-expression) - false positive warning
45
5/8
✓ Branch 0 taken 282 times.
✓ Branch 1 taken 14 times.
✓ Branch 3 taken 15 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 15 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 296 times.
578 && !dataIdentifierHaveCommon(startPin.dataIdentifier, endPin.dataIdentifier))
46 {
47 dataTypesMatch = false;
48 }
49
50 296 return startPin.id != endPin.id // Different Pins
51
1/2
✓ Branch 1 taken 296 times.
✗ Branch 2 not taken.
296 && startPin.kind != endPin.kind // Input <=> Output
52
1/2
✓ Branch 1 taken 296 times.
✗ Branch 2 not taken.
296 && startPin.type == endPin.type // Same Type (Flow, Object, ...)
53
1/2
✓ Branch 0 taken 296 times.
✗ Branch 1 not taken.
296 && startPin.parentNode != endPin.parentNode // Different Nodes
54
3/4
✓ Branch 0 taken 296 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 294 times.
✓ Branch 3 taken 2 times.
592 && dataTypesMatch; // Data identifier match
55 }
56
57 15 bool NAV::Pin::dataIdentifierHaveCommon(const std::vector<std::string>& a, const std::vector<std::string>& b)
58 {
59
1/2
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
30 return !a.empty() && !b.empty()
60
4/8
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✓ Branch 4 taken 15 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 15 times.
✗ Branch 8 not taken.
✓ Branch 11 taken 15 times.
✗ Branch 12 not taken.
45 && std::ranges::find_if(a, [&b](const std::string& str) { return std::ranges::find(b, str) != b.end(); }) != a.end();
61 }
62
63 ImColor NAV::Pin::getIconColor() const
64 {
65 switch (Type::Value(type))
66 {
67 case Type::None:
68 return { 0, 0, 0 };
69 case Type::Flow:
70 if (ax::NodeEditor::GetStyle().Colors[ax::NodeEditor::StyleColor_NodeBg].x
71 + ax::NodeEditor::GetStyle().Colors[ax::NodeEditor::StyleColor_NodeBg].y
72 + ax::NodeEditor::GetStyle().Colors[ax::NodeEditor::StyleColor_NodeBg].z
73 > 2.0F)
74 {
75 return { 0, 0, 0 };
76 }
77 return { 255, 255, 255 };
78 case Type::Bool:
79 return { 220, 48, 48 };
80 case Type::Int:
81 return { 68, 201, 156 };
82 case Type::Float:
83 return { 147, 226, 74 };
84 case Type::String:
85 return { 124, 21, 153 };
86 case Type::Object:
87 return { 51, 150, 215 };
88 case Type::Matrix:
89 return { 255, 165, 0 };
90 case Type::Delegate:
91 return { 255, 48, 48 };
92 }
93 return { 0, 0, 0 };
94 }
95
96 void NAV::Pin::drawPinIcon(bool connected, int alpha) const
97 {
98 namespace PinIcon = gui::widgets::PinIcon;
99
100 PinIcon::Type iconType = PinIcon::Type::Flow;
101 ImColor color = getIconColor();
102 color.Value.w = static_cast<float>(alpha) / 255.0F;
103 switch (Type::Value(type))
104 {
105 case Type::None:
106 iconType = PinIcon::Type::Grid;
107 break;
108 case Type::Flow:
109 iconType = PinIcon::Type::Flow;
110 break;
111 case Type::Bool:
112 // iconType = PinIcon::Type::Circle;
113 // break;
114 case Type::Int:
115 // iconType = PinIcon::Type::Circle;
116 // break;
117 case Type::Float:
118 iconType = PinIcon::Type::Circle;
119 break;
120 case Type::String:
121 iconType = PinIcon::Type::RoundSquare;
122 break;
123 case Type::Object:
124 // iconType = PinIcon::Type::Diamond;
125 // break;
126 case Type::Matrix:
127 iconType = PinIcon::Type::Diamond;
128 break;
129 case Type::Delegate:
130 iconType = PinIcon::Type::Square;
131 break;
132 default:
133 return;
134 }
135
136 gui::widgets::PinIcon::Draw(ImVec2(static_cast<float>(m_PinIconSize) * gui::NodeEditorApplication::defaultFontRatio(),
137 static_cast<float>(m_PinIconSize) * gui::NodeEditorApplication::defaultFontRatio()),
138 iconType, connected, color, ImColor(32, 32, 32, alpha));
139 }
140
141 583 bool NAV::Pin::createLink(OutputPin& startPin, InputPin& endPin, ax::NodeEditor::LinkId linkId)
142 {
143
2/2
✓ Branch 1 taken 288 times.
✓ Branch 2 taken 295 times.
583 if (startPin.isPinLinked(endPin)) { return true; } // Pins are already linked
144
145
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 293 times.
295 if (!startPin.canCreateLink(endPin)) { return false; } // Types do not match
146
147
2/4
✓ Branch 0 taken 293 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 293 times.
293 if (!startPin.parentNode || !endPin.parentNode) { return false; }
148 LOG_TRACE("called: {} of [{}] ==> {} of [{}]", size_t(startPin.id), startPin.parentNode->nameId(), size_t(endPin.id), endPin.parentNode->nameId());
149
150
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 293 times.
293 if (!startPin.parentNode->onCreateLink(startPin, endPin))
151 {
152 LOG_ERROR("The new Link between node '{}' and '{}' was refused by its start node.",
153 startPin.parentNode->nameId(), endPin.parentNode->nameId());
154 return false;
155 }
156
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 293 times.
293 if (!endPin.parentNode->onCreateLink(startPin, endPin))
157 {
158 LOG_ERROR("The new Link between node '{}' and '{}' was refused by its end node.",
159 startPin.parentNode->nameId(), endPin.parentNode->nameId());
160 return false;
161 }
162
163
5/10
✓ Branch 2 taken 293 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 293 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 293 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 293 times.
✗ Branch 12 not taken.
✓ Branch 15 taken 293 times.
✗ Branch 16 not taken.
293 LOG_DEBUG("Creating link from pin {} of [{}] ==> {} of [{}]",
164 size_t(startPin.id), startPin.parentNode->nameId(),
165 size_t(endPin.id), endPin.parentNode->nameId());
166
167 293 startPin.connect(endPin, linkId);
168 293 endPin.connect(startPin, linkId);
169
170 293 flow::AddLink(linkId);
171
172
2/2
✓ Branch 1 taken 22 times.
✓ Branch 2 taken 271 times.
293 if (endPin.type != Pin::Type::Flow)
173 {
174
4/8
✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 22 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 22 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 22 times.
✗ Branch 8 not taken.
22 if (startPin.parentNode && endPin.parentNode && !startPin.parentNode->isInitialized())
175 {
176
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 22 times.
22 if (endPin.parentNode->isInitialized())
177 {
178 endPin.parentNode->doDeinitialize(true);
179 }
180 }
181 }
182
183
2/4
✓ Branch 0 taken 293 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 293 times.
✗ Branch 3 not taken.
293 if (startPin.parentNode && endPin.parentNode)
184 {
185 293 startPin.parentNode->afterCreateLink(startPin, endPin);
186 293 endPin.parentNode->afterCreateLink(startPin, endPin);
187 }
188
189 293 flow::ApplyChanges();
190
191 293 return true;
192 }
193
194 3 bool NAV::Pin::recreateLink(OutputPin& startPin, InputPin& endPin)
195 {
196
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 if (startPin.isPinLinked(endPin))
197 {
198 3 deleteLink(startPin, endPin);
199
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 return createLink(startPin, endPin);
200 }
201 return false;
202 }
203
204 292 void NAV::Pin::deleteLink(OutputPin& startPin, InputPin& endPin)
205 {
206
2/4
✓ Branch 0 taken 292 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 292 times.
292 if (!startPin.parentNode || !endPin.parentNode) { return; }
207
208
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 292 times.
292 if (!startPin.isPinLinked(endPin))
209 {
210 LOG_ERROR("Cannot delete the link, because the nodes '{}' and '{}' are not linked over pins '{}' => '{}'.",
211 startPin.parentNode->nameId(), endPin.parentNode->nameId(),
212 size_t(startPin.id), size_t(endPin.id));
213 return;
214 }
215
216
6/12
✓ Branch 2 taken 292 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 292 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 292 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 292 times.
✗ Branch 12 not taken.
✓ Branch 14 taken 292 times.
✗ Branch 15 not taken.
✓ Branch 18 taken 292 times.
✗ Branch 19 not taken.
292 LOG_DEBUG("Deleting link {} from pin {} of [{}] ==> pin {} of [{}]", size_t(endPin.link.linkId),
217 size_t(startPin.id), startPin.parentNode->nameId(), size_t(endPin.id), endPin.parentNode->nameId());
218
219 292 startPin.parentNode->onDeleteLink(startPin, endPin);
220 292 endPin.parentNode->onDeleteLink(startPin, endPin);
221
222
2/2
✓ Branch 1 taken 22 times.
✓ Branch 2 taken 270 times.
292 if (endPin.type != Pin::Type::Flow) { endPin.parentNode->doDeinitialize(true); }
223
224 292 startPin.disconnect(endPin);
225 292 endPin.disconnect();
226
227 292 startPin.parentNode->afterDeleteLink(startPin, endPin);
228 292 endPin.parentNode->afterDeleteLink(startPin, endPin);
229
230 292 flow::ApplyChanges();
231 }
232
233 // ###########################################################################################################
234 // OutputPin
235 // ###########################################################################################################
236
237 296 bool NAV::OutputPin::canCreateLink(const NAV::InputPin& other) const
238 {
239 296 return Pin::canCreateLink(*this, other);
240 }
241
242 614 bool NAV::OutputPin::isPinLinked() const
243 {
244 614 return !links.empty();
245 }
246
247 878 bool NAV::OutputPin::isPinLinked(const NAV::InputPin& endPin) const
248 {
249
1/2
✓ Branch 1 taken 878 times.
✗ Branch 2 not taken.
878 auto iter = std::ranges::find_if(links, [&endPin](const OutgoingLink& link) {
250
4/4
✓ Branch 0 taken 631 times.
✓ Branch 1 taken 150 times.
✓ Branch 3 taken 583 times.
✓ Branch 4 taken 48 times.
781 return link.connectedNode == endPin.parentNode && link.connectedPinId == endPin.id;
251 });
252 878 return iter != links.cend();
253 }
254
255 580 bool NAV::OutputPin::createLink(InputPin& endPin, ax::NodeEditor::LinkId linkId)
256 {
257 580 return Pin::createLink(*this, endPin, linkId);
258 }
259
260 3 bool NAV::OutputPin::recreateLink(InputPin& endPin)
261 {
262 3 return Pin::recreateLink(*this, endPin);
263 }
264
265 void NAV::OutputPin::deleteLink(InputPin& endPin)
266 {
267 Pin::deleteLink(*this, endPin);
268 }
269
270 82 void NAV::OutputPin::deleteLinks()
271 {
272
2/2
✓ Branch 1 taken 101 times.
✓ Branch 2 taken 82 times.
183 while (!links.empty())
273 {
274
1/2
✓ Branch 2 taken 101 times.
✗ Branch 3 not taken.
101 if (auto* endPin = links.back().getConnectedPin())
275 {
276 101 Pin::deleteLink(*this, *endPin);
277 }
278 else
279 {
280 links.pop_back();
281 }
282 }
283 82 }
284
285 293 void NAV::OutputPin::connect(NAV::InputPin& endPin, ax::NodeEditor::LinkId linkId)
286 {
287
1/2
✓ Branch 1 taken 293 times.
✗ Branch 2 not taken.
293 auto iter = std::ranges::find_if(links, [&endPin](const OutgoingLink& link) {
288
3/4
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 63 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 16 times.
79 return link.connectedNode == endPin.parentNode && link.connectedPinId == endPin.id;
289 });
290
1/2
✓ Branch 2 taken 293 times.
✗ Branch 3 not taken.
293 if (iter == links.end()) // Link does not yet exist
291 {
292
3/4
✓ Branch 1 taken 293 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 290 times.
✓ Branch 4 taken 3 times.
293 if (linkId)
293 {
294 // LinkId is given
295
1/2
✓ Branch 1 taken 290 times.
✗ Branch 2 not taken.
290 links.emplace_back(linkId, endPin.parentNode, endPin.id);
296
2/4
✓ Branch 1 taken 290 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 290 times.
✗ Branch 4 not taken.
290 if (endPin.link.linkId != linkId)
297 {
298
1/2
✓ Branch 1 taken 290 times.
✗ Branch 2 not taken.
290 endPin.disconnect();
299
1/2
✓ Branch 1 taken 290 times.
✗ Branch 2 not taken.
290 endPin.connect(*this, linkId);
300 }
301 }
302
2/8
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 3 times.
3 else if (endPin.link.connectedNode == parentNode && endPin.link.connectedPinId == id)
303 {
304 // Connected pin is already linked, so get linkId from the connected pin
305 links.emplace_back(endPin.link.linkId, endPin.parentNode, endPin.id);
306 }
307 else
308 {
309 // Connected pin is not linked, so get new linkId
310
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 links.emplace_back(flow::GetNextLinkId(), endPin.parentNode, endPin.id);
311 // Also connect the endPin to this one
312
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 endPin.connect(*this);
313 }
314 }
315 293 }
316
317 292 void NAV::OutputPin::disconnect(InputPin& endPin)
318 {
319
1/2
✓ Branch 1 taken 292 times.
✗ Branch 2 not taken.
292 auto iter = std::ranges::find_if(links, [&endPin](const OutgoingLink& link) {
320
4/4
✓ Branch 0 taken 308 times.
✓ Branch 1 taken 25 times.
✓ Branch 3 taken 292 times.
✓ Branch 4 taken 16 times.
333 return link.connectedNode == endPin.parentNode && link.connectedPinId == endPin.id;
321 });
322
1/2
✓ Branch 2 taken 292 times.
✗ Branch 3 not taken.
292 if (iter != links.end())
323 {
324
1/2
✓ Branch 2 taken 292 times.
✗ Branch 3 not taken.
292 links.erase(iter);
325
4/8
✓ Branch 0 taken 292 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 292 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 292 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 292 times.
✗ Branch 8 not taken.
292 if (endPin.link.connectedNode == parentNode && endPin.link.connectedPinId == id)
326 {
327
1/2
✓ Branch 1 taken 292 times.
✗ Branch 2 not taken.
292 endPin.disconnect();
328 }
329 }
330 292 }
331
332 556284 NAV::InputPin* NAV::OutputPin::OutgoingLink::getConnectedPin() const
333 {
334
1/2
✓ Branch 0 taken 556496 times.
✗ Branch 1 not taken.
556284 if (connectedNode)
335 {
336
1/2
✓ Branch 5 taken 1098892 times.
✗ Branch 6 not taken.
1099112 for (auto& inputPin : connectedNode->inputPins)
337 {
338
3/4
✓ Branch 1 taken 1099204 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 556588 times.
✓ Branch 4 taken 542616 times.
1098838 if (inputPin.id == connectedPinId) { return &inputPin; }
339 }
340 }
341 return nullptr;
342 }
343
344 // ###########################################################################################################
345 // InputPin
346 // ###########################################################################################################
347
348 bool NAV::InputPin::canCreateLink(const OutputPin& other) const
349 {
350 return Pin::canCreateLink(other, *this);
351 }
352
353 667050 bool NAV::InputPin::isPinLinked() const
354 {
355
4/6
✓ Branch 1 taken 667302 times.
✓ Branch 2 taken 128 times.
✓ Branch 3 taken 667322 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 666720 times.
✗ Branch 7 not taken.
667050 return link.linkId && link.connectedNode && link.connectedPinId;
356 }
357
358 bool NAV::InputPin::createLink(OutputPin& startPin, ax::NodeEditor::LinkId linkId)
359 {
360 return Pin::createLink(startPin, *this, linkId);
361 }
362
363 bool NAV::InputPin::recreateLink(OutputPin& startPin)
364 {
365 return Pin::recreateLink(startPin, *this);
366 }
367
368 191 void NAV::InputPin::deleteLink()
369 {
370
2/2
✓ Branch 1 taken 188 times.
✓ Branch 2 taken 3 times.
191 if (auto* startPin = link.getConnectedPin())
371 {
372 188 Pin::deleteLink(*startPin, *this);
373 }
374 191 }
375
376 586 void NAV::InputPin::connect(NAV::OutputPin& startPin, ax::NodeEditor::LinkId linkId)
377 {
378
5/6
✓ Branch 0 taken 293 times.
✓ Branch 1 taken 293 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 293 times.
✓ Branch 5 taken 293 times.
✓ Branch 6 taken 293 times.
586 if (link.connectedNode != startPin.parentNode || link.connectedPinId != startPin.id) // Link does not yet exist
379 {
380 293 link.connectedNode = startPin.parentNode;
381 293 link.connectedPinId = startPin.id;
382
383
1/2
✓ Branch 1 taken 293 times.
✗ Branch 2 not taken.
293 auto iter = std::ranges::find_if(startPin.links, [&, this](const OutputPin::OutgoingLink& link) {
384
4/4
✓ Branch 0 taken 309 times.
✓ Branch 1 taken 63 times.
✓ Branch 3 taken 293 times.
✓ Branch 4 taken 16 times.
372 return link.connectedNode == parentNode && link.connectedPinId == id;
385 });
386
387
3/4
✓ Branch 1 taken 293 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 290 times.
✓ Branch 4 taken 3 times.
293 if (linkId)
388 {
389 // LinkId is given
390 290 link.linkId = linkId;
391
4/8
✓ Branch 2 taken 290 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 290 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 290 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 290 times.
290 if (iter != startPin.links.end() && iter->linkId != linkId)
392 {
393 startPin.disconnect(*this);
394 startPin.connect(*this, linkId);
395 }
396 }
397
1/2
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 else if (iter != startPin.links.end())
398 {
399 // Connected pin is already linked, so get linkId from the connected pin
400 3 link.linkId = iter->linkId;
401 }
402 else
403 {
404 // Connected pin is not linked, so get new linkId
405 link.linkId = flow::GetNextLinkId();
406 // Also connect the startPin to this one
407 startPin.connect(*this);
408 }
409 }
410 586 }
411
412 874 void NAV::InputPin::disconnect()
413 {
414
6/8
✓ Branch 0 taken 582 times.
✓ Branch 1 taken 292 times.
✓ Branch 3 taken 582 times.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 582 times.
✓ Branch 8 taken 292 times.
✓ Branch 9 taken 582 times.
874 if (link.connectedNode || link.connectedPinId || link.linkId)
415 {
416 292 auto* startPin = link.getConnectedPin();
417
418
1/2
✓ Branch 1 taken 292 times.
✗ Branch 2 not taken.
292 link.linkId = 0;
419 292 link.connectedNode = nullptr;
420
1/2
✓ Branch 1 taken 292 times.
✗ Branch 2 not taken.
292 link.connectedPinId = 0;
421
422
1/2
✓ Branch 0 taken 292 times.
✗ Branch 1 not taken.
292 if (startPin)
423 {
424
1/2
✓ Branch 1 taken 292 times.
✗ Branch 2 not taken.
292 auto iter = std::ranges::find_if(startPin->links, [&, this](const OutputPin::OutgoingLink& link) {
425
3/4
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 63 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 16 times.
79 return link.connectedNode == parentNode && link.connectedPinId == id;
426 });
427
428
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 292 times.
292 if (iter != startPin->links.end())
429 {
430 startPin->disconnect(*this);
431 }
432 }
433 }
434 874 }
435
436 1661921 NAV::OutputPin* NAV::InputPin::IncomingLink::getConnectedPin() const
437 {
438
2/2
✓ Branch 0 taken 1661729 times.
✓ Branch 1 taken 192 times.
1661921 if (connectedNode)
439 {
440
1/2
✓ Branch 5 taken 2224758 times.
✗ Branch 6 not taken.
2225059 for (auto& outputPin : connectedNode->outputPins)
441 {
442
3/4
✓ Branch 1 taken 2224875 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1661545 times.
✓ Branch 4 taken 563330 times.
2224662 if (outputPin.id == connectedPinId) { return &outputPin; }
443 }
444 }
445 192 return nullptr;
446 }
447
448 // ###########################################################################################################
449
450 void NAV::to_json(json& j, const OutputPin& pin)
451 {
452 j = json{
453 { "id", size_t(pin.id) },
454 { "name", pin.name },
455 };
456 }
457 1155 void NAV::from_json(const json& j, OutputPin& pin)
458 {
459
3/6
✓ Branch 1 taken 1155 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1155 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1155 times.
✗ Branch 8 not taken.
1155 pin.id = j.at("id").get<size_t>();
460
1/2
✓ Branch 1 taken 1155 times.
✗ Branch 2 not taken.
1155 if (j.contains("name")) { j.at("name").get_to(pin.name); }
461 1155 }
462
463 void NAV::to_json(json& j, const InputPin& pin)
464 {
465 j = json{
466 { "id", size_t(pin.id) },
467 { "name", pin.name },
468 };
469 }
470 1120 void NAV::from_json(const json& j, InputPin& pin)
471 {
472
3/6
✓ Branch 1 taken 1120 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1120 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1120 times.
✗ Branch 8 not taken.
1120 pin.id = j.at("id").get<size_t>();
473
1/2
✓ Branch 1 taken 1120 times.
✗ Branch 2 not taken.
1120 if (j.contains("name")) { j.at("name").get_to(pin.name); }
474 1120 }
475