UI options to change density

This commit is contained in:
Phan Tuấn Anh
2023-09-20 18:47:46 +02:00
committed by GitHub
parent 79727fb52d
commit eeef20394f
2 changed files with 62 additions and 30 deletions
+5 -2
View File
@@ -31,8 +31,11 @@ undo/redo possible with https://github.com/bmad4ever/ComfyUI-Bmad-DirtyUndoRedo
recommend: remove reroute nodes so the algorithms work better as it figures out the ranks/depth, after applied layout u can re-add reroute nodes for any wires partially hidden by nodes
TODO:
- add UI options to change density
**TODO**:
- [x] refresh after apply layout
- [x] add UI options to change density
- [ ] better UI than pop-up for options to change density
- [ ] option to select layout strategy (see docs for each algo)
## example
using [noisy latent composition example](https://comfyanonymous.github.io/ComfyUI_examples/noisy_latent_composition/)
+57 -28
View File
@@ -6,6 +6,7 @@ import "./dagre.min.js";
import "./elk.bundled.min.js";
// idk how to do named import with those
app.registerExtension({
"name": "doof.autoNodesLayout",
setup() { // Add canvas menu options
@@ -15,9 +16,10 @@ app.registerExtension({
options.push(myRightClickMenu); // def below
return options;
}
}
},
});
const myRightClickMenu = {
"content": "📍 auto nodes layout",
"has_submenu": true,
@@ -34,44 +36,53 @@ const myRightClickMenu = {
{
"content": "ELK.js ‘layered’ layout",
"callback": elkLayeredLayout // def below
}
]
}
},
],
},
};
// 2 values to control layout density (string for max compability with window.prompt)
var ranksep = "200"; // spacing (px) between ranks/depths/columns
var nodesep = "150"; // spacing (px) between nodes in same rank/depth/column
/**
* arrange nodes using Dagre layout
* @see https://github.com/dagrejs/dagre
* @todo add options to change density
* @todo better UI than pop-up
* @returns {undefined} Nothing is returned.
*/
function dagreLayout() {
detectRerouteNode(); // def below
popupInput(); // def below
// setup dagre
const daG = new dagre.graphlib
.Graph()
.Graph({ "compound": false })
.setGraph({
"rankdir": "LR", // left to right
"ranker": "network-simplex", // values: "network-simplex", "tight-tree", "longest-path"
"nodesep": 100, // spacing in same column, can be changed
"ranksep": 200, // spacing between columns, can be changed
});
"ranker": "network-simplex",
// values: "network-simplex", "tight-tree", "longest-path"
"ranksep": parseFloat(ranksep),
"nodesep": parseFloat(nodesep),
})
.setDefaultNodeLabel(() => ({}))
.setDefaultEdgeLabel(() => ({}));
// convert litegraph to dagre
app.graph._nodes.forEach((n) => daG.setNode(
n.id.toString(),
{
"label": n.type,
"width": n.size[0],
"height": n.size[1]
"height": n.size[1],
}
));
app.graph.links.forEach((e) => daG.setEdge(
e.origin_id.toString(),
e.target_id.toString(),
{ "label": e.type }
e.target_id.toString()
));
// apply layout algorithm
dagre.layout(daG);
// retrieve nodes position
@@ -85,43 +96,53 @@ function dagreLayout() {
return;
}
/**
* arrange nodes using ELK ‘layered’ layout
* @see https://github.com/kieler/elkjs
* @todo add options to change density
* @todo better UI than pop-up
* @returns {undefined} Nothing is returned.
*/
function elkLayeredLayout() {
detectRerouteNode(); // def below
popupInput(); // def below
// convert litegraph to elk
const myElkNodes = app.graph._nodes.map((n) => ({
"id": n.id,
"width": n.size[0],
"height": n.size[1]
"height": n.size[1],
}));
const myElkEdges = app.graph.links.filter(Boolean).map((e) => ({
"id": e.id,
"sources": [ e.origin_id ],
"targets": [ e.target_id ]
"targets": [ e.target_id ],
}));
// setup ELK
const myElkGraph = {
"id": "root",
"children": myElkNodes,
"edges": myElkEdges,
"layoutOptions": {
"elk.algorithm": "layered"
}
"elk.algorithm": "layered",
"elk.direction": "RIGHT",
"elk.layered.layering.strategy": "NETWORK_SIMPLEX",
// values: "NETWORK_SIMPLEX", "LONGEST_PATH", "COFFMAN_GRAHAM"
"elk.layered.nodePlacement.strategy": "BRANDES_KOEPF",
// values: "NETWORK_SIMPLEX", "BRANDES_KOEPF", "LINEAR_SEGMENTS"
"elk.layered.spacing.nodeNodeBetweenLayers": ranksep,
"elk.spacing.nodeNode": nodesep,
},
}
// setup ELK & retrieve nodes position
// apply layout algorithm
const elk = new ELK()
.layout(myElkGraph)
.then((val) => {
.then((val) => { // retrieve nodes position
for (const nodeLaidOut of val.children) {
const n = app.graph.getNodeById(nodeLaidOut.id);
n.pos[0] = nodeLaidOut.x * 2; // add more spacing
n.pos[1] = nodeLaidOut.y * 2;
n.pos[0] = nodeLaidOut.x;
n.pos[1] = nodeLaidOut.y;
}
})
.catch(console.error);
@@ -130,19 +151,27 @@ function elkLayeredLayout() {
return;
}
/**
* ask for user input to control layout density then
* make a alert in case there’s any reroute node
* @returns {undefined} Nothing is returned.
*/
function detectRerouteNode() {
function popupInput() {
// change global value in a session
ranksep = window.prompt("Enter spacing (px) between ranks/depths/columns", ranksep);
nodesep = window.prompt("Enter spacing (px) between nodes in same rank/depth/column", nodesep);
// make a alert in case there’s any reroute node
for (const n of app.graph._nodes) {
if (n.constructor.name === "RerouteNode") {
if (n.type === "Reroute") {
window.alert(
"Layout algorithms work best without Reroute nodes!\n"
"Layout algorithms work better without Reroute nodes!\n"
+ "better remove reroute before auto-layout then re-add after"
);
break;
}
}
return;
}