Some checks failed
Python Linting / Run Ruff (push) Has been cancelled
Python Linting / Run Pylint (push) Has been cancelled
Full Comfy CI Workflow Runs / test-stable (12.1, , linux, 3.10, [self-hosted Linux], stable) (push) Has been cancelled
Full Comfy CI Workflow Runs / test-stable (12.1, , linux, 3.11, [self-hosted Linux], stable) (push) Has been cancelled
Full Comfy CI Workflow Runs / test-stable (12.1, , linux, 3.12, [self-hosted Linux], stable) (push) Has been cancelled
Full Comfy CI Workflow Runs / test-unix-nightly (12.1, , linux, 3.11, [self-hosted Linux], nightly) (push) Has been cancelled
Execution Tests / test (macos-latest) (push) Has been cancelled
Execution Tests / test (ubuntu-latest) (push) Has been cancelled
Execution Tests / test (windows-latest) (push) Has been cancelled
Test server launches without errors / test (push) Has been cancelled
Unit Tests / test (macos-latest) (push) Has been cancelled
Unit Tests / test (ubuntu-latest) (push) Has been cancelled
Unit Tests / test (windows-2022) (push) Has been cancelled
Includes 30 custom nodes committed directly, 7 Civitai-exclusive loras stored via Git LFS, and a setup script that installs all dependencies and downloads HuggingFace-hosted models on vast.ai. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
164 lines
6.4 KiB
JavaScript
164 lines
6.4 KiB
JavaScript
import { app } from "../../../scripts/app.js";
|
|
import { getGraphDependantNodeKey, getGroupNodes, reduceNodesDepthFirst } from "../utils.js";
|
|
class FastGroupsService {
|
|
constructor() {
|
|
this.msThreshold = 400;
|
|
this.msLastUnsorted = 0;
|
|
this.msLastAlpha = 0;
|
|
this.msLastPosition = 0;
|
|
this.groupsUnsorted = [];
|
|
this.groupsSortedAlpha = [];
|
|
this.groupsSortedPosition = [];
|
|
this.fastGroupNodes = [];
|
|
this.runScheduledForMs = null;
|
|
this.runScheduleTimeout = null;
|
|
this.runScheduleAnimation = null;
|
|
this.cachedNodeBoundings = null;
|
|
}
|
|
addFastGroupNode(node) {
|
|
this.fastGroupNodes.push(node);
|
|
this.scheduleRun(8);
|
|
}
|
|
removeFastGroupNode(node) {
|
|
var _a;
|
|
const index = this.fastGroupNodes.indexOf(node);
|
|
if (index > -1) {
|
|
this.fastGroupNodes.splice(index, 1);
|
|
}
|
|
if (!((_a = this.fastGroupNodes) === null || _a === void 0 ? void 0 : _a.length)) {
|
|
this.clearScheduledRun();
|
|
this.groupsUnsorted = [];
|
|
this.groupsSortedAlpha = [];
|
|
this.groupsSortedPosition = [];
|
|
}
|
|
}
|
|
run() {
|
|
if (!this.runScheduledForMs) {
|
|
return;
|
|
}
|
|
for (const node of this.fastGroupNodes) {
|
|
node.refreshWidgets();
|
|
}
|
|
this.clearScheduledRun();
|
|
this.scheduleRun();
|
|
}
|
|
scheduleRun(ms = 500) {
|
|
if (this.runScheduledForMs && ms < this.runScheduledForMs) {
|
|
this.clearScheduledRun();
|
|
}
|
|
if (!this.runScheduledForMs && this.fastGroupNodes.length) {
|
|
this.runScheduledForMs = ms;
|
|
this.runScheduleTimeout = setTimeout(() => {
|
|
this.runScheduleAnimation = requestAnimationFrame(() => this.run());
|
|
}, ms);
|
|
}
|
|
}
|
|
clearScheduledRun() {
|
|
this.runScheduleTimeout && clearTimeout(this.runScheduleTimeout);
|
|
this.runScheduleAnimation && cancelAnimationFrame(this.runScheduleAnimation);
|
|
this.runScheduleTimeout = null;
|
|
this.runScheduleAnimation = null;
|
|
this.runScheduledForMs = null;
|
|
}
|
|
getBoundingsForAllNodes() {
|
|
if (!this.cachedNodeBoundings) {
|
|
this.cachedNodeBoundings = reduceNodesDepthFirst(app.graph._nodes, (node, acc) => {
|
|
var _a, _b;
|
|
let bounds = node.getBounding();
|
|
if (bounds[0] === 0 && bounds[1] === 0 && bounds[2] === 0 && bounds[3] === 0) {
|
|
const ctx = (_b = (_a = node.graph) === null || _a === void 0 ? void 0 : _a.primaryCanvas) === null || _b === void 0 ? void 0 : _b.canvas.getContext("2d");
|
|
if (ctx) {
|
|
node.updateArea(ctx);
|
|
bounds = node.getBounding();
|
|
}
|
|
}
|
|
acc[getGraphDependantNodeKey(node)] = bounds;
|
|
}, {});
|
|
setTimeout(() => {
|
|
this.cachedNodeBoundings = null;
|
|
}, 50);
|
|
}
|
|
return this.cachedNodeBoundings;
|
|
}
|
|
recomputeInsideNodesForGroup(group) {
|
|
if (app.canvas.isDragging)
|
|
return;
|
|
const cachedBoundings = this.getBoundingsForAllNodes();
|
|
const nodes = group.graph.nodes;
|
|
group._children.clear();
|
|
group.nodes.length = 0;
|
|
for (const node of nodes) {
|
|
const nodeBounding = cachedBoundings[getGraphDependantNodeKey(node)];
|
|
const nodeCenter = nodeBounding &&
|
|
[nodeBounding[0] + nodeBounding[2] * 0.5, nodeBounding[1] + nodeBounding[3] * 0.5];
|
|
if (nodeCenter) {
|
|
const grouBounds = group._bounding;
|
|
if (nodeCenter[0] >= grouBounds[0] &&
|
|
nodeCenter[0] < grouBounds[0] + grouBounds[2] &&
|
|
nodeCenter[1] >= grouBounds[1] &&
|
|
nodeCenter[1] < grouBounds[1] + grouBounds[3]) {
|
|
group._children.add(node);
|
|
group.nodes.push(node);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
getGroupsUnsorted(now) {
|
|
var _a, _b, _c;
|
|
const canvas = app.canvas;
|
|
const graph = (_a = canvas.getCurrentGraph()) !== null && _a !== void 0 ? _a : app.graph;
|
|
if (!canvas.selected_group_moving &&
|
|
(!this.groupsUnsorted.length || now - this.msLastUnsorted > this.msThreshold)) {
|
|
this.groupsUnsorted = [...graph._groups];
|
|
const subgraphs = (_b = graph.subgraphs) === null || _b === void 0 ? void 0 : _b.values();
|
|
if (subgraphs) {
|
|
let s;
|
|
while ((s = subgraphs.next().value))
|
|
this.groupsUnsorted.push(...((_c = s.groups) !== null && _c !== void 0 ? _c : []));
|
|
}
|
|
for (const group of this.groupsUnsorted) {
|
|
this.recomputeInsideNodesForGroup(group);
|
|
group.rgthree_hasAnyActiveNode = getGroupNodes(group).some((n) => n.mode === LiteGraph.ALWAYS);
|
|
}
|
|
this.msLastUnsorted = now;
|
|
}
|
|
return this.groupsUnsorted;
|
|
}
|
|
getGroupsAlpha(now) {
|
|
if (!this.groupsSortedAlpha.length || now - this.msLastAlpha > this.msThreshold) {
|
|
this.groupsSortedAlpha = [...this.getGroupsUnsorted(now)].sort((a, b) => {
|
|
return a.title.localeCompare(b.title);
|
|
});
|
|
this.msLastAlpha = now;
|
|
}
|
|
return this.groupsSortedAlpha;
|
|
}
|
|
getGroupsPosition(now) {
|
|
if (!this.groupsSortedPosition.length || now - this.msLastPosition > this.msThreshold) {
|
|
this.groupsSortedPosition = [...this.getGroupsUnsorted(now)].sort((a, b) => {
|
|
const aY = Math.floor(a._pos[1] / 30);
|
|
const bY = Math.floor(b._pos[1] / 30);
|
|
if (aY == bY) {
|
|
const aX = Math.floor(a._pos[0] / 30);
|
|
const bX = Math.floor(b._pos[0] / 30);
|
|
return aX - bX;
|
|
}
|
|
return aY - bY;
|
|
});
|
|
this.msLastPosition = now;
|
|
}
|
|
return this.groupsSortedPosition;
|
|
}
|
|
getGroups(sort) {
|
|
const now = +new Date();
|
|
if (sort === "alphanumeric") {
|
|
return this.getGroupsAlpha(now);
|
|
}
|
|
if (sort === "position") {
|
|
return this.getGroupsPosition(now);
|
|
}
|
|
return this.getGroupsUnsorted(now);
|
|
}
|
|
}
|
|
export const SERVICE = new FastGroupsService();
|