mirror of
https://github.com/node-red/node-red.git
synced 2023-10-10 13:36:53 +02:00
Add initial core:layout-flow action
This commit is contained in:
parent
17d3a5840d
commit
0281a9c3d0
@ -108,6 +108,168 @@ RED.view.tools = (function() {
|
||||
}
|
||||
}
|
||||
|
||||
function layoutFlow() {
|
||||
|
||||
var selection = RED.view.selection();
|
||||
if (!selection.nodes || selection.nodes.length !== 1) {
|
||||
RED.notify("Select exactly one node");
|
||||
return;
|
||||
}
|
||||
var ns = RED.nodes.getAllFlowNodes(selection.nodes[0]);
|
||||
|
||||
// Find Input node
|
||||
|
||||
var nodes = {};
|
||||
var minRank = 0;
|
||||
var stack = [];
|
||||
var candidateInputs = {};
|
||||
var candidateOutputs = {};
|
||||
ns.forEach(function(n) {
|
||||
candidateInputs[n.id] = n;
|
||||
candidateOutputs[n.id] = n;
|
||||
nodes[n.id] = {
|
||||
n:n,
|
||||
i:[],
|
||||
o:[],
|
||||
d:-1, // depth from start
|
||||
r:-1, // rank order at that depth
|
||||
downstream: 0
|
||||
}
|
||||
});
|
||||
RED.nodes.eachLink(function(link) {
|
||||
if (nodes[link.source.id] || nodes[link.target.id]) {
|
||||
nodes[link.source.id].o.push(link.target.id);
|
||||
nodes[link.target.id].i.push(link.source.id);
|
||||
delete candidateInputs[link.target.id]
|
||||
delete candidateOutputs[link.source.id]
|
||||
}
|
||||
})
|
||||
|
||||
var inputs = Object.keys(candidateInputs);
|
||||
var outputs = Object.keys(candidateOutputs);
|
||||
|
||||
if (inputs.length > 1) {
|
||||
RED.notify("Multiple start points - bailing")
|
||||
return;
|
||||
}
|
||||
|
||||
if (outputs.length === 0) {
|
||||
RED.notify("No outputs - is this a big loop? Bailing");
|
||||
return;
|
||||
}
|
||||
|
||||
function applyDepth(id,d) {
|
||||
if (nodes[id].d < d) {
|
||||
nodes[id].d = d;
|
||||
nodes[id].o.forEach(function(nid) {
|
||||
applyDepth(nid,d+1);
|
||||
})
|
||||
}
|
||||
}
|
||||
applyDepth(inputs[0],0)
|
||||
|
||||
function calculateDownstream(id,downstream) {
|
||||
nodes[id].downstream += downstream;
|
||||
nodes[id].i.forEach(function(nid) {
|
||||
calculateDownstream(nid, nodes[id].downstream+1);
|
||||
})
|
||||
}
|
||||
outputs.forEach(function(id) {
|
||||
calculateDownstream(id, 0)
|
||||
})
|
||||
|
||||
var ranks = {};
|
||||
function rankNodes(node) {
|
||||
if (node.r === -1) {
|
||||
ranks[node.d] = ranks[node.d] || [];
|
||||
node.r = ranks[node.d].length;
|
||||
ranks[node.d].push(node);
|
||||
node.o.sort(function(a,b) {
|
||||
return nodes[b].downstream - nodes[a].downstream
|
||||
})
|
||||
node.o.forEach(function(nid) {
|
||||
rankNodes(nodes[nid])
|
||||
})
|
||||
}
|
||||
}
|
||||
rankNodes(nodes[inputs[0]]);
|
||||
function shuffleRanks(node) {
|
||||
var pushed = false;
|
||||
if (node.o.length > 1) {
|
||||
var outputs = node.o.slice(0);
|
||||
outputs.sort(function(a,b) {
|
||||
if (nodes[a].d === nodes[b].d) {
|
||||
return nodes[a].r - nodes[b].r;
|
||||
} else {
|
||||
return nodes[b].d - nodes[a].d;
|
||||
}
|
||||
})
|
||||
// outputs.forEach(function(o,i) { console.log(" ",i," + "+nodes[o].n.type," d:",nodes[o].d," r:",nodes[o].r)});
|
||||
var rank = nodes[outputs[0]].r;
|
||||
var depth = nodes[outputs[0]].d;
|
||||
for (var i=1;i<outputs.length;i++) {
|
||||
// console.log(outputs[i]);
|
||||
var n = nodes[outputs[i]];
|
||||
if (n.d !== depth && n.r === rank) {
|
||||
// need to move n down one.
|
||||
var r = n.r;
|
||||
ns.forEach(function(_n) {
|
||||
var nn = nodes[_n.id];
|
||||
if (nn.d >= n.d && nn.d < depth && nn.r >= r) {
|
||||
pushed = true;
|
||||
nn.r++;
|
||||
}
|
||||
})
|
||||
}
|
||||
depth = n.d;
|
||||
rank = n.r;
|
||||
}
|
||||
}
|
||||
node.o.forEach(function(n) {
|
||||
pushed = pushed || shuffleRanks(nodes[n])
|
||||
})
|
||||
return pushed;
|
||||
}
|
||||
var shuffle = function() {
|
||||
if (shuffleRanks(nodes[inputs[0]])) {
|
||||
shuffle();
|
||||
}
|
||||
}
|
||||
shuffle();
|
||||
|
||||
|
||||
var x = nodes[inputs[0]].n.x;
|
||||
var y = nodes[inputs[0]].n.y;
|
||||
var changedNodes = [];
|
||||
ns.forEach(function(n) {
|
||||
var d = nodes[n.id].d;
|
||||
var r = nodes[n.id].r;
|
||||
|
||||
changedNodes.push({
|
||||
n:n,
|
||||
ox: n.x,
|
||||
oy: n.y,
|
||||
moved: n.moved
|
||||
});
|
||||
|
||||
n.x = x + d*200;
|
||||
n.y = y + r*50;
|
||||
n.dirty = true;
|
||||
// n.dirtyStatus = true;
|
||||
// n.status = {
|
||||
// text:"d"+d+" : r"+r+" : ds"+nodes[n.id].downstream
|
||||
// }
|
||||
});
|
||||
|
||||
if (changedNodes.length > 0) {
|
||||
RED.history.push({t:"move",nodes:changedNodes,dirty:RED.nodes.dirty()});
|
||||
RED.nodes.dirty(true);
|
||||
RED.view.redraw(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
init: function() {
|
||||
RED.actions.add("core:align-selection-to-grid", alignToGrid);
|
||||
@ -121,6 +283,8 @@ RED.view.tools = (function() {
|
||||
RED.actions.add("core:step-selection-right", function() { moveSelection(RED.view.gridSize(),0);});
|
||||
RED.actions.add("core:step-selection-down", function() { moveSelection(0,RED.view.gridSize());});
|
||||
RED.actions.add("core:step-selection-left", function() { moveSelection(-RED.view.gridSize(),0);});
|
||||
|
||||
RED.actions.add("core:layout-flow", function() { layoutFlow() })
|
||||
},
|
||||
/**
|
||||
* Aligns all selected nodes to the current grid
|
||||
|
Loading…
Reference in New Issue
Block a user