Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/scene/gui/graph_edit_arranger.cpp
9903 views
1
/**************************************************************************/
2
/* graph_edit_arranger.cpp */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10
/* */
11
/* Permission is hereby granted, free of charge, to any person obtaining */
12
/* a copy of this software and associated documentation files (the */
13
/* "Software"), to deal in the Software without restriction, including */
14
/* without limitation the rights to use, copy, modify, merge, publish, */
15
/* distribute, sublicense, and/or sell copies of the Software, and to */
16
/* permit persons to whom the Software is furnished to do so, subject to */
17
/* the following conditions: */
18
/* */
19
/* The above copyright notice and this permission notice shall be */
20
/* included in all copies or substantial portions of the Software. */
21
/* */
22
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29
/**************************************************************************/
30
31
#include "graph_edit_arranger.h"
32
33
#include "scene/gui/graph_edit.h"
34
35
void GraphEditArranger::arrange_nodes() {
36
ERR_FAIL_NULL(graph_edit);
37
38
if (!arranging_graph) {
39
arranging_graph = true;
40
} else {
41
return;
42
}
43
44
Dictionary node_names;
45
HashSet<StringName> selected_nodes;
46
47
bool arrange_entire_graph = true;
48
for (int i = graph_edit->get_child_count() - 1; i >= 0; i--) {
49
GraphNode *graph_element = Object::cast_to<GraphNode>(graph_edit->get_child(i));
50
if (!graph_element) {
51
continue;
52
}
53
54
node_names[graph_element->get_name()] = graph_element;
55
56
if (graph_element->is_selected()) {
57
arrange_entire_graph = false;
58
}
59
}
60
61
HashMap<StringName, HashSet<StringName>> upper_neighbours;
62
HashMap<StringName, Pair<int, int>> port_info;
63
Vector2 origin(FLT_MAX, FLT_MAX);
64
65
float gap_v = 100.0f;
66
float gap_h = 100.0f;
67
68
const Vector<Ref<GraphEdit::Connection>> connection_list = graph_edit->get_connections();
69
70
for (int i = graph_edit->get_child_count() - 1; i >= 0; i--) {
71
GraphNode *graph_element = Object::cast_to<GraphNode>(graph_edit->get_child(i));
72
if (!graph_element) {
73
continue;
74
}
75
76
if (graph_element->is_selected() || arrange_entire_graph) {
77
selected_nodes.insert(graph_element->get_name());
78
HashSet<StringName> s;
79
80
for (const Ref<GraphEdit::Connection> &connection : connection_list) {
81
GraphNode *p_from = Object::cast_to<GraphNode>(node_names[connection->from_node]);
82
if (!p_from) {
83
continue;
84
}
85
if (connection->to_node == graph_element->get_name() && (p_from->is_selected() || arrange_entire_graph) && connection->to_node != connection->from_node) {
86
if (!s.has(p_from->get_name())) {
87
s.insert(p_from->get_name());
88
}
89
String s_connection = String(p_from->get_name()) + " " + String(connection->to_node);
90
StringName _connection(s_connection);
91
Pair<int, int> ports(connection->from_port, connection->to_port);
92
port_info.insert(_connection, ports);
93
}
94
}
95
upper_neighbours.insert(graph_element->get_name(), s);
96
}
97
}
98
99
if (!selected_nodes.size()) {
100
arranging_graph = false;
101
return;
102
}
103
104
HashMap<int, Vector<StringName>> layers = _layering(selected_nodes, upper_neighbours);
105
_crossing_minimisation(layers, upper_neighbours);
106
107
Dictionary root, align, sink, shift;
108
_horizontal_alignment(root, align, layers, upper_neighbours, selected_nodes);
109
110
HashMap<StringName, Vector2> new_positions;
111
Vector2 default_position(FLT_MAX, FLT_MAX);
112
Dictionary inner_shift;
113
HashSet<StringName> block_heads;
114
115
for (const StringName &E : selected_nodes) {
116
inner_shift[E] = 0.0f;
117
sink[E] = E;
118
shift[E] = FLT_MAX;
119
new_positions.insert(E, default_position);
120
if ((StringName)root[E] == E) {
121
block_heads.insert(E);
122
}
123
}
124
125
_calculate_inner_shifts(inner_shift, root, node_names, align, block_heads, port_info);
126
127
for (const StringName &E : block_heads) {
128
_place_block(E, gap_v, layers, root, align, node_names, inner_shift, sink, shift, new_positions);
129
}
130
origin.y = Object::cast_to<GraphNode>(node_names[layers[0][0]])->get_position_offset().y - (new_positions[layers[0][0]].y + (float)inner_shift[layers[0][0]]);
131
origin.x = Object::cast_to<GraphNode>(node_names[layers[0][0]])->get_position_offset().x;
132
133
for (const StringName &E : block_heads) {
134
StringName u = E;
135
float start_from = origin.y + new_positions[E].y;
136
do {
137
Vector2 cal_pos;
138
cal_pos.y = start_from + (real_t)inner_shift[u];
139
new_positions.insert(u, cal_pos);
140
u = align[u];
141
} while (u != E);
142
}
143
144
// Compute horizontal coordinates individually for layers to get uniform gap.
145
float start_from = origin.x;
146
float largest_node_size = 0.0f;
147
148
for (unsigned int i = 0; i < layers.size(); i++) {
149
Vector<StringName> layer = layers[i];
150
for (int j = 0; j < layer.size(); j++) {
151
float current_node_size = Object::cast_to<GraphNode>(node_names[layer[j]])->get_size().x;
152
largest_node_size = MAX(largest_node_size, current_node_size);
153
}
154
155
for (int j = 0; j < layer.size(); j++) {
156
float current_node_size = Object::cast_to<GraphNode>(node_names[layer[j]])->get_size().x;
157
Vector2 cal_pos = new_positions[layer[j]];
158
159
if (current_node_size == largest_node_size) {
160
cal_pos.x = start_from;
161
} else {
162
float current_node_start_pos = start_from;
163
if (current_node_size < largest_node_size / 2) {
164
if (!(i || j)) {
165
start_from -= (largest_node_size - current_node_size);
166
}
167
current_node_start_pos = start_from + largest_node_size - current_node_size;
168
}
169
cal_pos.x = current_node_start_pos;
170
}
171
new_positions.insert(layer[j], cal_pos);
172
}
173
174
start_from += largest_node_size + gap_h;
175
largest_node_size = 0.0f;
176
}
177
178
graph_edit->emit_signal(SNAME("begin_node_move"));
179
for (const StringName &E : selected_nodes) {
180
GraphNode *graph_node = Object::cast_to<GraphNode>(node_names[E]);
181
graph_node->set_drag(true);
182
Vector2 pos = (new_positions[E]);
183
184
if (graph_edit->is_snapping_enabled()) {
185
float snapping_distance = graph_edit->get_snapping_distance();
186
pos = pos.snappedf(snapping_distance);
187
}
188
graph_node->set_position_offset(pos);
189
graph_node->set_drag(false);
190
}
191
graph_edit->emit_signal(SNAME("end_node_move"));
192
arranging_graph = false;
193
}
194
195
int GraphEditArranger::_set_operations(SET_OPERATIONS p_operation, HashSet<StringName> &r_u, const HashSet<StringName> &r_v) {
196
switch (p_operation) {
197
case GraphEditArranger::IS_EQUAL: {
198
for (const StringName &E : r_u) {
199
if (!r_v.has(E)) {
200
return 0;
201
}
202
}
203
return r_u.size() == r_v.size();
204
} break;
205
case GraphEditArranger::IS_SUBSET: {
206
if (r_u.size() == r_v.size() && !r_u.size()) {
207
return 1;
208
}
209
for (const StringName &E : r_u) {
210
if (!r_v.has(E)) {
211
return 0;
212
}
213
}
214
return 1;
215
} break;
216
case GraphEditArranger::DIFFERENCE: {
217
Vector<StringName> common;
218
for (const StringName &E : r_u) {
219
if (r_v.has(E)) {
220
common.append(E);
221
}
222
}
223
for (const StringName &E : common) {
224
r_u.erase(E);
225
}
226
return r_u.size();
227
} break;
228
case GraphEditArranger::UNION: {
229
for (const StringName &E : r_v) {
230
if (!r_u.has(E)) {
231
r_u.insert(E);
232
}
233
}
234
return r_u.size();
235
} break;
236
default:
237
break;
238
}
239
return -1;
240
}
241
242
HashMap<int, Vector<StringName>> GraphEditArranger::_layering(const HashSet<StringName> &r_selected_nodes, const HashMap<StringName, HashSet<StringName>> &r_upper_neighbours) {
243
HashMap<int, Vector<StringName>> l;
244
245
HashSet<StringName> p = r_selected_nodes, q = r_selected_nodes, u, z;
246
int current_layer = 0;
247
bool selected = false;
248
249
while (!_set_operations(GraphEditArranger::IS_EQUAL, q, u)) {
250
_set_operations(GraphEditArranger::DIFFERENCE, p, u);
251
for (const StringName &E : p) {
252
HashSet<StringName> n = r_upper_neighbours[E];
253
if (_set_operations(GraphEditArranger::IS_SUBSET, n, z)) {
254
Vector<StringName> t;
255
t.push_back(E);
256
if (!l.has(current_layer)) {
257
l.insert(current_layer, Vector<StringName>{});
258
}
259
selected = true;
260
t.append_array(l[current_layer]);
261
l.insert(current_layer, t);
262
u.insert(E);
263
}
264
}
265
if (!selected) {
266
current_layer++;
267
uint32_t previous_size_z = z.size();
268
_set_operations(GraphEditArranger::UNION, z, u);
269
if (z.size() == previous_size_z) {
270
WARN_PRINT("Graph contains cycle(s). The cycle(s) will not be rearranged accurately.");
271
Vector<StringName> t;
272
if (l.has(0)) {
273
t.append_array(l[0]);
274
}
275
for (const StringName &E : p) {
276
t.push_back(E);
277
}
278
l.insert(0, t);
279
break;
280
}
281
}
282
selected = false;
283
}
284
285
return l;
286
}
287
288
Vector<StringName> GraphEditArranger::_split(const Vector<StringName> &r_layer, const HashMap<StringName, Dictionary> &r_crossings) {
289
if (!r_layer.size()) {
290
return Vector<StringName>();
291
}
292
293
const StringName &p = r_layer[Math::random(0, r_layer.size() - 1)];
294
Vector<StringName> left;
295
Vector<StringName> right;
296
297
for (int i = 0; i < r_layer.size(); i++) {
298
if (p != r_layer[i]) {
299
const StringName &q = r_layer[i];
300
int cross_pq = r_crossings[p][q];
301
int cross_qp = r_crossings[q][p];
302
if (cross_pq > cross_qp) {
303
left.push_back(q);
304
} else {
305
right.push_back(q);
306
}
307
}
308
}
309
310
left.push_back(p);
311
left.append_array(right);
312
return left;
313
}
314
315
void GraphEditArranger::_horizontal_alignment(Dictionary &r_root, Dictionary &r_align, const HashMap<int, Vector<StringName>> &r_layers, const HashMap<StringName, HashSet<StringName>> &r_upper_neighbours, const HashSet<StringName> &r_selected_nodes) {
316
for (const StringName &E : r_selected_nodes) {
317
r_root[E] = E;
318
r_align[E] = E;
319
}
320
321
if (r_layers.size() == 1) {
322
return;
323
}
324
325
for (unsigned int i = 1; i < r_layers.size(); i++) {
326
Vector<StringName> lower_layer = r_layers[i];
327
Vector<StringName> upper_layer = r_layers[i - 1];
328
int r = -1;
329
330
for (int j = 0; j < lower_layer.size(); j++) {
331
Vector<Pair<int, StringName>> up;
332
const StringName &current_node = lower_layer[j];
333
for (int k = 0; k < upper_layer.size(); k++) {
334
const StringName &adjacent_neighbour = upper_layer[k];
335
if (r_upper_neighbours[current_node].has(adjacent_neighbour)) {
336
up.push_back(Pair<int, StringName>(k, adjacent_neighbour));
337
}
338
}
339
340
int start = (up.size() - 1) / 2;
341
int end = (up.size() - 1) % 2 ? start + 1 : start;
342
for (int p = start; p <= end; p++) {
343
StringName Align = r_align[current_node];
344
if (Align == current_node && r < up[p].first) {
345
r_align[up[p].second] = lower_layer[j];
346
r_root[current_node] = r_root[up[p].second];
347
r_align[current_node] = r_root[up[p].second];
348
r = up[p].first;
349
}
350
}
351
}
352
}
353
}
354
355
void GraphEditArranger::_crossing_minimisation(HashMap<int, Vector<StringName>> &r_layers, const HashMap<StringName, HashSet<StringName>> &r_upper_neighbours) {
356
if (r_layers.size() == 1) {
357
return;
358
}
359
360
for (unsigned int i = 1; i < r_layers.size(); i++) {
361
Vector<StringName> upper_layer = r_layers[i - 1];
362
Vector<StringName> lower_layer = r_layers[i];
363
HashMap<StringName, Dictionary> c;
364
365
for (int j = 0; j < lower_layer.size(); j++) {
366
const StringName &p = lower_layer[j];
367
Dictionary d;
368
369
for (int k = 0; k < lower_layer.size(); k++) {
370
unsigned int crossings = 0;
371
const StringName &q = lower_layer[k];
372
373
if (j != k) {
374
for (int h = 1; h < upper_layer.size(); h++) {
375
if (r_upper_neighbours[p].has(upper_layer[h])) {
376
for (int g = 0; g < h; g++) {
377
if (r_upper_neighbours[q].has(upper_layer[g])) {
378
crossings++;
379
}
380
}
381
}
382
}
383
}
384
d[q] = crossings;
385
}
386
c.insert(p, d);
387
}
388
389
r_layers.insert(i, _split(lower_layer, c));
390
}
391
}
392
393
void GraphEditArranger::_calculate_inner_shifts(Dictionary &r_inner_shifts, const Dictionary &r_root, const Dictionary &r_node_names, const Dictionary &r_align, const HashSet<StringName> &r_block_heads, const HashMap<StringName, Pair<int, int>> &r_port_info) {
394
for (const StringName &E : r_block_heads) {
395
real_t left = 0;
396
StringName u = E;
397
StringName v = r_align[u];
398
while (u != v && (StringName)r_root[u] != v) {
399
String _connection = String(u) + " " + String(v);
400
401
GraphNode *gnode_from = Object::cast_to<GraphNode>(r_node_names[u]);
402
GraphNode *gnode_to = Object::cast_to<GraphNode>(r_node_names[v]);
403
404
Pair<int, int> ports = r_port_info[_connection];
405
int port_from = ports.first;
406
int port_to = ports.second;
407
408
Vector2 pos_from = gnode_from->get_output_port_position(port_from) * graph_edit->get_zoom();
409
Vector2 pos_to = gnode_to->get_input_port_position(port_to) * graph_edit->get_zoom();
410
411
real_t s = (real_t)r_inner_shifts[u] + (pos_from.y - pos_to.y) / graph_edit->get_zoom();
412
r_inner_shifts[v] = s;
413
left = MIN(left, s);
414
415
u = v;
416
v = (StringName)r_align[v];
417
}
418
419
u = E;
420
do {
421
r_inner_shifts[u] = (real_t)r_inner_shifts[u] - left;
422
u = (StringName)r_align[u];
423
} while (u != E);
424
}
425
}
426
427
float GraphEditArranger::_calculate_threshold(const StringName &p_v, const StringName &p_w, const Dictionary &r_node_names, const HashMap<int, Vector<StringName>> &r_layers, const Dictionary &r_root, const Dictionary &r_align, const Dictionary &r_inner_shift, real_t p_current_threshold, const HashMap<StringName, Vector2> &r_node_positions) {
428
#define MAX_ORDER 2147483647
429
#define ORDER(node, layers) \
430
for (unsigned int i = 0; i < layers.size(); i++) { \
431
int index = layers[i].find(node); \
432
if (index > 0) { \
433
order = index; \
434
break; \
435
} \
436
order = MAX_ORDER; \
437
}
438
439
int order = MAX_ORDER;
440
float threshold = p_current_threshold;
441
if (p_v == p_w) {
442
int min_order = MAX_ORDER;
443
Ref<GraphEdit::Connection> incoming;
444
const Vector<Ref<GraphEdit::Connection>> connection_list = graph_edit->get_connections();
445
for (const Ref<GraphEdit::Connection> &connection : connection_list) {
446
if (connection->to_node == p_w) {
447
ORDER(connection->from_node, r_layers);
448
if (min_order > order) {
449
min_order = order;
450
incoming = connection;
451
}
452
}
453
}
454
455
if (incoming.is_valid()) {
456
GraphNode *gnode_from = Object::cast_to<GraphNode>(r_node_names[incoming->from_node]);
457
GraphNode *gnode_to = Object::cast_to<GraphNode>(r_node_names[p_w]);
458
Vector2 pos_from = gnode_from->get_output_port_position(incoming->from_port) * graph_edit->get_zoom();
459
Vector2 pos_to = gnode_to->get_input_port_position(incoming->to_port) * graph_edit->get_zoom();
460
461
// If connected block node is selected, calculate threshold or add current block to list.
462
if (gnode_from->is_selected()) {
463
Vector2 connected_block_pos = r_node_positions[r_root[incoming->from_node]];
464
if (connected_block_pos.y != FLT_MAX) {
465
//Connected block is placed, calculate threshold.
466
threshold = connected_block_pos.y + (real_t)r_inner_shift[incoming->from_node] - (real_t)r_inner_shift[p_w] + pos_from.y - pos_to.y;
467
}
468
}
469
}
470
}
471
if (threshold == FLT_MIN && (StringName)r_align[p_w] == p_v) {
472
// This time, pick an outgoing edge and repeat as above!
473
int min_order = MAX_ORDER;
474
Ref<GraphEdit::Connection> outgoing;
475
const Vector<Ref<GraphEdit::Connection>> connection_list = graph_edit->get_connections();
476
for (const Ref<GraphEdit::Connection> &connection : connection_list) {
477
if (connection->from_node == p_w) {
478
ORDER(connection->to_node, r_layers);
479
if (min_order > order) {
480
min_order = order;
481
outgoing = connection;
482
}
483
}
484
}
485
486
if (outgoing.is_valid()) {
487
GraphNode *gnode_from = Object::cast_to<GraphNode>(r_node_names[p_w]);
488
GraphNode *gnode_to = Object::cast_to<GraphNode>(r_node_names[outgoing->to_node]);
489
Vector2 pos_from = gnode_from->get_output_port_position(outgoing->from_port) * graph_edit->get_zoom();
490
Vector2 pos_to = gnode_to->get_input_port_position(outgoing->to_port) * graph_edit->get_zoom();
491
492
// If connected block node is selected, calculate threshold or add current block to list.
493
if (gnode_to->is_selected()) {
494
Vector2 connected_block_pos = r_node_positions[r_root[outgoing->to_node]];
495
if (connected_block_pos.y != FLT_MAX) {
496
//Connected block is placed. Calculate threshold
497
threshold = connected_block_pos.y + (real_t)r_inner_shift[outgoing->to_node] - (real_t)r_inner_shift[p_w] + pos_from.y - pos_to.y;
498
}
499
}
500
}
501
}
502
#undef MAX_ORDER
503
#undef ORDER
504
return threshold;
505
}
506
507
void GraphEditArranger::_place_block(const StringName &p_v, float p_delta, const HashMap<int, Vector<StringName>> &r_layers, const Dictionary &r_root, const Dictionary &r_align, const Dictionary &r_node_name, const Dictionary &r_inner_shift, Dictionary &r_sink, Dictionary &r_shift, HashMap<StringName, Vector2> &r_node_positions) {
508
#define PRED(node, layers) \
509
for (unsigned int i = 0; i < layers.size(); i++) { \
510
int index = layers[i].find(node); \
511
if (index > 0) { \
512
predecessor = layers[i][index - 1]; \
513
break; \
514
} \
515
predecessor = StringName(); \
516
}
517
518
StringName predecessor;
519
StringName successor;
520
Vector2 pos = r_node_positions[p_v];
521
522
if (pos.y == FLT_MAX) {
523
pos.y = 0;
524
bool initial = false;
525
StringName w = p_v;
526
real_t threshold = FLT_MIN;
527
do {
528
PRED(w, r_layers);
529
if (predecessor != StringName()) {
530
StringName u = r_root[predecessor];
531
_place_block(u, p_delta, r_layers, r_root, r_align, r_node_name, r_inner_shift, r_sink, r_shift, r_node_positions);
532
threshold = _calculate_threshold(p_v, w, r_node_name, r_layers, r_root, r_align, r_inner_shift, threshold, r_node_positions);
533
if ((StringName)r_sink[p_v] == p_v) {
534
r_sink[p_v] = r_sink[u];
535
}
536
537
Vector2 predecessor_root_pos = r_node_positions[u];
538
Vector2 predecessor_node_size = Object::cast_to<GraphNode>(r_node_name[predecessor])->get_size();
539
if (r_sink[p_v] != r_sink[u]) {
540
real_t sc = pos.y + (real_t)r_inner_shift[w] - predecessor_root_pos.y - (real_t)r_inner_shift[predecessor] - predecessor_node_size.y - p_delta;
541
r_shift[r_sink[u]] = MIN(sc, (real_t)r_shift[r_sink[u]]);
542
} else {
543
real_t sb = predecessor_root_pos.y + (real_t)r_inner_shift[predecessor] + predecessor_node_size.y - (real_t)r_inner_shift[w] + p_delta;
544
sb = MAX(sb, threshold);
545
if (initial) {
546
pos.y = sb;
547
} else {
548
pos.y = MAX(pos.y, sb);
549
}
550
initial = false;
551
}
552
}
553
threshold = _calculate_threshold(p_v, w, r_node_name, r_layers, r_root, r_align, r_inner_shift, threshold, r_node_positions);
554
w = r_align[w];
555
} while (w != p_v);
556
r_node_positions.insert(p_v, pos);
557
}
558
559
#undef PRED
560
}
561
562