Increase efficiency of Groups Muter/Bypasser calculations by abandoning LiteGraph's built in mechanism.
This commit is contained in:
@@ -12,6 +12,7 @@ import {
|
||||
SerializedLGraphNode,
|
||||
IWidget,
|
||||
LGraphGroup,
|
||||
Vector4,
|
||||
} from "../typings/litegraph.js";
|
||||
import { fitString } from "./utils_canvas.js";
|
||||
|
||||
@@ -45,6 +46,8 @@ class FastGroupsService {
|
||||
private runScheduleTimeout: number|null = null;
|
||||
private runScheduleAnimation: number|null = null;
|
||||
|
||||
private cachedNodeBoundings: {[key: number]: Vector4}|null = null;
|
||||
|
||||
constructor() {
|
||||
// Don't need to do anything, wait until a signal.
|
||||
}
|
||||
@@ -104,12 +107,52 @@ class FastGroupsService {
|
||||
this.runScheduledForMs = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the boundings for all nodes on the graph, then clears it after a short delay. This is
|
||||
* to increase efficiency by caching the nodes' boundings when multiple groups are on the page.
|
||||
*/
|
||||
getBoundingsForAllNodes() {
|
||||
if (!this.cachedNodeBoundings) {
|
||||
this.cachedNodeBoundings = {};
|
||||
for (const node of app.graph._nodes) {
|
||||
this.cachedNodeBoundings[node.id] = node.getBounding();
|
||||
}
|
||||
setTimeout(() => {
|
||||
this.cachedNodeBoundings = null;
|
||||
}, 50);
|
||||
}
|
||||
return this.cachedNodeBoundings;
|
||||
}
|
||||
|
||||
/**
|
||||
* This overrides `LGraphGroup.prototype.recomputeInsideNodes` to be much more efficient when
|
||||
* calculating for many groups at once (only compute all nodes once in `getBoundingsForAllNodes`).
|
||||
*/
|
||||
recomputeInsideNodesForGroup(group: LGraphGroup) {
|
||||
const cachedBoundings = this.getBoundingsForAllNodes();
|
||||
const nodes = group.graph._nodes;
|
||||
group._nodes.length = 0;
|
||||
|
||||
for (const node of nodes) {
|
||||
const node_bounding = cachedBoundings[node.id];
|
||||
if (!node_bounding || !LiteGraph.overlapBounding(group._bounding, node_bounding)) {
|
||||
continue;
|
||||
}
|
||||
group._nodes.push(node);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Everything goes through getGroupsUnsorted, so we only get groups once. However, LiteGraph's
|
||||
* `recomputeInsideNodes` is inefficient when calling multiple groups (it iterates over all nodes
|
||||
* each time). So, we'll do our own dang thing, once.
|
||||
*/
|
||||
private getGroupsUnsorted(now: number) {
|
||||
const graph = app.graph as TLGraph;
|
||||
if (!this.groupsUnsorted.length || now - this.msLastUnsorted > this.msThreshold) {
|
||||
this.groupsUnsorted = [...graph._groups];
|
||||
for (const group of this.groupsUnsorted) {
|
||||
group.recomputeInsideNodes();
|
||||
this.recomputeInsideNodesForGroup(group);
|
||||
(group as any)._rgthreeHasAnyActiveNode = group._nodes.some(
|
||||
(n) => n.mode === LiteGraph.ALWAYS,
|
||||
);
|
||||
|
||||
Vendored
+7
-2
@@ -297,6 +297,7 @@ export const LiteGraph: {
|
||||
|
||||
//@rgthree
|
||||
isValidConnection(type: string|string[], type: string|string[]):boolean;
|
||||
overlapBounding(a: Vector4, b: Vector4) : boolean;
|
||||
|
||||
createNode<T extends LGraphNode = LGraphNode>(type: string): T;
|
||||
/** Register a node class so it can be listed when the user wants to create a new one */
|
||||
@@ -435,7 +436,8 @@ export declare class LGraph {
|
||||
starttime: number;
|
||||
status: typeof LGraph.STATUS_RUNNING | typeof LGraph.STATUS_STOPPED;
|
||||
|
||||
private _nodes: LGraphNode[];
|
||||
// @rgthree, remove private; it's not really private b/c it's javascript.
|
||||
_nodes: LGraphNode[];
|
||||
// @rgthree, remove private; it's not really private b/c it's javascript.
|
||||
_groups: LGraphGroup[];
|
||||
private _nodes_by_id: Record<number, LGraphNode>;
|
||||
@@ -1166,7 +1168,8 @@ export type SerializedLGraphGroup = {
|
||||
};
|
||||
export declare class LGraphGroup {
|
||||
title: string;
|
||||
private _bounding: Vector4;
|
||||
// @rgthree - mark unprivate
|
||||
_bounding: Vector4;
|
||||
color: string;
|
||||
font: string;
|
||||
// @rgthree
|
||||
@@ -1175,6 +1178,8 @@ export declare class LGraphGroup {
|
||||
_pos: Vector2;
|
||||
// @rgthree
|
||||
_size: Vector2;
|
||||
// @rgthree
|
||||
graph: LGraph;
|
||||
|
||||
|
||||
configure(o: SerializedLGraphGroup): void;
|
||||
|
||||
@@ -21,6 +21,7 @@ class FastGroupsService {
|
||||
this.runScheduledForMs = null;
|
||||
this.runScheduleTimeout = null;
|
||||
this.runScheduleAnimation = null;
|
||||
this.cachedNodeBoundings = null;
|
||||
}
|
||||
addFastGroupNode(node) {
|
||||
this.fastGroupNodes.push(node);
|
||||
@@ -67,12 +68,36 @@ class FastGroupsService {
|
||||
this.runScheduleAnimation = null;
|
||||
this.runScheduledForMs = null;
|
||||
}
|
||||
getBoundingsForAllNodes() {
|
||||
if (!this.cachedNodeBoundings) {
|
||||
this.cachedNodeBoundings = {};
|
||||
for (const node of app.graph._nodes) {
|
||||
this.cachedNodeBoundings[node.id] = node.getBounding();
|
||||
}
|
||||
setTimeout(() => {
|
||||
this.cachedNodeBoundings = null;
|
||||
}, 50);
|
||||
}
|
||||
return this.cachedNodeBoundings;
|
||||
}
|
||||
recomputeInsideNodesForGroup(group) {
|
||||
const cachedBoundings = this.getBoundingsForAllNodes();
|
||||
const nodes = group.graph._nodes;
|
||||
group._nodes.length = 0;
|
||||
for (const node of nodes) {
|
||||
const node_bounding = cachedBoundings[node.id];
|
||||
if (!node_bounding || !LiteGraph.overlapBounding(group._bounding, node_bounding)) {
|
||||
continue;
|
||||
}
|
||||
group._nodes.push(node);
|
||||
}
|
||||
}
|
||||
getGroupsUnsorted(now) {
|
||||
const graph = app.graph;
|
||||
if (!this.groupsUnsorted.length || now - this.msLastUnsorted > this.msThreshold) {
|
||||
this.groupsUnsorted = [...graph._groups];
|
||||
for (const group of this.groupsUnsorted) {
|
||||
group.recomputeInsideNodes();
|
||||
this.recomputeInsideNodesForGroup(group);
|
||||
group._rgthreeHasAnyActiveNode = group._nodes.some((n) => n.mode === LiteGraph.ALWAYS);
|
||||
}
|
||||
this.msLastUnsorted = now;
|
||||
|
||||
Reference in New Issue
Block a user