From bbb5c5ed4da341e0f9d456dd3f094c00e35442aa Mon Sep 17 00:00:00 2001 From: rgthree Date: Sun, 19 Nov 2023 00:11:05 -0500 Subject: [PATCH] A bunch of util changes as building out dynamic context. --- .prettierrc.json | 4 +++- ts/reroute.ts | 50 +++++++++++++++++++++++++++++------------------- ts/rgthree.ts | 6 +++--- ts/utils.ts | 49 +++++++++++++++++++++++++++++------------------ web/reroute.js | 41 ++++++++++++++++++++------------------- web/rgthree.js | 4 ++-- web/utils.js | 43 ++++++++++++++++++++++++----------------- 7 files changed, 114 insertions(+), 83 deletions(-) diff --git a/.prettierrc.json b/.prettierrc.json index de753c5..582f76b 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -1,3 +1,5 @@ { - "printWidth": 100 + "printWidth": 100, + "bracketSpacing": false, + "bracketSameLine": true } diff --git a/ts/reroute.ts b/ts/reroute.ts index 20bce62..beb5278 100644 --- a/ts/reroute.ts +++ b/ts/reroute.ts @@ -21,6 +21,7 @@ import { LAYOUT_LABEL_TO_DATA, addConnectionLayoutSupport, addMenuItem, + getConnectedOutputNodesAndFilterPassThroughs, getSlotLinks, isValidConnection, } from "./utils.js"; @@ -71,7 +72,6 @@ app.registerExtension({ readonly hideSlotLabels: boolean; private configuring = true; - private schedulePromise: Promise | null = null; defaultConnectionsLayout = configLayout; @@ -137,7 +137,26 @@ app.registerExtension({ } } } - this.scheduleStabilize(); + if (this.configuring) { + return; + } + this.stabilize(); + if (type === LiteGraph.INPUT) { + this.updateDownstream(connected ? 'connect' : 'disconnect', {index: 0, name: this.inputs[0]!.name}); + } + } + + /** + * Updates connected nodes of a change (checking and calling updateFromUpstream). Used for Dynamic Context. + */ + private updateDownstream( + update: "connect" | "disconnect" | "move" | "update", + updatedIndexes: {index: number; name: string; from?: number}, + ) { + const nodes = getConnectedOutputNodesAndFilterPassThroughs(this, this, 0); + for (const node of nodes) { + (node as any)?.updateFromUpstream?.(update, this, updatedIndexes); + } } override onDrawForeground(ctx: CanvasRenderingContext2D, canvas: TLGraphCanvas): void { @@ -161,20 +180,6 @@ app.registerExtension({ return super.disconnectOutput(slot, targetNode); } - - scheduleStabilize(ms = 64) { - if (!this.schedulePromise) { - this.schedulePromise = new Promise((resolve) => { - setTimeout(() => { - this.schedulePromise = null - this.stabilize(); - resolve(); - }, ms); - }); - } - return this.schedulePromise; - } - stabilize() { // If we are currently "configuring" then skip this stabilization. The connected nodes may // not yet be configured. @@ -229,7 +234,7 @@ app.registerExtension({ // Find all outputs const nodes: TLGraphNode[] = [this]; - let outputNode = null; + let outputNodes: TLGraphNode[] = []; let outputType = null; while (nodes.length) { currentNode = nodes.pop()!; @@ -266,8 +271,11 @@ app.registerExtension({ console.warn(`[rgthree] Reroute - Disconnecting connected node's input (${node.id}.${link.target_slot}) (${node.type}) because its type (${String(nodeOutType)}) does not match the reroute type (${String(inputType)})`); node.disconnectInput(link.target_slot); } else { + if (outputType != null && outputType !== nodeOutType) { + console.warn(`[rgthree] Reroute - Mismatching output types..`); + } outputType = nodeOutType; - outputNode = node; + outputNodes.push(node); } } } @@ -306,8 +314,10 @@ app.registerExtension({ } } } - (inputNode as any)?.onConnectionsChainChange?.(); - (outputNode as any)?.onConnectionsChainChange?.(); + // (inputNode as any)?.onConnectionsChainChange?.(this); + // for (const outputNode of outputNodes) { + // (outputNode as any)?.onConnectionsChainChange?.(this); + // } app.graph.setDirtyCanvas(true, true); } diff --git a/ts/rgthree.ts b/ts/rgthree.ts index 6f8a957..ce01e0f 100644 --- a/ts/rgthree.ts +++ b/ts/rgthree.ts @@ -77,7 +77,7 @@ class LogSession { export type RgthreeUiMessage = { id: string; message: string; - type?: 'warn'|null; + type?: 'warn'|'info'|null; timeout?: number; // closeable?: boolean; // TODO actions?: Array<{ @@ -305,8 +305,8 @@ class Rgthree { fireEvent(event: string, data: any) { if (this.eventsToFns.has(event)) { for (let fn of this.eventsToFns.get(event)!) { - const event = new Event(data); - fn(event); + const e = new CustomEvent(event, {detail:data}); + fn(e); } } } diff --git a/ts/utils.ts b/ts/utils.ts index 7f25712..92ca48d 100644 --- a/ts/utils.ts +++ b/ts/utils.ts @@ -400,7 +400,7 @@ export function getConnectedInputNodes( slot?: number, passThroughFollowing = PassThroughFollowing.ALL, ) : TLGraphNode[] { - return getConnectedNodes(startNode, IoDirection.INPUT, currentNode, slot, passThroughFollowing).map(n => n.node); + return getConnectedNodesInfo(startNode, IoDirection.INPUT, currentNode, slot, passThroughFollowing).map(n => n.node); } export function getConnectedInputNodesAndFilterPassThroughs( startNode: TLGraphNode, @@ -413,14 +413,16 @@ export function getConnectedInputNodesAndFilterPassThroughs( passThroughFollowing, ); } + export function getConnectedOutputNodes( startNode: TLGraphNode, currentNode?: TLGraphNode, slot?: number, passThroughFollowing = PassThroughFollowing.ALL, ) : TLGraphNode[] { - return getConnectedNodes(startNode, IoDirection.OUTPUT, currentNode, slot, passThroughFollowing).map(n => n.node); + return getConnectedNodesInfo(startNode, IoDirection.OUTPUT, currentNode, slot, passThroughFollowing).map(n => n.node); } + export function getConnectedOutputNodesAndFilterPassThroughs( startNode: TLGraphNode, currentNode?: TLGraphNode, @@ -434,39 +436,48 @@ export function getConnectedOutputNodesAndFilterPassThroughs( } -export function getConnectedNodes( +export type ConnectedNodeInfo = {node: TLGraphNode, travelFromSlot: number, travelToSlot: number, originTravelFromSlot: number}; + +export function getConnectedNodesInfo( startNode: TLGraphNode, dir = IoDirection.INPUT, currentNode?: TLGraphNode, slot?: number, passThroughFollowing = PassThroughFollowing.ALL, -) : {node:TLGraphNode, slot: number}[] { + originTravelFromSlot?: number +) : ConnectedNodeInfo[] { currentNode = currentNode || startNode; - let rootNodes: {node:TLGraphNode, slot: number}[] = []; + let rootNodes: ConnectedNodeInfo[] = []; const slotsToRemove = []; if (startNode === currentNode || shouldPassThrough(currentNode, passThroughFollowing)) { - // const removeDups = startNode === currentNode; let linkIds: Array; + if (dir == IoDirection.OUTPUT) { - linkIds = currentNode.outputs?.flatMap((i) => i.links) || []; - } else { - linkIds = currentNode.inputs?.map((i) => i.link) || []; - } - if (typeof slot == "number" && slot > -1) { - if (linkIds[slot]) { - linkIds = [linkIds[slot]!]; + if (slot != null && slot > -1) { + linkIds = currentNode.outputs[slot]?.links ?? []; } else { - return []; + linkIds = currentNode.outputs?.flatMap((i) => i.links) || []; + } + } else { + if (slot != null && slot > -1) { + linkIds = [currentNode.inputs[slot]?.link ?? -1].filter(i => i > -1); + } else { + linkIds = currentNode.inputs?.map((i) => i.link) || []; } } let graph = app.graph as LGraph; for (const linkId of linkIds) { - const link: LLink = (linkId != null && graph.links[linkId]) as LLink; + let link: LLink | null = null; + if (typeof linkId == "number") { + link = graph.links[linkId] as LLink; + } if (!link) { continue; } + const travelFromSlot = dir == IoDirection.OUTPUT ? link.origin_slot : link.target_slot; const connectedId = dir == IoDirection.OUTPUT ? link.target_id : link.origin_id; - const originSlot = dir == IoDirection.OUTPUT ? link.target_slot : link.origin_slot; + const travelToSlot = dir == IoDirection.OUTPUT ? link.target_slot : link.origin_slot; + originTravelFromSlot = originTravelFromSlot != null ? originTravelFromSlot : travelFromSlot; const originNode: TLGraphNode = graph.getNodeById(connectedId)!; if (!link) { console.error("No connected node found... weird"); @@ -480,10 +491,10 @@ export function getConnectedNodes( ); } else { // Add the node and, if it's a pass through, let's collect all its nodes as well. - rootNodes.push({node: originNode, slot: originSlot}); + rootNodes.push({node: originNode, travelFromSlot, travelToSlot, originTravelFromSlot}); if (shouldPassThrough(originNode, passThroughFollowing)) { - for (const foundNode of getConnectedNodes(startNode, dir, originNode)) { - if (!rootNodes.includes(foundNode)) { + for (const foundNode of getConnectedNodesInfo(startNode, dir, originNode, undefined, undefined, originTravelFromSlot)) { + if (!rootNodes.map(n => n.node).includes(foundNode.node)) { rootNodes.push(foundNode); } } diff --git a/web/reroute.js b/web/reroute.js index 7fba2fc..db91b27 100644 --- a/web/reroute.js +++ b/web/reroute.js @@ -2,7 +2,7 @@ var _a; import { app } from "../../scripts/app.js"; import { rgthreeConfig } from "./rgthree_config.js"; import { rgthree } from "./rgthree.js"; -import { LAYOUT_CLOCKWISE, LAYOUT_LABEL_OPPOSITES, LAYOUT_LABEL_TO_DATA, addConnectionLayoutSupport, addMenuItem, getSlotLinks, isValidConnection, } from "./utils.js"; +import { LAYOUT_CLOCKWISE, LAYOUT_LABEL_OPPOSITES, LAYOUT_LABEL_TO_DATA, addConnectionLayoutSupport, addMenuItem, getConnectedOutputNodesAndFilterPassThroughs, getSlotLinks, isValidConnection, } from "./utils.js"; import { wait } from "./shared_utils.js"; const rerouteConfig = ((_a = rgthreeConfig === null || rgthreeConfig === void 0 ? void 0 : rgthreeConfig['nodes']) === null || _a === void 0 ? void 0 : _a['reroute']) || {}; let configWidth = Math.max(Math.round((Number(rerouteConfig['default_width']) || 40) / 10) * 10, 10); @@ -31,7 +31,6 @@ app.registerExtension({ var _a; super(title); this.configuring = true; - this.schedulePromise = null; this.defaultConnectionsLayout = configLayout; this.isVirtualNode = true; this.hideSlotLabels = true; @@ -75,7 +74,20 @@ app.registerExtension({ } } } - this.scheduleStabilize(); + if (this.configuring) { + return; + } + this.stabilize(); + if (type === LiteGraph.INPUT) { + this.updateDownstream(connected ? 'connect' : 'disconnect', { index: 0, name: this.inputs[0].name }); + } + } + updateDownstream(update, updatedIndexes) { + var _a; + const nodes = getConnectedOutputNodesAndFilterPassThroughs(this, this, 0); + for (const node of nodes) { + (_a = node === null || node === void 0 ? void 0 : node.updateFromUpstream) === null || _a === void 0 ? void 0 : _a.call(node, update, this, updatedIndexes); + } } onDrawForeground(ctx, canvas) { var _a, _b, _c; @@ -97,20 +109,8 @@ app.registerExtension({ disconnectOutput(slot, targetNode) { return super.disconnectOutput(slot, targetNode); } - scheduleStabilize(ms = 64) { - if (!this.schedulePromise) { - this.schedulePromise = new Promise((resolve) => { - setTimeout(() => { - this.schedulePromise = null; - this.stabilize(); - resolve(); - }, ms); - }); - } - return this.schedulePromise; - } stabilize() { - var _a, _b, _c, _d, _e, _f, _g, _h; + var _a, _b, _c, _d, _e, _f; if (this.configuring) { return; } @@ -155,7 +155,7 @@ app.registerExtension({ } } const nodes = [this]; - let outputNode = null; + let outputNodes = []; let outputType = null; while (nodes.length) { currentNode = nodes.pop(); @@ -187,8 +187,11 @@ app.registerExtension({ node.disconnectInput(link.target_slot); } else { + if (outputType != null && outputType !== nodeOutType) { + console.warn(`[rgthree] Reroute - Mismatching output types..`); + } outputType = nodeOutType; - outputNode = node; + outputNodes.push(node); } } } @@ -220,8 +223,6 @@ app.registerExtension({ } } } - (_g = inputNode === null || inputNode === void 0 ? void 0 : inputNode.onConnectionsChainChange) === null || _g === void 0 ? void 0 : _g.call(inputNode); - (_h = outputNode === null || outputNode === void 0 ? void 0 : outputNode.onConnectionsChainChange) === null || _h === void 0 ? void 0 : _h.call(outputNode); app.graph.setDirtyCanvas(true, true); } computeSize(out) { diff --git a/web/rgthree.js b/web/rgthree.js index 178b7eb..21562ae 100644 --- a/web/rgthree.js +++ b/web/rgthree.js @@ -245,8 +245,8 @@ class Rgthree { fireEvent(event, data) { if (this.eventsToFns.has(event)) { for (let fn of this.eventsToFns.get(event)) { - const event = new Event(data); - fn(event); + const e = new CustomEvent(event, { detail: data }); + fn(e); } } } diff --git a/web/utils.js b/web/utils.js index 236e642..cc83289 100644 --- a/web/utils.js +++ b/web/utils.js @@ -275,46 +275,53 @@ export function filterOutPassthroughNodes(nodes, passThroughFollowing = PassThro return nodes.filter((n) => !shouldPassThrough(n, passThroughFollowing)); } export function getConnectedInputNodes(startNode, currentNode, slot, passThroughFollowing = PassThroughFollowing.ALL) { - return getConnectedNodes(startNode, IoDirection.INPUT, currentNode, slot, passThroughFollowing).map(n => n.node); + return getConnectedNodesInfo(startNode, IoDirection.INPUT, currentNode, slot, passThroughFollowing).map(n => n.node); } export function getConnectedInputNodesAndFilterPassThroughs(startNode, currentNode, slot, passThroughFollowing = PassThroughFollowing.ALL) { return filterOutPassthroughNodes(getConnectedInputNodes(startNode, currentNode, slot, passThroughFollowing), passThroughFollowing); } export function getConnectedOutputNodes(startNode, currentNode, slot, passThroughFollowing = PassThroughFollowing.ALL) { - return getConnectedNodes(startNode, IoDirection.OUTPUT, currentNode, slot, passThroughFollowing).map(n => n.node); + return getConnectedNodesInfo(startNode, IoDirection.OUTPUT, currentNode, slot, passThroughFollowing).map(n => n.node); } export function getConnectedOutputNodesAndFilterPassThroughs(startNode, currentNode, slot, passThroughFollowing = PassThroughFollowing.ALL) { return filterOutPassthroughNodes(getConnectedOutputNodes(startNode, currentNode, slot, passThroughFollowing), passThroughFollowing); } -export function getConnectedNodes(startNode, dir = IoDirection.INPUT, currentNode, slot, passThroughFollowing = PassThroughFollowing.ALL) { - var _a, _b; +export function getConnectedNodesInfo(startNode, dir = IoDirection.INPUT, currentNode, slot, passThroughFollowing = PassThroughFollowing.ALL, originTravelFromSlot) { + var _a, _b, _c, _d, _e, _f; currentNode = currentNode || startNode; let rootNodes = []; const slotsToRemove = []; if (startNode === currentNode || shouldPassThrough(currentNode, passThroughFollowing)) { let linkIds; if (dir == IoDirection.OUTPUT) { - linkIds = ((_a = currentNode.outputs) === null || _a === void 0 ? void 0 : _a.flatMap((i) => i.links)) || []; - } - else { - linkIds = ((_b = currentNode.inputs) === null || _b === void 0 ? void 0 : _b.map((i) => i.link)) || []; - } - if (typeof slot == "number" && slot > -1) { - if (linkIds[slot]) { - linkIds = [linkIds[slot]]; + if (slot != null && slot > -1) { + linkIds = (_b = (_a = currentNode.outputs[slot]) === null || _a === void 0 ? void 0 : _a.links) !== null && _b !== void 0 ? _b : []; } else { - return []; + linkIds = ((_c = currentNode.outputs) === null || _c === void 0 ? void 0 : _c.flatMap((i) => i.links)) || []; + } + } + else { + if (slot != null && slot > -1) { + linkIds = [(_e = (_d = currentNode.inputs[slot]) === null || _d === void 0 ? void 0 : _d.link) !== null && _e !== void 0 ? _e : -1].filter(i => i > -1); + } + else { + linkIds = ((_f = currentNode.inputs) === null || _f === void 0 ? void 0 : _f.map((i) => i.link)) || []; } } let graph = app.graph; for (const linkId of linkIds) { - const link = (linkId != null && graph.links[linkId]); + let link = null; + if (typeof linkId == "number") { + link = graph.links[linkId]; + } if (!link) { continue; } + const travelFromSlot = dir == IoDirection.OUTPUT ? link.origin_slot : link.target_slot; const connectedId = dir == IoDirection.OUTPUT ? link.target_id : link.origin_id; - const originSlot = dir == IoDirection.OUTPUT ? link.target_slot : link.origin_slot; + const travelToSlot = dir == IoDirection.OUTPUT ? link.target_slot : link.origin_slot; + originTravelFromSlot = originTravelFromSlot != null ? originTravelFromSlot : travelFromSlot; const originNode = graph.getNodeById(connectedId); if (!link) { console.error("No connected node found... weird"); @@ -324,10 +331,10 @@ export function getConnectedNodes(startNode, dir = IoDirection.INPUT, currentNod console.log(`${startNode.title} (${startNode.id}) seems to have two links to ${originNode.title} (${originNode.id}). One may be stale: ${linkIds.join(", ")}`); } else { - rootNodes.push({ node: originNode, slot: originSlot }); + rootNodes.push({ node: originNode, travelFromSlot, travelToSlot, originTravelFromSlot }); if (shouldPassThrough(originNode, passThroughFollowing)) { - for (const foundNode of getConnectedNodes(startNode, dir, originNode)) { - if (!rootNodes.includes(foundNode)) { + for (const foundNode of getConnectedNodesInfo(startNode, dir, originNode, undefined, undefined, originTravelFromSlot)) { + if (!rootNodes.map(n => n.node).includes(foundNode.node)) { rootNodes.push(foundNode); } }