This commit is contained in:
Chris
2025-10-27 14:32:26 +11:00
parent 425390000e
commit 753d2660d3
7 changed files with 167 additions and 47 deletions
+16 -1
View File
@@ -15,6 +15,10 @@ If upgrading from before version 7, see the end of this document for the major c
<details>
<summary>Changes since 7.0</summary>
## 7.5
- Added per-output granularity to any-node broadcasting
## 7.4
- Added broadcasting from [any node](#any-node-broadcasting)
@@ -84,6 +88,11 @@ When broadcasting, the node acts like all its outputs were connected to a single
|This...|...does the same as this|
|-|-|
|![with](docs/broadcaston.png)|![without](docs/broadcastoff.png)|
From `7.5`, the outputs that will broadcast can also be set (like inputs):
![output_set](docs/output_set.png)
</details>
# Where will the data be sent?
@@ -180,7 +189,7 @@ and connections, it is muted.
</details>
<details>
<summary>Inputs have a subtle visual clue</summary>
<summary>Inputs and outputs have a subtle visual clue</summary>
The state of inputs is also represented visually: a black ring and a glow on the input dot indicates it is connectable.
In the image below, `positive` has been set to not accept UE inputs, `steps` has been set to accept them, and `model` has a UE connection.
@@ -190,6 +199,12 @@ In the image below, `positive` has been set to not accept UE inputs, `steps` has
If a widget is getting data from a UE connection, it is grayed out, like `steps` below:
![uec](docs/connectable3.png)
Similarly, if a node has had broadcasting added to it, the outputs that can broadcast are shown:
here `merged` can broadcast, but isn't, `x` can broadcast and is, and `y` is set to not broadcast.
![outputs](docs/outputs.png)
</details>
# Special Case Nodes
Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

+21 -6
View File
@@ -1,5 +1,6 @@
import { i18n_functional, i18n_functional_regex } from "./i18n.js";
import { default_priority } from "./ue_properties.js";
import { is_able_to_broadcast } from "./use_everywhere_settings.js";
import { connection_from_output_as_input, visible_graph } from "./use_everywhere_subgraph_utils.js";
import { nodes_in_my_group, nodes_not_in_my_group, nodes_my_color, nodes_not_my_color, nodes_in_groups_matching } from "./use_everywhere_ui.js";
import { Logger, node_is_live, get_real_node, get_connection } from "./use_everywhere_utilities.js";
@@ -160,10 +161,6 @@ function validity_errors(params) {
export class UseEverywhereList {
constructor() { this.ues = []; this.unmatched_inputs = []; }
node_sending_anywhere(node) {
return this.ues.find((ue)=>(ue.controller?.id==node.id && ue.sending_to?.length>0)) ? true : false;
}
differs_from(another_uel) {
if (!another_uel || !another_uel.ues || !this.ues) return true;
if (this.ues.length != another_uel.ues.length) return true;
@@ -258,6 +255,18 @@ export class UseEverywhereList {
return ue_connections;
}
node_sending_anywhere(node) {
return this.ues.find((ue)=>(ue.controller?.id==node.id && ue.sending_to?.length>0)) ? true : false;
}
all_sending_slots(for_node) {
const sending_slots = new Set()
this.ues.filter((ue)=>(ue.controller.id == for_node.id) && (ue.sending_to.length>0)).forEach((ue) => {
sending_slots.add(ue.control_node_input_index)
});
return sending_slots;
}
all_ue_connections() {
const ue_connections = [];
this.ues.forEach((ue) => {
@@ -283,19 +292,25 @@ export class UseEverywhereList {
var the_possibles
var connection_finder
var check_if_able_to_broadcast
if (node.properties.ue_convert) {
the_possibles = node.outputs
connection_finder = connection_from_output_as_input
check_if_able_to_broadcast = (node, i) => {
const name = node.outputs[i].name
return is_able_to_broadcast(node, name)
}
} else {
the_possibles = node.inputs
connection_finder = get_connection
check_if_able_to_broadcast = ()=>{true}
}
const broadcasted_types = new Set()
const duplicated_broadcasted_types = new Set()
for (var i=0; i<the_possibles.length; i++) {
const connection = connection_finder(node, i);
if (connection.link) {
if (connection.link && check_if_able_to_broadcast(node,i)) {
if (broadcasted_types.has(connection.type)) duplicated_broadcasted_types.add(connection.type)
broadcasted_types.add(connection.type)
}
@@ -303,7 +318,7 @@ export class UseEverywhereList {
the_possibles.forEach((possible, i) => {
const connection = connection_finder(node, i);
if (connection.link) {
if (connection.link && check_if_able_to_broadcast(node,i)) {
const input_regex = undefined
var additional_requirement = null
if (duplicated_broadcasted_types.has(connection.type)) {
-1
View File
@@ -3,7 +3,6 @@ import { node_is_live, is_connected, is_UEnode, Logger, Pausable } from "./use_e
import { convert_to_links } from "./use_everywhere_apply.js";
import { app } from "../../scripts/app.js";
import { settingsCache } from "./use_everywhere_cache.js";
import { visible_graph } from "./use_everywhere_subgraph_utils.js";
import { is_connectable } from "./use_everywhere_settings.js";
import { for_all_graphs } from "./recursive_callbacks.js";
+55 -2
View File
@@ -127,7 +127,7 @@ function show_connectable(submenu_root, node) {
export function is_connectable(node, input_name){
const input = node.inputs.find(i => i.name==input_name);
if (!input) {
Logger.log_error(`Can't find input ${label} on node ${node.title}`);
Logger.log_error(`Can't find input ${input_name} on node ${node.title}`);
return false;
}
if (input.widget) {
@@ -172,6 +172,50 @@ function widget_ue_submenu(value, options, e, menu, node) {
show_connectable(submenu.root, node)
}
function show_broadcasting(submenu_root, node) {
node.outputs.forEach((input, i) => {
const current_element = submenu_root?.querySelector(`:nth-child(${i+1})`);
if (current_element) current_element.style.borderLeft = (is_able_to_broadcast(node,input.name)) ? "2px solid #484" : "";
})
}
export function is_able_to_broadcast(node, output_name) {
if (!node.properties.ue_convert) return false
const output = node.outputs.find(i => i.name==output_name);
if (!output) {
Logger.log_error(`Can't find output ${output_name} on node ${node.title}`);
return false;
}
return ! (node.properties?.ue_properties?.output_not_broadcasting?.[output_name])
}
function toggle_broadcasting(node, output_name){
const p = node.properties.ue_properties
p.output_not_broadcasting[output_name] = !!!p.output_not_broadcasting[output_name]
}
function output_ue_submenu(value, options, e, menu, node) {
if (!(node.properties.ue_properties)) node.properties.ue_properties = {}
if (!(node.properties.ue_properties.output_not_broadcasting)) node.properties.ue_properties.output_not_broadcasting = {};
const label_to_name = {}
node.outputs.forEach((output) => {label_to_name[output.label || output.name] = output.name})
const submenu = new LiteGraph.ContextMenu(
Object.keys(label_to_name),
{ event: e, callback: function (label) {
const name = label_to_name[label];
toggle_broadcasting(node, name);
shared.linkRenderController.mark_link_list_outdated();
show_broadcasting(this.parentElement, node)
return true; // keep open
},
parentMenu: menu, node:node}
)
show_broadcasting(submenu.root, node)
}
export function add_extra_menu_items(node_or_node_type, ioio) {
if (node_or_node_type.ue_extra_menu_items_added) return
const getExtraMenuOptions = node_or_node_type.getExtraMenuOptions;
@@ -244,9 +288,18 @@ function non_ue_menu_settings(options, node) {
{
content: node.properties.ue_convert ? "Remove UE broadcasting" : "Add UE broadcasting",
has_submenu: false,
callback: () => { node.properties.ue_convert = !!!node.properties.ue_convert },
callback: () => { node.properties.ue_convert = !!!node.properties.ue_convert },
}
)
if (node.properties.ue_convert) {
options.push(
{
content: "Broadcasting Outputs",
has_submenu: true,
callback: output_ue_submenu,
}
)
}
}
function remove_ue_nodes(graph) {
+75 -37
View File
@@ -3,7 +3,7 @@ import { app } from "../../scripts/app.js";
import { settingsCache } from "./use_everywhere_cache.js";
import { in_visible_graph, visible_graph } from "./use_everywhere_subgraph_utils.js";
import { maybe_show_tooltip } from "./tooltip_window.js";
import { is_connectable } from "./use_everywhere_settings.js";
import { is_able_to_broadcast, is_connectable } from "./use_everywhere_settings.js";
import { shared } from "./shared.js";
import { any_restrictions } from "./ue_properties.js";
@@ -204,6 +204,8 @@ export class LinkRenderController extends Pausable {
this.pause('highlight_ue_connections')
if (!this._list_ready()) return;
// get all the inputs that can be connected and aren't
// connected with a 'real' link
const unconnected_connectable_names = new Set(node.inputs
.filter((input)=>is_connectable(node,input.name))
.filter((input)=>(!input.link))
@@ -216,49 +218,35 @@ export class LinkRenderController extends Pausable {
const name_sent_to = node.inputs[ue_connection.input_index].name;
unconnected_connectable_names.delete(name_sent_to); // remove the name from the list of connectables
var pos2 = node.getConnectionPos(true, ue_connection.input_index, this.slot_pos1);
pos2[0] -= node.pos[0];
pos2[1] -= node.pos[1];
ctx.save();
ctx.lineWidth = 1;
var radius=5
ctx.strokeStyle = LGraphCanvas.link_type_colors[ue_connection.type];
ctx.shadowColor = "white";
ctx.shadowBlur = 10;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
ctx.beginPath();
ctx.roundRect(pos2[0]-radius,pos2[1]-radius,2*radius,2*radius,radius);
ctx.stroke();
ctx.shadowBlur = 0;
ctx.beginPath();
ctx.strokeStyle = "black";
ctx.shadowBlur = 0;
radius = radius - 1;
ctx.roundRect(pos2[0]-radius,pos2[1]-radius,2*radius,2*radius,radius);
ctx.stroke();
const pos2 = this._relative_connection_pos(node, true, ue_connection.input_index);
ctx.restore();
circle(ctx, pos2, {...CONNECTED_1, strokeStyle:LGraphCanvas.link_type_colors[ue_connection.type]})
circle(ctx, pos2, {...CONNECTED_2})
});
}
unconnected_connectable_names.forEach((name) => {
const index = node.inputs.findIndex((i) => i.name == name);
var pos2 = node.getConnectionPos(true, index, this.slot_pos1);
pos2[0] -= node.pos[0];
pos2[1] -= node.pos[1];
ctx.save();
ctx.strokeStyle = "black";
ctx.shadowColor = "green";
ctx.shadowBlur = 10;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
var radius = 3;
ctx.beginPath();
ctx.roundRect(pos2[0]-radius,pos2[1]-radius,2*radius,2*radius,radius);
ctx.stroke();
ctx.restore();
const pos2 = this._relative_connection_pos(node, true, index);
circle(ctx, pos2, {...UNCONNECTED_CONNECTABLE})
})
if (node.properties.ue_convert) {
const sending_slots = this.ue_list.all_sending_slots(node)
node.outputs.forEach((output,i) => {
if (is_able_to_broadcast(node, output.name)) {
const pos2 = this._relative_connection_pos(node, false, i);
if (sending_slots.has(i)) {
circle(ctx, pos2, {...CONNECTED_1, strokeStyle:LGraphCanvas.link_type_colors[node.outputs[i].type]})
circle(ctx, pos2, {...CONNECTED_2})
} else {
circle(ctx, pos2, {...UNCONNECTED_CONNECTABLE})
}
}
})
}
} catch (e) {
Logger.log_error(e);
} finally {
@@ -266,6 +254,13 @@ export class LinkRenderController extends Pausable {
}
}
_relative_connection_pos(node, input, slot) {
var pos2 = node.getConnectionPos(input, slot, this.slot_pos1);
pos2[0] -= node.pos[0];
pos2[1] -= node.pos[1];
return pos2
}
_list_ready() {
if (!shared.graphAnalyser) return false; // we don't have the analyser yet (still loading)
if (!this.ue_list) {
@@ -422,6 +417,49 @@ export class LinkRenderController extends Pausable {
}
}
const UNCONNECTED_CONNECTABLE = {
radius : 3,
strokeStyle : "black",
shadowColor : "green",
shadowBlur : 10,
shadowOffsetX : 0,
shadowOffsetY : 0,
}
const CONNECTED_1 = {
radius:5,
first_last:"first",
lineWidth:1,
shadowColor : "white",
shadowBlur : 10,
shadowOffsetX : 0,
shadowOffsetY : 0,
}
const CONNECTED_2 = {
radius:4,
first_last:"last",
shadowBlur : 0,
strokeStyle : "black",
}
function circle(ctx, position, properties) {
const p = {...properties}
const radius = p.radius
const first_last = p.first_last
delete p.radius
delete p.first_last
if (!first_last || first_last=='first') ctx.save()
Object.assign(ctx, p)
ctx.lineWidth = 1;
ctx.beginPath();
ctx.roundRect(position[0]-radius,position[1]-radius,2*radius,2*radius,radius);
ctx.stroke();
if (!first_last || first_last=='last') ctx.restore()
}
function modify(c) {
if (c.length==4) return c + "6"
if (c.length==7) return c + "66"