From 753d2660d3d065f5ce15c7de3913eb7297ad4d92 Mon Sep 17 00:00:00 2001 From: Chris Date: Mon, 27 Oct 2025 14:32:26 +1100 Subject: [PATCH] #408 --- README.md | 17 ++++- docs/output_set.png | Bin 0 -> 11995 bytes docs/outputs.png | Bin 0 -> 3901 bytes js/use_everywhere_classes.js | 27 +++++-- js/use_everywhere_graph_analysis.js | 1 - js/use_everywhere_settings.js | 57 +++++++++++++- js/use_everywhere_ui.js | 112 +++++++++++++++++++--------- 7 files changed, 167 insertions(+), 47 deletions(-) create mode 100644 docs/output_set.png create mode 100644 docs/outputs.png diff --git a/README.md b/README.md index 6d4636d..11fc303 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,10 @@ If upgrading from before version 7, see the end of this document for the major c
Changes since 7.0 +## 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) +
# Where will the data be sent? @@ -180,7 +189,7 @@ and connections, it is muted.
-Inputs have a subtle visual clue +Inputs and outputs have a subtle visual clue 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) +
# Special Case Nodes diff --git a/docs/output_set.png b/docs/output_set.png new file mode 100644 index 0000000000000000000000000000000000000000..b368fca71c8485bb5fd514073edc5203378a25fb GIT binary patch literal 11995 zcmaKS1z41A*X_^<2!nJE(j_HQ5&}brbeD99bax0!3iU^&XN5ZH7`*Flx#jM&sr2>ZnJk8H(wEEiuGdYf4Z7jHJ%7WNKRnz(*!jKw z`#M>>vN-YHXh5=^iJi|>k(IF7ZBo)d>UXur^o|9?bQNzS-|KyQDlu%6CPeHQ3!_!8 zXXlNP&t+)H5|cSjI5jJG%?>5)e%y1WY!E*uwcC6#+b<=3(s=Q+my3rdLk_d6yBnF3 z(%qt=rWPBUla!Qn>FMEdhm4FYH8ZngDM9Av2@5{6V9U+bE;1t{*0%rMT~Sf0y1Kfa z-rm;bWmY0wEC>?^2ghpC-2@$}#9r1CrAV8^Q5J=?%1S}VqmYnhQ=AVIitIM%^YFN` z;)gR0N04*LLOXbkJ|li_c(tu~^k&=&XQHI>Zsq=q z_J!{}6^-G9RDAQB6)o>dEQQh%8Ru3!7Wi*a{QH~cH^wIqWjn0BymV(@78P+VFE3j> zf6l!y5h1L8q#~4NbCj0&SU;yY?lM!6ujmW%77e6lpub-}4_w(rm2JP3^yHkCWxR(J zGZJ6o;ltcwZhpoDCCVjiObp=%57?4>DNlw|L}&f5Txv{aRiXDSmJ}H^qT^+oDydvE z|Gu>m>iv74EvZzRAr0;k^H+!q3nD%l^xU;(O3TC~*19`Bx3G|-fLJT`bA5e!D2$r= zQDET1^o)$5@$sm)JO+y!g*mH7Gcz-SDg!PxOJ{32-2)k-l_6IFX1gABn#RVo)d@kR z&CSx1XUjlfL7=GZ7`0-jeyj6Q!gU?=bEE2^v4S?9TCL;5T|5?laZexYWZB1`l2xue;x zmqacnL8dwf2%eUaQB2F#REvVGEn7|8!-o&|S5nOLYHFxhSXkU7vYtJE-Uhqg!y~7l zSnzGRV4x?)yWVWMj)isG2w9%&%H5|)e-z%ORT$MN*^+^^2Pou`2o;~{_lXJWzP>&S z0cuS{Lwv~k#zyP@QuoJq1vE4?L)$tE3OJ~PgR{!S*7L=KGf3El;XO)1=(gw-B@r$N z0RbVQ&JKqx^gwBhqG*f7`Ps9u@y)lLLMqxS5#DV+nNyEny6$^OdDjw z!NJiae-#R2U{K&BM<9`PUK`qD7muOvbDO8gpLjngn53tzu9fuk1SKWosLX@3X7Ak-#KrzPGe8ixF$?865*cdn!Y^hbP~a8OO~D$;)YCo zwv2EAXE8Jc7t<%Ltu`|6CWg+KmbrxU^MEhkzM(iggrsvwG-R%X=lE${YUpw`@xg0) zf#p)y>sp^}T1Nk6(vTnpt@jSJ2J!dq-4pZMV|vGL&L`S^3mX%ixuyD!Km1RFq)Uye zanlQ0H@v#GSQs6<;~B(!whZNJy0)ikyg@uV+L+gdlwfMLmyb!@HYP*l@1&wyhm+PxZWIUS3;WwR--X05{^rcudZI zSCQFcc2FgBK)KL)o0ggi$*aif_b&Q|yp`75+k2Mw=V8{*RNw8j`@LD9;I&5J`D>EV zr1U>n$~t@X5|qur-6o}lBYZ2y-g;(li7ZZjekDgo9$mc^6$E)})6mFBgjC=$D^Z9X zv|~DP)OXt`K7V%BY)1I*#;UMT#sDW5SL^lF1t~tZ-SW%f{?TQRIsp+8+@#)@%Buq*y2{?KVXGYeBG< zld5RwNz<}0EhD2MC;8%wWI{s1g@d!j)#p~uXIIyO*#sE_c0|O)C?EM;D9IRalFBvb z7)RHyUz*c~rA6<3vr=z-M~n47m6dfy!9sclb3EioJGOd@nQQ-0ST~Hi(-g?O0 zqwlaMvXb>8`un=cZ%Ma;H3{@WF3=P=dRP;m=%zgWe{@6CpBmFFg(|ZOs*o0#NXES` z^9|67G&SG&sI%bLC4F<6Y@5N{_js^zCX}?|kmxA@7Bx9J`MZr!ZeAWHB5!Yro!Kns z`n=+XHrjYRI@`CtPnu@mB2mfGHY}%HbA3|!DQ8{ zb1$1ay==w(ABa}1pYQrKESy{n4G*^!)`E&OJm}fk$Wv{kQB{8qKs7r`|DOWE`T4#qFO>7MXTax_ki)T3dpcwA z3!qk9d!^{wJUb%;cqkZ~3)G%yBRPZ@#`ioKaeCr91XHxaBYldZ8JDQ;rG;Ie>XY## z%bKa7p`lzlFIJk9#H8L8k9|pPz7(ElMbr!V=8gT~E6?>F~ zl{ABcr9hViXv4&WuF;HNm1|YF_|qqVI;96+bucf>Tx?YfUf3rN4-bF2s^6z(WQ-i& zT%o}L?XGtdloKZrI%1ggkHJ<80gI!*qmZzr?u5aNz=+hq4(|!_yD|^{3!X(dDfL9n zvuy9|#B1FlYRXg;Qp`uiOx=sM!G2eehAyh0hF-Um4N^RshH1XM zJhc7a$?5rgci(URD8L$pC(BWYG|T?zCibJSp}06NHy0y??`dMEw&LNC&??J{qOY%S zTnXmT=7(bLns7Zmien~xtGfcOU{Sw3GbYs2TUr%Pgq45hx&e*j#izl;vYJ`UFq#XN zSDC7yw*rKB`Qj~T0O=1L{fIw0PNDjK z;Yfh^>t)F&0MSH|z!d%c#eFe&&70|8JPPBn`_|IGqLQA}yR_f2{HC~~q8mUeYhKGq zrQ&g7)|(2-r&`cKDN>UCOEjQ-O(%*vAbKTNPbmRFO@AJ~u~p5~{^h~o(yv_k$4LvM z9agWx-%SIgNMM}E?9}uOtYEomot#QCQ#4L*-e=`oR$PxLH_+QknUV{~hdmkx6URqA z;W5FMD)c3h(Fb&YxNG#`prQ{wGl+ixU&ub)ZI-_DHP2~GGkp1m8T9N_Ht>EplijU< z?%Ba*N7}dk0Da*oF0a+mO<#t1b4q^q&&-F zG|6yhRn&hTXqF0Nt6fo2TD_F$Fu25Sw-H(}zd16u$DX>o<66ZyAuc|?hlDC>=$Ut2 z589-Cx)bcp64|iZ$9U;1_`^uwg7DQ~=&{i69mnsYYwnuPt$0-sBn3cSA=d{v`2ePF z{}x!3a3a{sOjl*DgL|hX%Z^bkyE=Cv4PbH=V!y!U^^|P7pZ)5eFdrr;qdn!sQP@j- z((ZYR|CjDOk-tt@lInF*P??EB?AlIcBwkpRHruzKg27vkShk!zIAPGAu=D-4zcc$B4Ffnuz!zLBtVa%CoZ(Y9>&S)+#CG%YutocW_g|a>ZZLeLO@3X5 zkTIU~{t<0_slP3rtX4yPb1r6_>)iA@qGGFi?UMFs``6*Bjo=n>SXt~hnSk52A4kfn zJpZ8bS)Lh(rq#5zgdRiFiwkY*E^nmp)F!$r*f8K_Bde=+l83+K*Z=-aN6v)Z3qCJV z%YYcafyr|VePIe6b81b;oKM#<$b!V{OB%Q_;^|TR%{|~Or<}neKqx)TqjbWz1#9D5 zWsG}TP&So-OYT&PP=CWv_AKDJ*mJJXmkWiodD~4d$EZtAZ5#I$@qUKhFVofaHm(f1 zu1?uCo|tK$Sg#FK{Wvcok+*He6Rh6N_8DhjS0-w4j&WK4#IszOedl9X!q=O5MM;h? z=D2fSImB6br{bNhBjE|S1r(o(i$Mr&pOx`-oyiX!NO&>UtOUOk1A9kJP3@V3gRGw3 zXsPyZC}9T9C*}UV%=MEXb6qcATgCu^ms1g_V+S*#l)GMMywZ4KgFhEhq6A|y?Z&qj zP_6}Ky@NNd)P8%ppFZM2#^+;=b;lLuAGOjtS#Dn)=YOI&#R$S0I;OWv4)$b(eYAdc zdsb2TwC^H@WS$O!fFXbSII@k&_}y;OB0N2qGgbWbJF}j|>pRu<$%E-@_HV)0^5jj5 zByabM$w`mToMes*JsI9X*pqJIkubsmEL6{ucwo=9l?(Z!5juq-8|M8dWnR^zjeXizfvP9KD95T z3YKx~@UO}uDXjETY{a=ps?Z)Wps}_?fHk(=tzLdJb9JC9rGAu@J*nTX=BNhJGtMV5huuW?U*ORIZ745nCX0quPEjpxy^t7MrKBd=- zgT4QFLEGmCE4Yv-^D7*z9Nou|KEL;Qf7Br@GJJtLHo1Xl@mQzXPHiH3^Zqo0Nr;!t z`K|K^x}oGn;f#}pdg?V2g)&o({x9A|4!^$ON(u+m)QntDu7QCn{<0@*3TGs=`eFiE z^;R8vLNy7n3@>zfMXw|1QGd>265>a=@wISy^>|=f_3T7kdhAbkPX*H(h&zBgJ{Eo%Q|8qv_U3v*K5!iSP@|L>QO%|a$ST8@kfuf+S*X@>Yrsk$Me}k@a37Aulbq-}mQly7 z{;{$Riv5B<@4PtEjtuqbR|+^DVs_XJS!94kAZC=rPwHhgAU%jHg?H|v{N%hI^-p!~ zzblSwe9Bw&P0$1&-l9H%CAHa~DpglU=iHm3`}Muvd8#gEX0vew`T$tHNlL=fihR%` zy&_TB1KKMfQx>EGoVBnUzD{lX?KYiQd^NWI=I!P6-KAzTJEg$ENGEktu9+C+%lrWT zkxQMW6N8UVBOCC?k8hDHycl}c@%=j_NE%jLQbHO7w?Zw5(G%+$ z7+^tgadCgm&c=gckDizCS4x|%E>7~_zlT&b9n%6D2mr!AfBx8U;x^30ujPa?MiDbC zdN0;NqQxuxJ|EnEC3i_bnDr2^W@;e8{a{tC)Vzfe5MF4M?&*0Sfk0UO9C`i~CCw6$ z?b^7H3*9z&`jjE`qnnKl_QlDr5)(AK$a*_%z)te&aOBZpcNl|wV?1OJ@Ma)X^Z>&` z!ra8-24!yOGen%*a^<5eDh(~FC!qwd><&}1M5hdz?VsSdL;E7U!88uw@-(fLK1UML zT+2pBy{n@GyXN!ae`hFHADo#KGJmrvRP;qqP>|HqxpPDMf*uCTaVOe8AH3kn1GpLB z+m;qsG`E<7*1VU*(Bx!HU!RJ!vo1M~v$OO1)>a3AY8E0HfJrPjZ;5xF`i21k{1cc{ z!J)lV+Ikcb5fNY>f6;3b-fJTVqJN&ZE57?b@bpqAl{tBUnD4sLvSL1|dFa)H8aq8b zzz~HO7mEd-3xh=kQ`Evy06^prJ-su333}pGRdz7*wQe4v)5zENV9BUV|M%F~;^D@K zrrujT2A_tR>2PWA<>KPvy{+m&)Mn4QxR6jdz>Et&vmS!+Q9)iFD>5?j<^A-XP;>LS zxQo1EZfFyj70U3V+66D};Usbm`Arpgm2yv_jEfa^&p+sNT&+0rQJ!6I1P|6TKcFW1 zR8bLC;P83UX&RH9!juX6{S_ev7@t}c^|49xy#CI$q}6d6`7W(S2V`a7gY z#>dCkZiDwA=o5hzN7HujGRZM2AE{K{i%QG!2tas13DmlY-VQ2tSXDBS6Xe#^7?dvp z5`z)H%|c)=w(^~*d?zrRI=Ry&*8O7y9{2+0YKSCfoy>v&MX z(A~7zrj8&^?bRn+VE43IeE&OIV*R(Iqy!L+U^2=fV3*e-2P;u(S|?<80f<}-ND9CC zs!AFcxrl-&?P~xjUKBWhDCJnT6(auO2vZlBm7~JLF**BY)(`XZ^Gz+8pU((>9GJ#F z2_Jo~0Juw;o6E}}(|WI4fE8P zsUr9EDPMq!ZgWapDUUXKua9pk=;`VGKxOXixRl3Dkc;Z2F4_om27pZoc6OVSNV}7W zCl84hPx05U4~j5<>zpH%ZMqIcbO7U^Tl%AW6HvXy+@Ku1l+uYhGY$+4JR{FG`tU;o zU|&GzRIj3Hb4c-Wv)jmP`XeOXzR+OvAJX|`h1`ubT@WCo=G zVmU|+?e6X>ffcf}t#!ce z{@gd`DG`u1mR^UmI>~*~R-`^s6M~b;*}ai+cR~LKEI2t86&3`fGaz$oODE<)9&sWt zGX}b9?w(tJpJuVErU7XN7*4F&po0PGItziGP~l}*G8_;(_gDXvnKhkfmt9|AxIgM6 z@NAE~jXrX$%p}TtbIfx4&sW0+pL=5!<_X=fP2=i_ky9Ow(Fa1TX%S}zHeq+@|=1rupBFN;ZTtQL}wLc59IXdhW_{1vgQ0eynu z4iQq|x=^?|nDKp4(cETzvqwS=gRSwu;l+g&jXv)zsc1%!$XxC<-rSn3DN%0afg*n;d~?q6GXG>6Lat~QVGJK(Dx4H{OjCL zpYRvls9P>q`1jjk*HFM;C_$~Oc-yFAFZHyvkOEcIAEHgIyaWZA|7gc9b_!8OE9_e8 z5EE`?ezG8@S|;nzmenn7>)J@fNUOhR#@26>Cz+phz z(TMyzuSy~KUL9y8p`FG=KeC9^5Yav|cXnrO^^jwuqYE7|WMcj(R{rhn?W&3JkLBgW zj0sVA*KbqY)+Oa=x9fsBDqrKNB&dPP}T*&pttdT+KQJC>rsueCA`sB?4Mv->V~+}fI~#l*MGO2)@WrvE zg#`o_dMSt&M(M@e9*Mq3VUb9N@i(7hf{dwkU%~~%8WH4~lM@^$+{E-^Vc<)huPx8_ z+DTaM#QgZw;`Oaky+{kXi;9C0$5bbG-RN&H=d-9jNH`n}tA+3qb z@n5Bpjt~ntr?GfA1eF9V#WRTuA`6P4$gOrMjM8wxQPa?C;+%ff_Siqayqi@M4pI zQynjVir#)c+kakxURfprIbDq9_uE_K$o9hoy$PK7H0S0F1_CL$-$5qlI5J)DO&F~% zs>-gTN=H;Z8HWbx(igmT4LyKZJSEeFQ_$sMEf&gL6)_bJg8kZ`zdHxM0st>%ERKfc zJ~p3ubLMXxO;c0)q!ZcTlS@!5LJ-xHiWC?iOg2ear{A3d<_-pSs~!?3;2wlq~Frl#F$Bu<&&ZNGI_|=H_74 zE@hRKNSQZjYSMROJHzASi8Cer?*yLgtaM*?@`qsL5A`G$B^Dlu=ht6o^{P5PC0i!i1GBs#sg2lmDBb~{H-5T>5qe!uy(Hf4svgt4|> z59gB(=D&hkExq;7gZ8X*^lAKek4ZUwHtEMkm~4BK}15*pRqVRI{GT>p>JM6 z0WJ_sKyoH4;A4PW0$rIRCkS;5Ob+%o+)O(ft9nRdUdo`|z&UvU(?L^Td2ZV1M+zt; z%2?Y-nFsX5z)+E_v38f!T;xhLW5D$6JV+W8Kc|^`J+U@n)yilTP%66438LDA?t0W$ zG|tw0$L1)3(h>LZ9{MW4xxX*Cxs`mWtRzK4g^JKCN4{^$2@ zKB+qk1V5mKNa$;H6oNr3^yVZKwp6Z?@{nVPcl~55CJo5Z z2a-JdZNi@&oKOi>qCphif#4$sdsk(kQ;xV<6il&+)D$1F(Np)F4j zLrnmLqr`egmi11n?_6*cdMQpp=@Fp0{pRk4a;K(0fZ4!%BFRi9=*vqD@QV!iBK(_j zr+eZ~JA2_sOkqw4t>xVk4#Aoa`O3XYD$rPn+K7OVP+QV0DIp=y=PU6w^(er=4Mu&R zhNtE1t2F30zgJFe8`iim0NP>xx2VS2f#`|Pcb$z0aFk#yg)3#U{J*g^u9c%5^3$@dJ|9&BpEeS z83K>WfT@Hr0nev_!Q0zgfSQP!hUUNY_j!-H_nZX(5H4{=^1|bHo?r~m%t$SXVI%MD zj4G+{5>b{WcmaeM4QHv@rRfS?B35@hUfY4LZ(Y3Fye=?zE>1l(2@XboLSQWgql?>(2$R?xCrc242q<$c*|F+Ug4 zYN1-t4+s2^{D^Svj0>>z^p1kiaUp+7442~i*iqUE@o4F1R#~aR|FI9Pyb1zti)q3< zMib2!SpXYchR6WTE~g+vmA%%qJhku`^<~;gz_8-JG0etChL7#`_cs@C)L^9-$b!(+)O3HzkNVFMi3Lo8 zHiBRs;}S<#G_Wg^->+!IX5chj#xUV8hM*lFypI4!BOxJSWu*ZavVg3B7jl3BrDWP_ zEuD12%|mzt8UFNeM8j5#IrcMG6A_T3QaKiHt3-nCjG*8FcY9LF9U@z+AD# zC|K3Zgw9833Pw7pdSi@8h6#CDd_vTb*WIcn^hXZxC%|u~G>#%2SL8t?3M=9ujZ&q_ zZr&ZSj37PhFN;b`<93yxPj364C5h=zP?O%HYg*CZwfX}((Mr{zkU@pnYolvczTi8d zm+Y$&6<5Gj_x*$=`r}J6X*gS7K{Qes8$7J7txuO=H$Aa&y*6#YLl=*(Q&Lh=9uEYg zOq&8I`rqAa2OZ~aH;>Gw&Cw-qO7I4T=_e|Dm6*Lg_ z_qAfAp{9%8*QYG$QddlP9kwMOrzD*mKz~%_JUaN00+i?jA_6A4<*E<>jmVrAf9?24Y zaZ5yhV$Q>IX|12XWqGVf%wp0)1J3-hRT+497-@iyFoE25M*-O<1FvERFtSv6MI@D3 zZF)qU=}ydhu>XMm3tqtpl}!AywgQP)A*tz^FRkt0V8}t$G+wph?D20G&4X$?H#dl%#;8Ww@!y0k+D}neM*~P($;Vm!D{>AFTu?QeH6S~CNoZ+nV=8D_ z2-6A*2taZMqyGK{78_D9N9@-~13ny3xG&aGufUujEGla4=Jv*<`TtGY-dT-gjt-Rl zrZQP*2=-hZ$|fh;SnV=vCzD=vLv^2no&7U!ylvP2y#0y$^jb18td1-kv@;=THpspl zU@#S=EUJ~syXuKn;D#~jIfnU5TJKnOgcpt7uSYdNdPVf z7e4o-91I`!_8hKH^z;B3l?Mz10E~bK8ynmD<|Y;EE1m7!^}^el@^6tS}+Qaq1m zzv(_{bWDCudXiGnFdIyYY_W$U`+D$51^;hiG8Oqa*_M=nmR3%Teig$dqLsw%xSYBI0I|jqZZ;(tvuf_N}Fw&n1^iLg;W4!&1 z0BPXJ89Mk_E5=F$p<7X@Yau-hC^l*$+)}e9iGpU}{IsVU4LmU<0c$3d_Ss{lFLfI@ zUa=vj%|U5Awg8}Ma&{E%f=S+97<7&Vf2blfO_5%7@zGnnGRgOzUbyw8P#dFBF9k2rrz?<@s5l0RE`O}v${_EExX>BWYQoMXI!_GCr#RFXBP3lRX%4K-4f6RZo zXygA8wu^eAB{XN*H+uq!w|J@C9|p?3BT^fIK#GgIuq$m5U%|Ln#&Hft0(2Nb-|?jY zE6NPT{|?+t1L+UY?MK>ZReGX1_#yPR(eapxYbU2qcq*YyKk(Nn`O>c_z&t?k%nrMl zrew7KW5-Zcs5ib_{Q7n!@oPJQ-Tki`FE2IjB>c>D`v~K%=#^f|z zOA2g4C>QtrfPb(a@Pu0bk<)dUZ4S=3Fea6{UTBZfGBf)*bE`jvzfk!hT;Brm=%t!0 zS?e*`lg4delKO8~QfJANw_eM;{%2$tCcKNuHi^v3WN_oprzutD8m8ABZ}3jxi65nv zW&gL@^^caobPh2|(ES4>uOo7#_xm5GO}wEhUf%;pFR^7=VKPn)vk_!XS1bP^^ytH;F~RHcWW`(F#~cJf=l9|;ktW9mZA7LeyJv{2Av9e zLp80=OO!%&xPhrvA2z$(Z*Fs(&OuMpp5ROfIu2dvbhv|5?#H1()l z?2Zo4t%=?Z$?%nu)q!}CHG&SDPI7N+D65itM07+aFb9O^DbZDZ69g04j)X9?92c{k zFO}yG+X@a2KIbw0J^mCk2x}tJ($eT;nb3n(*j%%wKhtIn3s4K z!r?-_%@_I88Hs!xckdxwX(g}aFp$DdYHz5?l#H7h4k+*@R;N>j@Kpj)B+s6)P6MZI zcy9C@C%uHrxGN-*4RWOyf`sk6^&SYejIA8KAo%_oOo~{oubEDNkCY^q=Wr&DG(<); znaG~GQo^sMre!!qU zxZtTDo8edpryNnT`P}6jH@LGpK0ZWhNqiTd@_QC^(N|HQGlms$IfX&V8LMkaAlNbK zoYH_)P?AxGTcD*Z5zIOo;BfpV9rCULF}r3B;ig0rF`_{_+||ByCLjy*Y+c)~!)`Uc z9K@*twD!dibw)NAC z#}~K;bsn5Ybaw|=N-tHmSp?s`tC>ii459hgI-vWT1-R#^N(^-S?~o0m-q?3E_@%RU z{wD(>BI6f-(~kp0+bp=rOH;C_5&0w!6sNY*eIdMl#ZBOrHc?gyvYh&rFx=bwuWoZZ z)Cd3UBYx8_U#_Q@@v9A?E zb7|5jA<;asr)E>@p&SqH@5mECSh3%&FAcX-@K@C2zQ3`kl+GB+%&gwLVeIRB{v@M&{Tlx4N3*P=VFg%{8CyF?xpbFhRjLHrD!9ma$WtBfS7luNiZhLvTgoZx5 z)t1Uu9{ne(c}h8v>^Ivf^kZGCtE-_w@7%XjL zz96mt+c#iik$I+8|@ITeNO-I$wZ7Id%isTAlqd_vH99Qqkk~W5I0qK|4i3T{k*g!0j2(liPWA~jAwL~ zCCmQnq5=z>hJ;5%czSy?54Rbwii${ebr%7r6M(+HzR9VnSFT<)iipq=joQht&1-E% z4NMFTp=9~VwY8()=W^~ebbL+{=-LWV#~KB6*zCwxE@M5@BlDj z${Ej1clB{0nmI|`&SIj!zHCcOC_9xf?(|db+(Ul@xDQ!$=W^2mdTy5k{u3qJ%)9I(Uxhp&eq)(hUv9mRP zXSCwhN!s1jV%9JFj#u}Mj$*W+#g&!m3U+Ul)zyt7BlXJ4%6vx3V-gZL>S(ltHfC`> z>uyHISsUC;Gl@%GQ?n{!8$UErli{k0(S>+j7=QYe%W+FhkQ z!Y_`)!^5rOz>3m1B3L`{_$>!zs5JOCrPwrO0vz*lAEnvhdZmX?6^S6x%z*cd9`IDS zcL|r5#^Oc1hkSGnZgM)eWETpxSXMYR%hNA;IF z$0g*rAg4~5a^{P2sXC@Vd`L=0{AL=UD#kj8gbcCb=lKp?Dm<5ywzO#p(GAuackys_PInKDq?A|-Qkhj zgf0Bmk@+KYAQdpwbCL%-*&)MfuG&C{`tYY{cyqqd12p{f@6AoO6`4oqhnBCpR?31Y z#n~;%#h$k*ag-8JzET=@PVqtrcEIe?=~Hu!%XEzp6K|$q# zbC!W3PEXS=s~f*lH0z=WlQpcH`;-Usz2vD1C2P3J&CpFg{Yrgn^GaX#9zv#6?YlW* zaj|r6*sM@pT^$RA)0bM zkLx2$2{|+;g6W=yD(X!gTMN|#X$LBl!I=R|=0oWM`L(rK`>UVwY~(-RWkj}ZGotS# z-URiHjeYt3TYY6^V4BuHI@&Rp7mc6gJUn(dYi*Nx-RWG>Q+35m z#W)qYoVHBkpB@1@FY=$5jIfA|SAR#WElt!E)O6KK?{lV8RF)JG@>gD65BD~oYx&2r zNbCasdwaF-I13E?48lfAZL4OwhE$P~H65>sj2|6Lo5BwK**kel!|i?6ogzeR`=ip) zBpk>{_MWS%r+cShprN}~U0-C2ZiyuAcZ&#$<{%gyt$!VpWB?N{?3BaMWJ8icDbG5*Tsg?6U3QJwvK_T?k5Cwk<}%vp@0Z-Q>HA z`}6MD_h*=3QWw6o@(T*srRp#eQSGb-#p}j}rEd6=M@=fYUU_Aa7uh>Ytd@$VP0Do` zd!viJ1fHImo!zZWoT~Lpp<0vTk@2S=LDPO2n^o^S7fQ~um5$GhrA6XZ%u_R8RDxrm zmR~m`8(co)tA>^0Dj_jYo+=%6(XHyORrCB(qOTJjJfj=HFE`V(OB2dAkh5`me+{j}tjinW&2ci?)jhz@k4oy{ixCooj1Y z{P&%vgJH+7Can6+I=&yI+VO#aoe{IkI|O}sg~)@gn$=$|ymo@;&Pl`mWZY^ya_cVq z2z~Mq#1bV7EcOu^{%fMI$_sZ6hdc9JZ{G=e>FCbE6t73Z=butgp7ETs&(j5(CKm^( zAp=krzALWjadR)de(Pl9=2leC;TjN!l=hZ7AIn&sBMCSvujVWAlN-};teMVa67DS( x&l=^#LRQ9b{Vk6ul)@`=fdL3036^>!dL5<%)NnDvu|{41#>CS24f1B}{{i5YaEbr` literal 0 HcmV?d00001 diff --git a/js/use_everywhere_classes.js b/js/use_everywhere_classes.js index 9f9a5a7..f926f83 100644 --- a/js/use_everywhere_classes.js +++ b/js/use_everywhere_classes.js @@ -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 { 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)) { diff --git a/js/use_everywhere_graph_analysis.js b/js/use_everywhere_graph_analysis.js index dc48b8b..14934cc 100644 --- a/js/use_everywhere_graph_analysis.js +++ b/js/use_everywhere_graph_analysis.js @@ -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"; diff --git a/js/use_everywhere_settings.js b/js/use_everywhere_settings.js index df5ff45..e22f417 100644 --- a/js/use_everywhere_settings.js +++ b/js/use_everywhere_settings.js @@ -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) { diff --git a/js/use_everywhere_ui.js b/js/use_everywhere_ui.js index ca6a1c3..4cb0fb6 100644 --- a/js/use_everywhere_ui.js +++ b/js/use_everywhere_ui.js @@ -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"