From 7bd51549fa7bcc2e5074301dcf6cb93212cdafda Mon Sep 17 00:00:00 2001 From: rgthree Date: Mon, 8 Jan 2024 00:31:13 -0500 Subject: [PATCH] Adds the ability to change the relay signal, so one could essentially create an inverse relay. Fixes #102 --- ts/comfyui/base_node_collector.ts | 1 - ts/comfyui/node_mode_relay.ts | 105 ++++++++++++++++++++++++++--- web/comfyui/base_node_collector.js | 1 - web/comfyui/node_mode_relay.js | 92 ++++++++++++++++++++++--- 4 files changed, 175 insertions(+), 24 deletions(-) diff --git a/ts/comfyui/base_node_collector.ts b/ts/comfyui/base_node_collector.ts index 341a39c..4b47e28 100644 --- a/ts/comfyui/base_node_collector.ts +++ b/ts/comfyui/base_node_collector.ts @@ -17,7 +17,6 @@ export class BaseCollectorNode extends BaseAnyInputConnectedNode { constructor(title?: string) { super(title); - this.addInput("", "*"); this.addOutput("Output", "*"); } diff --git a/ts/comfyui/node_mode_relay.ts b/ts/comfyui/node_mode_relay.ts index c88fc83..39c7961 100644 --- a/ts/comfyui/node_mode_relay.ts +++ b/ts/comfyui/node_mode_relay.ts @@ -4,9 +4,12 @@ import { app } from "../../scripts/app.js"; import type { INodeInputSlot, INodeOutputSlot, + IWidget, + LGraphCanvas, LGraphNode, LLink, LiteGraph as TLiteGraph, + Vector2, } from "../typings/litegraph.js"; import type { NodeMode } from "../typings/comfy.js"; import { @@ -18,6 +21,7 @@ import { import { wait } from "./shared_utils.js"; import { BaseCollectorNode } from "./base_node_collector.js"; import { NodeTypesString, stripRgthree } from "./constants.js"; +import { fitString } from "./utils_canvas.js"; declare const LiteGraph: typeof TLiteGraph; @@ -25,6 +29,27 @@ const MODE_ALWAYS = 0; const MODE_MUTE = 2; const MODE_BYPASS = 4; const MODE_REPEATS = [MODE_MUTE, MODE_BYPASS]; +const MODE_IGNORE = -99; // MADE THIS UP. + +const MODE_TO_OPTION = new Map([ + [MODE_ALWAYS, 'ACTIVE'], + [MODE_MUTE, 'MUTE'], + [MODE_BYPASS, 'BYPASS'], + [MODE_IGNORE, 'IGNORE'], +]); + +const OPTION_TO_MODE = new Map([ + ['ACTIVE', MODE_ALWAYS], + ['MUTE', MODE_MUTE], + ['BYPASS', MODE_BYPASS], + ['IGNORE',MODE_IGNORE], +]); + +const MODE_TO_PROPERTY = new Map([ + [MODE_MUTE, 'on_muted_inputs'], + [MODE_BYPASS, 'on_bypassed_inputs'], + [MODE_ALWAYS, 'on_any_active_inputs'], +]); /** * Like a BaseCollectorNode, this relay node connects to a Repeater and changes it mode (so it can go @@ -36,6 +61,21 @@ class NodeModeRelay extends BaseCollectorNode { static override type = NodeTypesString.NODE_MODE_RELAY; static override title = NodeTypesString.NODE_MODE_RELAY; + static "@on_muted_inputs" = { + type: "combo", + values: ["MUTE", "ACTIVE", "BYPASS", "NOTHING"], + }; + + static "@on_bypassed_inputs" = { + type: "combo", + values: ["BYPASS", "ACTIVE", "MUTE", "NOTHING"], + }; + + static "@on_any_active_inputs" = { + type: "combo", + values: ["BYPASS", "ACTIVE", "MUTE", "NOTHING"], + }; + constructor(title?: string) { super(title); @@ -49,8 +89,40 @@ class NodeModeRelay extends BaseCollectorNode { color_off: "#a80", shape: LiteGraph.ARROW_SHAPE, }); + + this.properties['on_muted_inputs'] = 'MUTE'; + this.properties['on_bypassed_inputs'] = 'BYPASS'; + this.properties['on_any_active_inputs'] = 'ACTIVE'; } + override onDrawForeground(ctx: CanvasRenderingContext2D, canvas: LGraphCanvas): void { + if ( + this.properties['on_muted_inputs'] !== 'MUTE' || + this.properties['on_bypassed_inputs'] !== 'BYPASS' || + this.properties['on_any_active_inputs'] != 'ACTIVE') { + let margin = 15; + ctx.textAlign = "left"; + let label = `*(MUTE > ${this.properties['on_muted_inputs']}, `; + label += `BYPASS > ${this.properties['on_bypassed_inputs']}, `; + label += `ACTIVE > ${this.properties['on_any_active_inputs']})`; + ctx.fillStyle = LiteGraph.WIDGET_SECONDARY_TEXT_COLOR; + const oldFont = ctx.font; + ctx.font = "italic " + (LiteGraph.NODE_SUBTEXT_SIZE - 2) + "px Arial"; + ctx.fillText(fitString(ctx, label, this.size[0] - 20), 15, this.size[1] - 6); + ctx.font = oldFont; + } + } + + override computeSize(out: Vector2) { + let size = super.computeSize(out); + if ( + this.properties['on_muted_inputs'] !== 'MUTE' || + this.properties['on_bypassed_inputs'] !== 'BYPASS' || + this.properties['on_any_active_inputs'] != 'ACTIVE') { + size[1] += 17; + } + return size; + } override onConnectOutput( outputIndex: number, inputType: string | -1, @@ -94,7 +166,7 @@ class NodeModeRelay extends BaseCollectorNode { -1, this.inputsPassThroughFollowing, ); - let mode: NodeMode | null = undefined; + let mode: NodeMode | -99 | null = undefined; for (const inputNode of inputNodes) { // If we haven't set our mode to be, then let's set it. Otherwise, mode will stick if it // remains constant, otherwise, if we hit an ALWAYS, then we'll unmute all repeaters and @@ -111,13 +183,18 @@ class NodeModeRelay extends BaseCollectorNode { } if (mode != null) { - if (this.outputs?.length) { - const outputNodes = getConnectedOutputNodesAndFilterPassThroughs(this); - for (const outputNode of outputNodes) { - outputNode.mode = mode; - wait(16).then(() => { - outputNode.setDirtyCanvas(true, true); - }); + const propertyVal = this.properties?.[MODE_TO_PROPERTY.get(mode)||'']; + const newMode = OPTION_TO_MODE.get(propertyVal) + mode = (newMode !== null ? newMode : mode) as NodeMode | -99; + if (mode !== null && mode !== MODE_IGNORE) { + if (this.outputs?.length) { + const outputNodes = getConnectedOutputNodesAndFilterPassThroughs(this); + for (const outputNode of outputNodes) { + outputNode.mode = mode; + wait(16).then(() => { + outputNode.setDirtyCanvas(true, true); + }); + } } } } @@ -136,17 +213,23 @@ class NodeModeRelay extends BaseCollectorNode { +

+ Note, you can change which signals get sent on the above in the Properties. + For instance, you could configure an inverse relay which will send a MUTE when any of its + inputs are active (instead of sending an ACTIVE signal), and send an ACTIVE signal when all + of its inputs are muted (instead of sending a MUTE signal), etc. +

`; } } diff --git a/web/comfyui/base_node_collector.js b/web/comfyui/base_node_collector.js index 24c7ecf..d6f835a 100644 --- a/web/comfyui/base_node_collector.js +++ b/web/comfyui/base_node_collector.js @@ -5,7 +5,6 @@ export class BaseCollectorNode extends BaseAnyInputConnectedNode { constructor(title) { super(title); this.inputsPassThroughFollowing = PassThroughFollowing.REROUTE_ONLY; - this.addInput("", "*"); this.addOutput("Output", "*"); } clone() { diff --git a/web/comfyui/node_mode_relay.js b/web/comfyui/node_mode_relay.js index 8df1015..fdb98cd 100644 --- a/web/comfyui/node_mode_relay.js +++ b/web/comfyui/node_mode_relay.js @@ -3,10 +3,29 @@ import { PassThroughFollowing, addConnectionLayoutSupport, getConnectedInputNode import { wait } from "./shared_utils.js"; import { BaseCollectorNode } from "./base_node_collector.js"; import { NodeTypesString, stripRgthree } from "./constants.js"; +import { fitString } from "./utils_canvas.js"; const MODE_ALWAYS = 0; const MODE_MUTE = 2; const MODE_BYPASS = 4; const MODE_REPEATS = [MODE_MUTE, MODE_BYPASS]; +const MODE_IGNORE = -99; +const MODE_TO_OPTION = new Map([ + [MODE_ALWAYS, 'ACTIVE'], + [MODE_MUTE, 'MUTE'], + [MODE_BYPASS, 'BYPASS'], + [MODE_IGNORE, 'IGNORE'], +]); +const OPTION_TO_MODE = new Map([ + ['ACTIVE', MODE_ALWAYS], + ['MUTE', MODE_MUTE], + ['BYPASS', MODE_BYPASS], + ['IGNORE', MODE_IGNORE], +]); +const MODE_TO_PROPERTY = new Map([ + [MODE_MUTE, 'on_muted_inputs'], + [MODE_BYPASS, 'on_bypassed_inputs'], + [MODE_ALWAYS, 'on_any_active_inputs'], +]); class NodeModeRelay extends BaseCollectorNode { constructor(title) { super(title); @@ -20,6 +39,34 @@ class NodeModeRelay extends BaseCollectorNode { color_off: "#a80", shape: LiteGraph.ARROW_SHAPE, }); + this.properties['on_muted_inputs'] = 'MUTE'; + this.properties['on_bypassed_inputs'] = 'BYPASS'; + this.properties['on_any_active_inputs'] = 'ACTIVE'; + } + onDrawForeground(ctx, canvas) { + if (this.properties['on_muted_inputs'] !== 'MUTE' || + this.properties['on_bypassed_inputs'] !== 'BYPASS' || + this.properties['on_any_active_inputs'] != 'ACTIVE') { + let margin = 15; + ctx.textAlign = "left"; + let label = `*(MUTE > ${this.properties['on_muted_inputs']}, `; + label += `BYPASS > ${this.properties['on_bypassed_inputs']}, `; + label += `ACTIVE > ${this.properties['on_any_active_inputs']})`; + ctx.fillStyle = LiteGraph.WIDGET_SECONDARY_TEXT_COLOR; + const oldFont = ctx.font; + ctx.font = "italic " + (LiteGraph.NODE_SUBTEXT_SIZE - 2) + "px Arial"; + ctx.fillText(fitString(ctx, label, this.size[0] - 20), 15, this.size[1] - 6); + ctx.font = oldFont; + } + } + computeSize(out) { + let size = super.computeSize(out); + if (this.properties['on_muted_inputs'] !== 'MUTE' || + this.properties['on_bypassed_inputs'] !== 'BYPASS' || + this.properties['on_any_active_inputs'] != 'ACTIVE') { + size[1] += 17; + } + return size; } onConnectOutput(outputIndex, inputType, inputSlot, inputNode, inputIndex) { var _a, _b; @@ -34,7 +81,7 @@ class NodeModeRelay extends BaseCollectorNode { }, 500); } stabilize() { - var _a; + var _a, _b; if (!this.graph || !this.isAnyOutputConnected() || !this.isInputConnected(0)) { return; } @@ -55,13 +102,18 @@ class NodeModeRelay extends BaseCollectorNode { } } if (mode != null) { - if ((_a = this.outputs) === null || _a === void 0 ? void 0 : _a.length) { - const outputNodes = getConnectedOutputNodesAndFilterPassThroughs(this); - for (const outputNode of outputNodes) { - outputNode.mode = mode; - wait(16).then(() => { - outputNode.setDirtyCanvas(true, true); - }); + const propertyVal = (_a = this.properties) === null || _a === void 0 ? void 0 : _a[MODE_TO_PROPERTY.get(mode) || '']; + const newMode = OPTION_TO_MODE.get(propertyVal); + mode = (newMode !== null ? newMode : mode); + if (mode !== null && mode !== MODE_IGNORE) { + if ((_b = this.outputs) === null || _b === void 0 ? void 0 : _b.length) { + const outputNodes = getConnectedOutputNodesAndFilterPassThroughs(this); + for (const outputNode of outputNodes) { + outputNode.mode = mode; + wait(16).then(() => { + outputNode.setDirtyCanvas(true, true); + }); + } } } } @@ -79,22 +131,40 @@ class NodeModeRelay extends BaseCollectorNode { +

+ Note, you can change which signals get sent on the above in the Properties. + For instance, you could configure an inverse relay which will send a MUTE when any of its + inputs are active (instead of sending an ACTIVE signal), and send an ACTIVE signal when all + of its inputs are muted (instead of sending a MUTE signal), etc. +

`; } } NodeModeRelay.type = NodeTypesString.NODE_MODE_RELAY; NodeModeRelay.title = NodeTypesString.NODE_MODE_RELAY; +NodeModeRelay["@on_muted_inputs"] = { + type: "combo", + values: ["MUTE", "ACTIVE", "BYPASS", "NOTHING"], +}; +NodeModeRelay["@on_bypassed_inputs"] = { + type: "combo", + values: ["BYPASS", "ACTIVE", "MUTE", "NOTHING"], +}; +NodeModeRelay["@on_any_active_inputs"] = { + type: "combo", + values: ["BYPASS", "ACTIVE", "MUTE", "NOTHING"], +}; app.registerExtension({ name: "rgthree.NodeModeRepeaterHelper", registerCustomNodes() {