|
|
|
|
|
|
|
|
import { app } from "../../scripts/app.js";
|
|
|
import type {BaseFastGroupsModeChanger} from './fast_groups_muter.js';
|
|
|
import {
|
|
|
type LiteGraph as TLiteGraph,
|
|
|
type LGraph as TLGraph,
|
|
|
type LGraphCanvas as TLGraphCanvas,
|
|
|
LGraphGroup,
|
|
|
Vector4,
|
|
|
} from "typings/litegraph.js";
|
|
|
|
|
|
declare const LiteGraph: typeof TLiteGraph;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class FastGroupsService {
|
|
|
private msThreshold = 400;
|
|
|
private msLastUnsorted = 0;
|
|
|
private msLastAlpha = 0;
|
|
|
private msLastPosition = 0;
|
|
|
|
|
|
private groupsUnsorted: LGraphGroup[] = [];
|
|
|
private groupsSortedAlpha: LGraphGroup[] = [];
|
|
|
private groupsSortedPosition: LGraphGroup[] = [];
|
|
|
|
|
|
private readonly fastGroupNodes: BaseFastGroupsModeChanger[] = [];
|
|
|
|
|
|
private runScheduledForMs: number | null = null;
|
|
|
private runScheduleTimeout: number | null = null;
|
|
|
private runScheduleAnimation: number | null = null;
|
|
|
|
|
|
private cachedNodeBoundings: { [key: number]: Vector4 } | null = null;
|
|
|
|
|
|
constructor() {
|
|
|
|
|
|
}
|
|
|
|
|
|
addFastGroupNode(node: BaseFastGroupsModeChanger) {
|
|
|
this.fastGroupNodes.push(node);
|
|
|
|
|
|
|
|
|
this.scheduleRun(8);
|
|
|
}
|
|
|
|
|
|
removeFastGroupNode(node: BaseFastGroupsModeChanger) {
|
|
|
const index = this.fastGroupNodes.indexOf(node);
|
|
|
if (index > -1) {
|
|
|
this.fastGroupNodes.splice(index, 1);
|
|
|
}
|
|
|
|
|
|
if (!this.fastGroupNodes?.length) {
|
|
|
this.clearScheduledRun();
|
|
|
this.groupsUnsorted = [];
|
|
|
this.groupsSortedAlpha = [];
|
|
|
this.groupsSortedPosition = [];
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private run() {
|
|
|
|
|
|
if (!this.runScheduledForMs) {
|
|
|
return;
|
|
|
}
|
|
|
for (const node of this.fastGroupNodes) {
|
|
|
node.refreshWidgets();
|
|
|
}
|
|
|
this.clearScheduledRun();
|
|
|
this.scheduleRun();
|
|
|
}
|
|
|
|
|
|
private 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);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private 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 = {};
|
|
|
for (const node of app.graph._nodes) {
|
|
|
this.cachedNodeBoundings[node.id] = node.getBounding();
|
|
|
}
|
|
|
setTimeout(() => {
|
|
|
this.cachedNodeBoundings = null;
|
|
|
}, 50);
|
|
|
}
|
|
|
return this.cachedNodeBoundings;
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private getGroupsUnsorted(now: number) {
|
|
|
const canvas = app.canvas as TLGraphCanvas;
|
|
|
const graph = app.graph as TLGraph;
|
|
|
|
|
|
if (
|
|
|
|
|
|
!canvas.selected_group_moving &&
|
|
|
(!this.groupsUnsorted.length || now - this.msLastUnsorted > this.msThreshold)
|
|
|
) {
|
|
|
this.groupsUnsorted = [...graph._groups];
|
|
|
for (const group of this.groupsUnsorted) {
|
|
|
this.recomputeInsideNodesForGroup(group);
|
|
|
(group as any)._rgthreeHasAnyActiveNode = group._nodes.some(
|
|
|
(n) => n.mode === LiteGraph.ALWAYS,
|
|
|
);
|
|
|
}
|
|
|
this.msLastUnsorted = now;
|
|
|
}
|
|
|
return this.groupsUnsorted;
|
|
|
}
|
|
|
|
|
|
private getGroupsAlpha(now: number) {
|
|
|
const graph = app.graph as TLGraph;
|
|
|
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;
|
|
|
}
|
|
|
|
|
|
private getGroupsPosition(now: number) {
|
|
|
const graph = app.graph as TLGraph;
|
|
|
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?: string) {
|
|
|
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();
|
|
|
|